diff --git a/renovate.json b/.github/renovate.json similarity index 93% rename from renovate.json rename to .github/renovate.json index 295ed09df..2c23e9441 100644 --- a/renovate.json +++ b/.github/renovate.json @@ -25,5 +25,5 @@ ], "labels": ["Renovate"], "rebaseWhen": "conflicted", - "schedule": ["on the first day of the week"] + "schedule": ["on the first day of the month"] } diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml index 30871f791..60c938e66 100644 --- a/.github/workflows/build-pr.yml +++ b/.github/workflows/build-pr.yml @@ -11,7 +11,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@55e685c48d84285a5b0418cd094606e199cca3b6 # v1 + uses: gradle/wrapper-validation-action@v1 - name: Setup Java uses: actions/setup-java@v3 with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 62fb7e90a..832a1ba6b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@55e685c48d84285a5b0418cd094606e199cca3b6 # v1 + uses: gradle/wrapper-validation-action@v1 - name: Setup Java uses: actions/setup-java@v3 with: @@ -44,7 +44,7 @@ jobs: ORG_GRADLE_PROJECT_sonatypePassword: ${{ secrets.SONATYPE_PASSWORD }} - name: Publish core javadoc if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}} - uses: cpina/github-action-push-to-another-repository@0a14457bb28b04dfa1652e0ffdfda866d2845c73 # main + uses: cpina/github-action-push-to-another-repository@main env: SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }} with: @@ -56,7 +56,7 @@ jobs: target-directory: worldedit-core - name: Publish bukkit javadoc if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}} - uses: cpina/github-action-push-to-another-repository@0a14457bb28b04dfa1652e0ffdfda866d2845c73 # main + uses: cpina/github-action-push-to-another-repository@main env: SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }} with: @@ -78,7 +78,7 @@ jobs: MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} - name: Publish to CurseForge if: ${{ runner.os == 'Linux' && env.STATUS == 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main'}} - uses: itsmeow/curseforge-upload@13f278adc4cc7b881555f87e6ea528387dd6492b # v3 + uses: itsmeow/curseforge-upload@v3 with: file_path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-${{ github.event.release.tag_name }}.jar # https://minecraft.curseforge.com/api/game/versions?token=redacted diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1f5f844a8..3c5eb079f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,10 +19,10 @@ jobs: - name: Checkout repository uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2 + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@32dc499307d133bb5085bae78498c0ac2cf762d5 # v2 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/upload-release-assets.yml b/.github/workflows/upload-release-assets.yml index 485919fc1..e93fc35c5 100644 --- a/.github/workflows/upload-release-assets.yml +++ b/.github/workflows/upload-release-assets.yml @@ -9,7 +9,7 @@ jobs: - name: Checkout Repository uses: actions/checkout@v3 - name: Validate Gradle Wrapper - uses: gradle/wrapper-validation-action@55e685c48d84285a5b0418cd094606e199cca3b6 # v1 + uses: gradle/wrapper-validation-action@v1 - name: Setup Java uses: actions/setup-java@v3 with: @@ -19,7 +19,7 @@ jobs: - name: Clean Build run: ./gradlew clean build --no-daemon - name: Upload Release Assets - uses: AButler/upload-release-assets@ec6d3263266dc57eb6645b5f75e827987f7c217d # ratchet:AButler/upload-release-assets@v2.0 + uses: AButler/upload-release-assets@v2.0 with: files: 'worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar' repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.idea/icon.svg b/.idea/icon.svg index 8edca2aab..59d1990d1 100644 --- a/.idea/icon.svg +++ b/.idea/icon.svg @@ -1,44 +1,112 @@ - - + + - + image/svg+xml - + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/build.gradle.kts b/build.gradle.kts index 3c6dfb1a2..5bbde35da 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,9 @@ import org.ajoberstar.grgit.Grgit -import java.time.format.DateTimeFormatter import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import java.net.URI +import java.time.format.DateTimeFormatter +import xyz.jpenilla.runpaper.task.RunServer plugins { id("io.github.gradle-nexus.publish-plugin") version "1.3.0" @@ -82,8 +83,19 @@ allprojects { } applyCommonConfiguration() +val supportedVersions = listOf("1.16.5", "1.17", "1.17.1", "1.18.2", "1.19", "1.19.1", "1.19.2", "1.19.3", "1.19.4") tasks { + supportedVersions.forEach { + register("runServer-$it") { + minecraftVersion(it) + pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } + .toTypedArray()) + jvmArgs("-DPaper.IgnoreJavaVersion=true", "-Dcom.mojang.eula.agree=true") + group = "run paper" + runDirectory.set(file("run-$it")) + } + } runServer { minecraftVersion("1.19.3") pluginJars(*project(":worldedit-bukkit").getTasksByName("shadowJar", false).map { (it as Jar).archiveFile } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 661dbdc0c..d450d7ad9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ residence = "4.5._13.1" towny = "0.98.4.18" # Third party -bstats = "3.0.1" +bstats = "3.0.2" sparsebitset = "1.2" parallelgzip = "1.0.5" adventure = "4.9.3" diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts index 735141c7c..4d1c1ada5 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts +++ b/worldedit-bukkit/adapters/adapter-1_19_4/build.gradle.kts @@ -9,6 +9,6 @@ repositories { } dependencies { - paperDevBundle("1.19.4-R0.1-20230317.182750-6") + paperDevBundle("1.19.4-R0.1-20230412.010331-64") compileOnly("io.papermc:paperlib") } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java index 5edcb8618..e23e18a2e 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweAdapter.java @@ -306,54 +306,6 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements return SideEffectSet.defaults().getSideEffectsToApply(); } - public boolean setBlock(org.bukkit.Chunk chunk, int x, int y, int z, BlockStateHolder state, boolean update) { - CraftChunk craftChunk = (CraftChunk) chunk; - LevelChunk levelChunk = craftChunk.getHandle(); - Level level = levelChunk.getLevel(); - - BlockPos blockPos = new BlockPos(x, y, z); - net.minecraft.world.level.block.state.BlockState blockState = ((PaperweightBlockMaterial) state.getMaterial()).getState(); - LevelChunkSection[] levelChunkSections = levelChunk.getSections(); - int y4 = y >> 4; - LevelChunkSection section = levelChunkSections[y4]; - - net.minecraft.world.level.block.state.BlockState existing; - if (section == null) { - existing = ((PaperweightBlockMaterial) BlockTypes.AIR.getDefaultState().getMaterial()).getState(); - } else { - existing = section.getBlockState(x & 15, y & 15, z & 15); - } - - levelChunk.removeBlockEntity(blockPos); // Force delete the old tile entity - - CompoundBinaryTag compoundTag = state instanceof BaseBlock ? state.getNbt() : null; - if (compoundTag != null || existing instanceof TileEntityBlock) { - level.setBlock(blockPos, blockState, 0); - // remove tile - if (compoundTag != null) { - // We will assume that the tile entity was created for us, - // though we do not do this on the Forge version - BlockEntity blockEntity = level.getBlockEntity(blockPos); - if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNativeBinary(compoundTag); - tag.put("x", IntTag.valueOf(x)); - tag.put("y", IntTag.valueOf(y)); - tag.put("z", IntTag.valueOf(z)); - blockEntity.load(tag); // readTagIntoTileEntity - load data - } - } - } else { - if (existing == blockState) { - return true; - } - levelChunk.setBlockState(blockPos, blockState, false); - } - if (update) { - level.getMinecraftWorld().sendBlockUpdated(blockPos, existing, blockState, 0); - } - return true; - } - @Override public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { return new PaperweightFaweWorldNativeAccess( diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java index 4dead58fa..dbe0150a9 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightFaweWorldNativeAccess.java @@ -102,7 +102,7 @@ public class PaperweightFaweWorldNativeAccess implements WorldNativeAccess currentTick; if (nextTick || cachedChanges.size() >= 1024) { if (nextTick) { diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java index 115246ce3..33be73c69 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/PaperweightPlatformAdapter.java @@ -41,6 +41,8 @@ import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.GlobalPalette; import net.minecraft.world.level.chunk.HashMapPalette; import net.minecraft.world.level.chunk.LevelChunk; @@ -57,6 +59,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -74,6 +77,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Semaphore; import java.util.function.Function; +import static java.lang.invoke.MethodType.methodType; import static net.minecraft.core.registries.Registries.BIOME; public final class PaperweightPlatformAdapter extends NMSAdapter { @@ -103,6 +107,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static final MethodHandle methodRemoveGameEventListener; private static final MethodHandle methodremoveTickingBlockEntity; + /* + * This is a workaround for the changes from https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/commits/1fddefce1cdce44010927b888432bf70c0e88cde#src/main/java/org/bukkit/craftbukkit/CraftChunk.java + * and is only needed to support 1.19.4 versions before *and* after this change. + */ + private static final MethodHandle CRAFT_CHUNK_GET_HANDLE; + private static final Field fieldRemove; static final boolean POST_CHUNK_REWRITE; @@ -111,6 +121,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { private static Field SERVER_LEVEL_ENTITY_MANAGER; static { + final MethodHandles.Lookup lookup = MethodHandles.lookup(); try { fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d")); fieldData.setAccessible(true); @@ -136,7 +147,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { "b" ), long.class); getVisibleChunkIfPresent.setAccessible(true); - methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent); + methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); Unsafe unsafe = ReflectionUtils.getUnsafe(); if (!PaperLib.isPaper()) { @@ -160,7 +171,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { ServerLevel.class ); removeGameEventListener.setAccessible(true); - methodRemoveGameEventListener = MethodHandles.lookup().unreflect(removeGameEventListener); + methodRemoveGameEventListener = lookup.unreflect(removeGameEventListener); Method removeBlockEntityTicker = LevelChunk.class.getDeclaredMethod( Refraction.pickName( @@ -169,7 +180,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { ), BlockPos.class ); removeBlockEntityTicker.setAccessible(true); - methodremoveTickingBlockEntity = MethodHandles.lookup().unreflect(removeBlockEntityTicker); + methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p")); fieldRemove.setAccessible(true); @@ -208,6 +219,20 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { rethrow.printStackTrace(); throw new RuntimeException(rethrow); } + MethodHandle craftChunkGetHandle; + final MethodType type = methodType(ChunkAccess.class); + try { + craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", type); + } catch (NoSuchMethodException | IllegalAccessException e) { + try { + final MethodType newType = methodType(ChunkAccess.class, ChunkStatus.class); + craftChunkGetHandle = lookup.findVirtual(CraftChunk.class, "getHandle", newType); + craftChunkGetHandle = MethodHandles.insertArguments(craftChunkGetHandle, 1, ChunkStatus.FULL); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + CRAFT_CHUNK_GET_HANDLE = craftChunkGetHandle; } static boolean setSectionAtomic( @@ -280,7 +305,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter { CompletableFuture future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true); try { CraftChunk chunk = (CraftChunk) future.get(); - return chunk.getHandle(); + return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk); } catch (Throwable e) { e.printStackTrace(); } diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java index 282baeb21..4f22e8734 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_19_R3/regen/PaperweightRegen.java @@ -60,6 +60,7 @@ import net.minecraft.world.level.storage.LevelStorageSource; import net.minecraft.world.level.storage.PrimaryLevelData; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.craftbukkit.v1_19_R3.CraftServer; import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; import org.bukkit.craftbukkit.v1_19_R3.generator.CustomChunkGenerator; @@ -440,7 +441,11 @@ public class PaperweightRegen extends Regenerator blockPopulator.populate(freshWorld.getWorld(), random, levelChunk.getBukkitChunk())); + TaskManager.taskManager().task(() -> { + final CraftWorld world = freshWorld.getWorld(); + final Chunk chunk = world.getChunkAt(levelChunk.locX, levelChunk.locZ); + blockPopulator.populate(world, random, chunk); + }); } @Override diff --git a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java index 34e5aeaaa..cd6f5ae22 100644 --- a/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java +++ b/worldedit-bukkit/src/main/java/com/fastasyncworldedit/bukkit/regions/plotsquared/PlotSquaredFeature.java @@ -23,6 +23,7 @@ import com.sk89q.worldedit.world.World; import org.apache.logging.log4j.Logger; import org.bukkit.Bukkit; +import java.lang.ref.WeakReference; import java.util.List; import java.util.Locale; import java.util.Set; @@ -176,15 +177,28 @@ public class PlotSquaredFeature extends FaweMaskManager { maskedRegion = new RegionIntersection(world, weRegions); } - return new FaweMask(maskedRegion) { - @Override - public boolean isValid(Player player, MaskType type) { - if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(finalPlot)) { - return false; - } - return isAllowed(player, finalPlot, type); + return new PlotSquaredMask(maskedRegion, finalPlot); + } + + private final class PlotSquaredMask extends FaweMask { + + private final Plot plot; + private final WeakReference> connectedPlots; + + private PlotSquaredMask(Region region, Plot plot) { + super(region); + this.plot = plot; + connectedPlots = new WeakReference<>(plot.getConnectedPlots()); + } + + @Override + public boolean isValid(Player player, MaskType type) { + if (!connectedPlots.refersTo(plot.getConnectedPlots()) || (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot))) { + return false; } - }; + return isAllowed(player, plot, type); + } + } } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java index acdf9fad7..100e3265f 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/DiskOptimizedClipboard.java @@ -64,9 +64,9 @@ public class DiskOptimizedClipboard extends LinearClipboard { private final int headerSize; private RandomAccessFile braf; - private MappedByteBuffer byteBuffer; + private MappedByteBuffer byteBuffer = null; - private FileChannel fileChannel; + private FileChannel fileChannel = null; private boolean hasBiomes = false; private boolean canHaveBiomes = true; private int nbtBytesRemaining; @@ -133,7 +133,7 @@ public class DiskOptimizedClipboard extends LinearClipboard { e.printStackTrace(); } this.braf = new RandomAccessFile(file, "rw"); - long fileLength = (long) getVolume() * 2L + (long) headerSize; + long fileLength = (long) (getVolume() << 1) + (long) headerSize; braf.setLength(0); braf.setLength(fileLength); this.nbtBytesRemaining = Integer.MAX_VALUE - (int) fileLength; @@ -144,7 +144,11 @@ public class DiskOptimizedClipboard extends LinearClipboard { byteBuffer.putChar(6, (char) getHeight()); byteBuffer.putChar(8, (char) getLength()); } catch (IOException e) { + close(); throw new RuntimeException(e); + } catch (Throwable t) { + close(); + throw t; } } @@ -169,11 +173,15 @@ public class DiskOptimizedClipboard extends LinearClipboard { nbtMap = new HashMap<>(); try { this.file = file; + checkFileLength(file); this.braf = new RandomAccessFile(file, "rw"); braf.setLength(file.length()); this.nbtBytesRemaining = Integer.MAX_VALUE - (int) file.length(); init(); - long biomeLength = (long) ((getHeight() >> 2) + 1) * ((getLength() >> 2) + 1) * ((getWidth() >> 2) + 1); + + int biomeLength = ((getHeight() >> 2) + 1) * ((getLength() >> 2) + 1) * ((getWidth() >> 2) + 1); + canHaveBiomes = (long) headerSize + biomeLength < Integer.MAX_VALUE; + if (headerSize >= VERSION_2_HEADER_SIZE) { readBiomeStatusFromHeader(); int nbtCount = readNBTSavedCountFromHeader(); @@ -181,12 +189,42 @@ public class DiskOptimizedClipboard extends LinearClipboard { if (Settings.settings().CLIPBOARD.SAVE_CLIPBOARD_NBT_TO_DISK && (nbtCount + entitiesCount > 0)) { loadNBTFromFileFooter(nbtCount, entitiesCount, biomeLength); } - } else if (braf.length() - headerSize == ((long) getVolume() << 1) + biomeLength) { + } else if (canHaveBiomes && braf.length() - headerSize == ((long) getVolume() << 1) + biomeLength) { hasBiomes = true; } getAndSetOffsetAndOrigin(); } catch (IOException e) { + close(); throw new RuntimeException(e); + } catch (Throwable t) { + close(); + throw t; + } + } + + 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() + )); } } @@ -486,13 +524,26 @@ public class DiskOptimizedClipboard extends LinearClipboard { fileChannel.close(); braf.close(); file.setWritable(true); - closeDirectBuffer(byteBuffer); + MappedByteBuffer tmpBuffer = byteBuffer; byteBuffer = null; + closeDirectBuffer(tmpBuffer); fileChannel = null; braf = null; + } else if (fileChannel != null) { + fileChannel.close(); + fileChannel = null; } } catch (IOException e) { e.printStackTrace(); + if (fileChannel != null) { + try { + fileChannel.close(); + fileChannel = null; + } catch (IOException ex) { + LOGGER.error("Could not close file channel on clipboard {}. If this belongs to a player, the server may " + + "need to be restarted for clipboard use to work.", getFile().getName(), ex); + } + } } } @@ -648,8 +699,6 @@ public class DiskOptimizedClipboard extends LinearClipboard { char ordinal = byteBuffer.getChar(diskIndex); return BlockState.getFromOrdinal(ordinal); } catch (IndexOutOfBoundsException ignored) { - } catch (Exception e) { - e.printStackTrace(); } return BlockTypes.AIR.getDefaultState(); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java index 181711550..6f47132f7 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolUtilCommands.java @@ -169,9 +169,14 @@ public class ToolUtilCommands { public void traceMask( Player player, LocalSession session, @Arg(desc = "The trace mask to set", def = "") - Mask maskOpt + Mask maskOpt ) throws WorldEditException { - session.getBrushTool(player, false).setTraceMask(maskOpt); + BrushTool brushTool = session.getBrushTool(player, false); + if (brushTool == null) { + player.print(Caption.of("worldedit.brush.none.equipped")); + return; + } + brushTool.setTraceMask(maskOpt); if (maskOpt == null) { player.print(Caption.of("worldedit.tool.tracemask.disabled")); } else { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java index b11926db8..0e70a18a6 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/factory/parser/DefaultBlockParser.java @@ -207,7 +207,7 @@ public class DefaultBlockParser extends InputParser { }).collect(Collectors.toSet()); if (!blocked.isEmpty()) { throw new DisallowedUsageException(Caption.of( - "fawe.error.limit.disallowed-block", + "fawe.error.limit.disallowed-property", TextComponent.of(input) )); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java index 89cbf3860..5b14e2e06 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extension/platform/PlatformCommandManager.java @@ -671,31 +671,26 @@ public final class PlatformCommandManager { Actor actor = event.getActor(); String args = event.getArguments(); - TaskManager.taskManager().taskNow(() -> { - if (!Fawe.isMainThread()) { - Thread.currentThread().setName("FAWE Thread for player: " + actor.getName()); + int space0 = args.indexOf(' '); + String arg0 = space0 == -1 ? args : args.substring(0, space0); + Optional optional = commandManager.getCommand(arg0); + if (optional.isEmpty()) { + return; + } + Command cmd = optional.get(); + PermissionCondition queued = cmd.getCondition().as(PermissionCondition.class).orElse(null); + if (queued != null && !queued.isQueued()) { + TaskManager.taskManager().taskNow(() -> handleCommandOnCurrentThread(event), Fawe.isMainThread()); + return; + } else { + actor.decline(); + } + actor.runAction(() -> { + SessionKey key = actor.getSessionKey(); + if (key.isActive()) { + PlatformCommandManager.this.handleCommandOnCurrentThread(event); } - int space0 = args.indexOf(' '); - String arg0 = space0 == -1 ? args : args.substring(0, space0); - Optional optional = commandManager.getCommand(arg0); - if (!optional.isPresent()) { - return; - } - Command cmd = optional.get(); - PermissionCondition queued = cmd.getCondition().as(PermissionCondition.class).orElse(null); - if (queued != null && !queued.isQueued()) { - handleCommandOnCurrentThread(event); - return; - } else { - actor.decline(); - } - actor.runAction(() -> { - SessionKey key = actor.getSessionKey(); - if (key.isActive()) { - PlatformCommandManager.this.handleCommandOnCurrentThread(event); - } - }, false, true); - }, Fawe.isMainThread()); + }, false, true); } public void handleCommandOnCurrentThread(CommandEvent event) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java index 8382319c9..f22d7036d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/transform/BlockTransformExtent.java @@ -555,10 +555,11 @@ public class BlockTransformExtent extends ResettableExtent { int transformedId = transformState(state, transform); BlockState transformed = BlockState.getFromInternalId(transformedId); - if (block.hasNbtData()) { + boolean baseBlock = block instanceof BaseBlock; + if (baseBlock && block.hasNbtData()) { return (B) transformBaseBlockNBT(transformed, block.getNbtData(), transform); } - return (B) (block instanceof BaseBlock ? transformed.toBaseBlock() : transformed); + return (B) (baseBlock? transformed.toBaseBlock() : transformed); //FAWE end } diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 5c6a411d2..ac3c2d3ac 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -283,6 +283,7 @@ "worldedit.brush.butcher.equip": "Butcher brush equipped ({0}).", "worldedit.brush.operation.equip": "Set brush to {0}.", "worldedit.brush.none.equip": "Brush unbound from your current item.", + "worldedit.brush.none.equipped": "You have no brush bound to your current item. Try /brush sphere for a basic brush.", "worldedit.setbiome.changed": "Biomes were changed in {0} columns. You may have to rejoin your game (or close and reopen your world) to see a change.", "worldedit.setbiome.not-locatable": "Command sender must be present in the world to use the -p flag.", "worldedit.drawsel.disabled": "Server CUI disabled.",