diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index da9cf78a98..030aa0813e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -46,7 +46,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 # ℹī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -60,4 +60,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index a407c41379..8f282381a4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -20,7 +20,7 @@ jobs: distribution: 'temurin' - name: Build with Gradle run: ./gradlew build -s - - uses: actions/upload-artifact@v2.2.4 + - uses: actions/upload-artifact@v2 name: Archive Reports if: always() with: diff --git a/CHANGELOG.txt b/CHANGELOG.txt index c8189c65e5..7e2b1dc0b0 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,25 @@ +7.2.10 +- [Forge] Fixed a bug where the max world height would not always be read correctly +- [Bukkit] Fixed detection of other plugins bundling WorldEdit for newer Spigot versions +- [Forge] [Fabric] Improve fluid masks to properly detect mod fluids +- [Forge] Fixed the UPDATE side effect +- Update to 1.18.2 +- Moved external links away from tinyurl to EngineHub URL shortener +- Allowed biome commands to be used by non-player actors (console, command block, etc) +- Fixed the UPDATE side effect not applying when called later on by API or FAST reorder mode +- Fixed entity passenger and riding status not being copied correctly + +7.2.9 +- [Forge] Add support for the UPDATE side effect +- [Forge] Fixed regen on recent Forge builds +- [Fabric] Fixed regen outside of Overworld +- [Sponge] Update to Sponge API8 (MC 1.16.5) +- [Bukkit] Fixed regen when biomes are used +- Fixed snapshots in 1.18 +- Fixed smooth functions below y=0 +- Tweak command usage label correctness for Bukkit API usage +- Fixed legacy error message output for plugins utilising our legacy APIs (Eg, WorldGuard and CraftBook) + 7.2.8 - Update to 1.18.1 - Fixed issues with certain imports in craftscripts diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 972511eb8b..29d8be4d54 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -47,13 +47,15 @@ val mixinVersion: String = properties.getProperty("mixin.version") dependencies { implementation(gradleApi()) implementation("gradle.plugin.org.cadixdev.gradle:licenser:0.6.1") - implementation("org.ajoberstar.grgit:grgit-gradle:4.1.0") - implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.0") - implementation("org.jfrog.buildinfo:build-info-extractor-gradle:4.24.23") - implementation("org.spongepowered:SpongeGradle:0.11.5") - implementation("net.minecraftforge.gradle:ForgeGradle:5.1.26") + implementation("org.ajoberstar.grgit:grgit-gradle:4.1.1") + implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") + implementation("org.jfrog.buildinfo:build-info-extractor-gradle:4.27.1") + implementation("org.spongepowered:spongegradle-plugin-development:2.0.1") + implementation("org.spongepowered:vanillagradle:0.2") + implementation("net.minecraftforge.gradle:ForgeGradle:5.1.31") implementation("net.fabricmc:fabric-loom:$loomVersion") implementation("net.fabricmc:sponge-mixin:$mixinVersion") implementation("org.enginehub.gradle:gradle-codecov-plugin:0.1.0") implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.3.5") + implementation("org.spongepowered:mixingradle:0.7.32") } diff --git a/buildSrc/src/main/kotlin/LibsConfig.kt b/buildSrc/src/main/kotlin/LibsConfig.kt index 43b11bc17c..ffd4f981a5 100644 --- a/buildSrc/src/main/kotlin/LibsConfig.kt +++ b/buildSrc/src/main/kotlin/LibsConfig.kt @@ -52,6 +52,9 @@ fun Project.applyLibrariesConfiguration() { exclude(dependency("org.checkerframework:checker-qual")) exclude(dependency("org.apache.logging.log4j:log4j-api")) exclude(dependency("com.google.code.findbugs:jsr305")) + exclude { + it.moduleGroup == "org.jetbrains.kotlin" + } } relocations.forEach { (from, to) -> diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index b03007fcc4..ddf36aaea5 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -4,13 +4,13 @@ object Versions { const val TEXT = "3.0.4" const val TEXT_EXTRAS = "3.0.6" const val PISTON = "0.5.7" - const val AUTO_VALUE = "1.8.2" + const val AUTO_VALUE = "1.9" const val JUNIT = "5.8.1" - const val MOCKITO = "4.0.0" + const val MOCKITO = "4.3.1" const val FAST_UTIL = "8.5.6" const val GUAVA = "31.0.1-jre" - const val GSON = "2.8.8" - const val LOG4J = "2.15.0" + const val GSON = "2.8.9" + const val LOG4J = "2.17.0" } // Properties that need a project reference to resolve: diff --git a/gradle.properties b/gradle.properties index d123944110..feb00f8fe1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,5 +4,5 @@ version=7.3.0-SNAPSHOT org.gradle.jvmargs=-Xmx1512M org.gradle.parallel=true -loom.version=0.10.55 -mixin.version=0.10.6+mixin.0.8.4 +loom.version=0.11.32 +mixin.version=0.11.2+mixin.0.8.5 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2a..41d9927a4d 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e09..41dfb87909 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b3f06ccc6..c3c2e49660 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,6 +4,7 @@ include("worldedit-libs") include("worldedit-bukkit:adapters:adapter-1.17.1") include("worldedit-bukkit:adapters:adapter-1.18") +include("worldedit-bukkit:adapters:adapter-1.18.2") listOf("bukkit", "core", "sponge", "fabric", "forge", "cli").forEach { include("worldedit-libs:$it") diff --git a/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightAdapter.java index 7a0972a839..7ecbb6d87e 100644 --- a/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightAdapter.java @@ -23,12 +23,9 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; import com.sk89q.jnbt.AdventureNBTConverter; import com.sk89q.jnbt.ByteArrayTag; @@ -85,11 +82,8 @@ import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; -import net.minecraft.nbt.NbtOps; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; -import net.minecraft.resources.RegistryReadOps; -import net.minecraft.resources.RegistryWriteOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -151,13 +145,14 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; @@ -428,6 +423,11 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { CraftEntity craftEntity = ((CraftEntity) entity); Entity mcEntity = craftEntity.getHandle(); + // Do not allow creating of passenger entity snapshots, passengers are included in the vehicle entity + if (mcEntity.isPassenger()) { + return null; + } + String id = getEntityId(mcEntity); net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); @@ -444,27 +444,48 @@ public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state CraftWorld craftWorld = ((CraftWorld) location.getWorld()); ServerLevel worldServer = craftWorld.getHandle(); - Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle()); + String entityId = state.getType().getId(); - if (createdEntity != null) { - CompoundTag nativeTag = state.getNbtData(); - if (nativeTag != null) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNative(nativeTag); - for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - readTagIntoEntity(tag, createdEntity); - } + CompoundTag nativeTag = state.getNbtData(); + net.minecraft.nbt.CompoundTag tag; + if (nativeTag != null) { + tag = (net.minecraft.nbt.CompoundTag) fromNative(nativeTag); + removeUnwantedEntityTagsRecursively(tag); + } else { + tag = new net.minecraft.nbt.CompoundTag(); + } + + tag.putString("id", entityId); - createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + Entity createdEntity = EntityType.loadEntityRecursive(tag, craftWorld.getHandle(), (loadedEntity) -> { + loadedEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + return loadedEntity; + }); - worldServer.addEntity(createdEntity, SpawnReason.CUSTOM); + if (createdEntity != null) { + worldServer.addAllEntities(createdEntity, SpawnReason.CUSTOM); return createdEntity.getBukkitEntity(); } else { return null; } } + // This removes all unwanted tags from the main entity and all its passengers + private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + + // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive + if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) { + net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND); + + for (int i = 0; i < nbttaglist.size(); ++i) { + removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); + } + } + } + @Override public Component getRichBlockName(BlockType blockType) { return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId()); @@ -481,28 +502,34 @@ public Component getRichItemName(BaseItemStack itemStack) { } @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public Map> getProperties(BlockType blockType) { - Map> properties = Maps.newTreeMap(String::compareTo); - Block block = getBlockFromType(blockType); - StateDefinition blockStateList = - block.getStateDefinition(); - for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { - Property property; + private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() { + @Override + public Property load(net.minecraft.world.level.block.state.properties.Property state) throws Exception { if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { - property = new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); } else if (state instanceof DirectionProperty) { - property = new DirectionalProperty(state.getName(), + return new DirectionalProperty(state.getName(), (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - property = new EnumProperty(state.getName(), + return new EnumProperty(state.getName(), (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { - property = new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); } else { throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName()); } + } + }); + @SuppressWarnings({ "rawtypes" }) + @Override + public Map> getProperties(BlockType blockType) { + Map> properties = new TreeMap<>(); + Block block = getBlockFromType(blockType); + StateDefinition blockStateList = + block.getStateDefinition(); + for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { + Property property = PROPERTY_CACHE.getUnchecked(state); properties.put(property.getName(), property); } return properties; @@ -610,7 +637,7 @@ private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, long seed = options.getSeed().orElse(originalWorld.getSeed()); WorldGenSettings newOpts = options.getSeed().isPresent() - ? replaceSeed(originalWorld, seed, originalOpts) + ? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) : originalOpts; LevelSettings newWorldSettings = new LevelSettings( @@ -655,49 +682,6 @@ private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, } } - private WorldGenSettings replaceSeed(ServerLevel originalWorld, long seed, WorldGenSettings originalOpts) { - RegistryWriteOps nbtReadRegOps = RegistryWriteOps.create( - NbtOps.INSTANCE, - originalWorld.getServer().registryAccess() - ); - RegistryReadOps nbtRegOps = RegistryReadOps.createAndLoad( - NbtOps.INSTANCE, - originalWorld.getServer().getResourceManager(), - originalWorld.getServer().registryAccess() - ); - Codec dimCodec = WorldGenSettings.CODEC; - return dimCodec - .encodeStart(nbtReadRegOps, originalOpts) - .flatMap(tag -> - dimCodec.parse( - recursivelySetSeed(new Dynamic<>(nbtRegOps, tag), seed, new HashSet<>()) - ) - ) - .get() - .map( - l -> l, - error -> { - throw new IllegalStateException("Unable to map GeneratorOptions: " + error.message()); - } - ); - } - - @SuppressWarnings("unchecked") - private Dynamic recursivelySetSeed(Dynamic dynamic, long seed, Set> seen) { - if (!seen.add(dynamic)) { - return dynamic; - } - return dynamic.updateMapValues(pair -> { - if (pair.getFirst().asString("").equals("seed")) { - return pair.mapSecond(v -> v.createLong(seed)); - } - if (pair.getSecond().getValue() instanceof net.minecraft.nbt.CompoundTag) { - return pair.mapSecond(v -> recursivelySetSeed((Dynamic) v, seed, seen)); - } - return pair; - }); - } - private BiomeType adapt(ServerLevel serverWorld, Biome origBiome) { ResourceLocation key = serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(origBiome); if (key == null) { diff --git a/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightWorldNativeAccess.java index 5da95629f5..5911ca77db 100644 --- a/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1.17.1/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_17_R1_2/PaperweightWorldNativeAccess.java @@ -155,6 +155,12 @@ public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state. } } + @Override + public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + ServerLevel world = getWorld(); + newState.onPlace(world, pos, oldState, false); + } + private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false); } diff --git a/worldedit-bukkit/adapters/adapter-1.18.2/build.gradle.kts b/worldedit-bukkit/adapters/adapter-1.18.2/build.gradle.kts new file mode 100644 index 0000000000..31a9e5c851 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.18.2/build.gradle.kts @@ -0,0 +1,6 @@ +applyPaperweightAdapterConfiguration() + +dependencies { + // https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ + paperDevBundle("1.18.2-R0.1-20220304.102823-4") +} diff --git a/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightAdapter.java new file mode 100644 index 0000000000..353dcc9b0d --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightAdapter.java @@ -0,0 +1,1002 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_18_R2; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.Futures; +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Lifecycle; +import com.sk89q.jnbt.AdventureNBTConverter; +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntArrayTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongArrayTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.NBTConstants; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.bukkit.adapter.Refraction; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extension.platform.Watchdog; +import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector2; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.regions.Region; +import com.sk89q.worldedit.registry.state.BooleanProperty; +import com.sk89q.worldedit.registry.state.DirectionalProperty; +import com.sk89q.worldedit.registry.state.EnumProperty; +import com.sk89q.worldedit.registry.state.IntegerProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.util.io.file.SafeFiles; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import com.sk89q.worldedit.world.DataFixer; +import com.sk89q.worldedit.world.RegenOptions; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.item.ItemType; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.util.StringRepresentable; +import net.minecraft.util.thread.BlockableEventLoop; +import net.minecraft.world.Clearable; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelSettings; +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.block.entity.StructureBlockEntity; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.PrimaryLevelData; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World.Environment; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_18_R2.CraftServer; +import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; +import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_18_R2.util.CraftMagicNumbers; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.generator.ChunkGenerator; +import org.spigotmc.SpigotConfig; +import org.spigotmc.WatchdogThread; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ForkJoinPool; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +public final class PaperweightAdapter implements BukkitImplAdapter { + + private final Logger logger = Logger.getLogger(getClass().getCanonicalName()); + + private final Field serverWorldsField; + private final Method getChunkFutureMethod; + private final Field chunkProviderExecutorField; + private final Watchdog watchdog; + + // ------------------------------------------------------------------------ + // Code that may break between versions of Minecraft + // ------------------------------------------------------------------------ + + public PaperweightAdapter() throws NoSuchFieldException, NoSuchMethodException { + // A simple test + CraftServer.class.cast(Bukkit.getServer()); + + int dataVersion = CraftMagicNumbers.INSTANCE.getDataVersion(); + if (dataVersion != 2975) { + throw new UnsupportedClassVersionError("Not 1.18.2!"); + } + + serverWorldsField = CraftServer.class.getDeclaredField("worlds"); + serverWorldsField.setAccessible(true); + + getChunkFutureMethod = ServerChunkCache.class.getDeclaredMethod( + Refraction.pickName("getChunkFutureMainThread", "c"), + int.class, int.class, ChunkStatus.class, boolean.class + ); + getChunkFutureMethod.setAccessible(true); + + chunkProviderExecutorField = ServerChunkCache.class.getDeclaredField( + Refraction.pickName("mainThreadProcessor", "g") + ); + chunkProviderExecutorField.setAccessible(true); + + new PaperweightDataConverters(CraftMagicNumbers.INSTANCE.getDataVersion(), this).build(ForkJoinPool.commonPool()); + + Watchdog watchdog; + try { + Class.forName("org.spigotmc.WatchdogThread"); + watchdog = new SpigotWatchdog(); + } catch (ClassNotFoundException | NoSuchFieldException e) { + try { + watchdog = new MojangWatchdog(((CraftServer) Bukkit.getServer()).getServer()); + } catch (NoSuchFieldException ex) { + watchdog = null; + } + } + this.watchdog = watchdog; + + try { + Class.forName("org.spigotmc.SpigotConfig"); + SpigotConfig.config.set("world-settings.worldeditregentempworld.verbose", false); + } catch (ClassNotFoundException ignored) { + } + } + + @Override + public DataFixer getDataFixer() { + return PaperweightDataConverters.INSTANCE; + } + + /** + * Read the given NBT data into the given tile entity. + * + * @param tileEntity the tile entity + * @param tag the tag + */ + static void readTagIntoTileEntity(net.minecraft.nbt.CompoundTag tag, BlockEntity tileEntity) { + tileEntity.load(tag); + tileEntity.setChanged(); + } + + /** + * Get the ID string of the given entity. + * + * @param entity the entity + * @return the entity ID + */ + private static String getEntityId(Entity entity) { + return EntityType.getKey(entity.getType()).toString(); + } + + /** + * Create an entity using the given entity ID. + * + * @param id the entity ID + * @param world the world + * @return an entity or null + */ + @Nullable + private static Entity createEntityFromId(String id, net.minecraft.world.level.Level world) { + return EntityType.byString(id).map(t -> t.create(world)).orElse(null); + } + + /** + * Write the given NBT data into the given entity. + * + * @param entity the entity + * @param tag the tag + */ + private static void readTagIntoEntity(net.minecraft.nbt.CompoundTag tag, Entity entity) { + entity.load(tag); + } + + /** + * Write the entity's NBT data to the given tag. + * + * @param entity the entity + * @param tag the tag + */ + private static void readEntityIntoTag(Entity entity, net.minecraft.nbt.CompoundTag tag) { + entity.save(tag); + } + + private static Block getBlockFromType(BlockType blockType) { + return Registry.BLOCK.get(ResourceLocation.tryParse(blockType.getId())); + } + + private static Item getItemFromType(ItemType itemType) { + return Registry.ITEM.get(ResourceLocation.tryParse(itemType.getId())); + } + + @Override + public OptionalInt getInternalBlockStateId(BlockData data) { + net.minecraft.world.level.block.state.BlockState state = ((CraftBlockData) data).getState(); + int combinedId = Block.getId(state); + return combinedId == 0 && state.getBlock() != Blocks.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId); + } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + Block mcBlock = getBlockFromType(state.getBlockType()); + net.minecraft.world.level.block.state.BlockState newState = mcBlock.defaultBlockState(); + Map, Object> states = state.getStates(); + newState = applyProperties(mcBlock.getStateDefinition(), newState, states); + final int combinedId = Block.getId(newState); + return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId); + } + + @Override + public BlockState getBlock(Location location) { + checkNotNull(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + final BlockPos blockPos = new BlockPos(x, y, z); + final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); + int internalId = Block.getId(blockData); + BlockState state = BlockStateIdAccess.getBlockStateById(internalId); + if (state == null) { + org.bukkit.block.Block bukkitBlock = location.getBlock(); + state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); + } + + return state; + } + + @Override + public BaseBlock getFullBlock(Location location) { + BlockState state = getBlock(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + final BlockPos blockPos = new BlockPos(x, y, z); + + // Read the NBT data + BlockEntity te = chunk.getBlockEntity(blockPos); + if (te != null) { + net.minecraft.nbt.CompoundTag tag = te.saveWithId(); + return state.toBaseBlock((CompoundTag) toNative(tag)); + } + + return state.toBaseBlock(); + } + + @Override + public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + return new PaperweightWorldNativeAccess(this, + new WeakReference<>(((CraftWorld) world).getHandle())); + } + + private static net.minecraft.core.Direction adapt(Direction face) { + switch (face) { + case NORTH: + return net.minecraft.core.Direction.NORTH; + case SOUTH: + return net.minecraft.core.Direction.SOUTH; + case WEST: + return net.minecraft.core.Direction.WEST; + case EAST: + return net.minecraft.core.Direction.EAST; + case DOWN: + return net.minecraft.core.Direction.DOWN; + case UP: + default: + return net.minecraft.core.Direction.UP; + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private net.minecraft.world.level.block.state.BlockState applyProperties( + StateDefinition stateContainer, + net.minecraft.world.level.block.state.BlockState newState, + Map, Object> states + ) { + for (Map.Entry, Object> state : states.entrySet()) { + net.minecraft.world.level.block.state.properties.Property property = + stateContainer.getProperty(state.getKey().getName()); + Comparable value = (Comparable) state.getValue(); + // we may need to adapt this value, depending on the source prop + if (property instanceof DirectionProperty) { + Direction dir = (Direction) value; + value = adapt(dir); + } else if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + String enumName = (String) value; + value = ((net.minecraft.world.level.block.state.properties.EnumProperty) property) + .getValue(enumName).orElseThrow(() -> + new IllegalStateException( + "Enum property " + property.getName() + " does not contain " + enumName + ) + ); + } + + newState = newState.setValue( + (net.minecraft.world.level.block.state.properties.Property) property, + (Comparable) value + ); + } + return newState; + } + + @Override + public BaseEntity getEntity(org.bukkit.entity.Entity entity) { + checkNotNull(entity); + + CraftEntity craftEntity = ((CraftEntity) entity); + Entity mcEntity = craftEntity.getHandle(); + + // Do not allow creating of passenger entity snapshots, passengers are included in the vehicle entity + if (mcEntity.isPassenger()) { + return null; + } + + String id = getEntityId(mcEntity); + + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + readEntityIntoTag(mcEntity, tag); + return new BaseEntity(com.sk89q.worldedit.world.entity.EntityTypes.get(id), (CompoundTag) toNative(tag)); + } + + @Nullable + @Override + public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) { + checkNotNull(location); + checkNotNull(state); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + ServerLevel worldServer = craftWorld.getHandle(); + + String entityId = state.getType().getId(); + + CompoundTag nativeTag = state.getNbtData(); + net.minecraft.nbt.CompoundTag tag; + if (nativeTag != null) { + tag = (net.minecraft.nbt.CompoundTag) fromNative(nativeTag); + removeUnwantedEntityTagsRecursively(tag); + } else { + tag = new net.minecraft.nbt.CompoundTag(); + } + + tag.putString("id", entityId); + + Entity createdEntity = EntityType.loadEntityRecursive(tag, craftWorld.getHandle(), (loadedEntity) -> { + loadedEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + return loadedEntity; + }); + + if (createdEntity != null) { + worldServer.addFreshEntityWithPassengers(createdEntity, SpawnReason.CUSTOM); + return createdEntity.getBukkitEntity(); + } else { + return null; + } + } + + // This removes all unwanted tags from the main entity and all its passengers + private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + + // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive + if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) { + net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND); + + for (int i = 0; i < nbttaglist.size(); ++i) { + removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); + } + } + } + + @Override + public Component getRichBlockName(BlockType blockType) { + return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId()); + } + + @Override + public Component getRichItemName(ItemType itemType) { + return TranslatableComponent.of(getItemFromType(itemType).getDescriptionId()); + } + + @Override + public Component getRichItemName(BaseItemStack itemStack) { + return TranslatableComponent.of(CraftItemStack.asNMSCopy(BukkitAdapter.adapt(itemStack)).getDescriptionId()); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() { + @Override + public Property load(net.minecraft.world.level.block.state.properties.Property state) throws Exception { + if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { + return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + } else if (state instanceof DirectionProperty) { + return new DirectionalProperty(state.getName(), + (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList())); + } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + return new EnumProperty(state.getName(), + (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList())); + } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { + return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + } else { + throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName()); + } + } + }); + + @SuppressWarnings({ "rawtypes" }) + @Override + public Map> getProperties(BlockType blockType) { + Map> properties = new TreeMap<>(); + Block block = getBlockFromType(blockType); + StateDefinition blockStateList = + block.getStateDefinition(); + for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { + Property property = PROPERTY_CACHE.getUnchecked(state); + properties.put(property.getName(), property); + } + return properties; + } + + @Override + public void sendFakeNBT(Player player, BlockVector3 pos, CompoundBinaryTag nbtData) { + ((CraftPlayer) player).getHandle().networkManager.send(ClientboundBlockEntityDataPacket.create( + new StructureBlockEntity( + new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()), + Blocks.STRUCTURE_BLOCK.defaultBlockState() + ), + __ -> (net.minecraft.nbt.CompoundTag) fromNative(AdventureNBTConverter.fromAdventure(nbtData)) + )); + } + + @Override + public void sendFakeOP(Player player) { + ((CraftPlayer) player).getHandle().networkManager.send(new ClientboundEntityEventPacket( + ((CraftPlayer) player).getHandle(), (byte) 28 + )); + } + + @Override + public org.bukkit.inventory.ItemStack adapt(BaseItemStack item) { + ItemStack stack = new ItemStack(Registry.ITEM.get(ResourceLocation.tryParse(item.getType().getId())), item.getAmount()); + stack.setTag(((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData()))); + return CraftItemStack.asCraftMirror(stack); + } + + @Override + public BaseItemStack adapt(org.bukkit.inventory.ItemStack itemStack) { + final ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack); + final BaseItemStack weStack = new BaseItemStack(BukkitAdapter.asItemType(itemStack.getType()), itemStack.getAmount()); + weStack.setNbtData(((CompoundTag) toNative(nmsStack.getTag()))); + return weStack; + } + + private final LoadingCache fakePlayers + = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new)); + + @Override + public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) { + CraftWorld craftWorld = (CraftWorld) world; + ServerLevel worldServer = craftWorld.getHandle(); + ItemStack stack = CraftItemStack.asNMSCopy(BukkitAdapter.adapt(item instanceof BaseItemStack + ? ((BaseItemStack) item) : new BaseItemStack(item.getType(), item.getNbtData(), 1))); + stack.setTag((net.minecraft.nbt.CompoundTag) fromNative(item.getNbtData())); + + PaperweightFakePlayer fakePlayer; + try { + fakePlayer = fakePlayers.get(worldServer); + } catch (ExecutionException ignored) { + return false; + } + fakePlayer.setItemInHand(InteractionHand.MAIN_HAND, stack); + fakePlayer.absMoveTo(position.getBlockX(), position.getBlockY(), position.getBlockZ(), + (float) face.toVector().toYaw(), (float) face.toVector().toPitch()); + + final BlockPos blockPos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + final Vec3 blockVec = Vec3.atLowerCornerOf(blockPos); + final net.minecraft.core.Direction enumFacing = adapt(face); + BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false); + UseOnContext context = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, rayTrace); + InteractionResult result = stack.useOn(context, InteractionHand.MAIN_HAND); + if (result != InteractionResult.SUCCESS) { + if (worldServer.getBlockState(blockPos).use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace).consumesAction()) { + result = InteractionResult.SUCCESS; + } else { + result = stack.getItem().use(worldServer, fakePlayer, InteractionHand.MAIN_HAND).getResult(); + } + } + + return result == InteractionResult.SUCCESS; + } + + @Override + public boolean canPlaceAt(org.bukkit.World world, BlockVector3 position, BlockState blockState) { + int internalId = BlockStateIdAccess.getBlockStateId(blockState); + net.minecraft.world.level.block.state.BlockState blockData = Block.stateById(internalId); + return blockData.canSurvive(((CraftWorld) world).getHandle(), new BlockPos(position.getX(), position.getY(), position.getZ())); + } + + @Override + public boolean regenerate(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) { + try { + doRegen(bukkitWorld, region, extent, options); + } catch (Exception e) { + throw new IllegalStateException("Regen failed.", e); + } + + return true; + } + + private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, RegenOptions options) throws Exception { + Environment env = bukkitWorld.getEnvironment(); + ChunkGenerator gen = bukkitWorld.getGenerator(); + + Path tempDir = Files.createTempDirectory("WorldEditWorldGen"); + LevelStorageSource levelStorage = LevelStorageSource.createDefault(tempDir); + ResourceKey worldDimKey = getWorldDimKey(env); + try (LevelStorageSource.LevelStorageAccess session = levelStorage.createAccess("worldeditregentempworld", worldDimKey)) { + ServerLevel originalWorld = ((CraftWorld) bukkitWorld).getHandle(); + PrimaryLevelData levelProperties = (PrimaryLevelData) originalWorld.getServer() + .getWorldData().overworldData(); + WorldGenSettings originalOpts = levelProperties.worldGenSettings(); + + long seed = options.getSeed().orElse(originalWorld.getSeed()); + WorldGenSettings newOpts = options.getSeed().isPresent() + ? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) + : originalOpts; + + LevelSettings newWorldSettings = new LevelSettings( + "worldeditregentempworld", + levelProperties.settings.gameType(), + levelProperties.settings.hardcore(), + levelProperties.settings.difficulty(), + levelProperties.settings.allowCommands(), + levelProperties.settings.gameRules(), + levelProperties.settings.getDataPackConfig() + ); + PrimaryLevelData newWorldData = new PrimaryLevelData(newWorldSettings, newOpts, Lifecycle.stable()); + + ServerLevel freshWorld = new ServerLevel( + originalWorld.getServer(), + originalWorld.getServer().executor, + session, newWorldData, + originalWorld.dimension(), + originalWorld.dimensionTypeRegistration(), + new NoOpWorldLoadListener(), + newOpts.dimensions().get(worldDimKey).generator(), + originalWorld.isDebug(), + seed, + ImmutableList.of(), + false, + env, gen, + bukkitWorld.getBiomeProvider() + ); + try { + regenForWorld(region, extent, freshWorld, options); + } finally { + freshWorld.getChunkSource().close(false); + } + } finally { + try { + @SuppressWarnings("unchecked") + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); + map.remove("worldeditregentempworld"); + } catch (IllegalAccessException ignored) { + } + SafeFiles.tryHardToDeleteDir(tempDir); + } + } + + private BiomeType adapt(ServerLevel serverWorld, Biome origBiome) { + ResourceLocation key = serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(origBiome); + if (key == null) { + return null; + } + return BiomeTypes.get(key.toString()); + } + + @SuppressWarnings("unchecked") + private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld, RegenOptions options) throws WorldEditException { + List> chunkLoadings = submitChunkLoadTasks(region, serverWorld); + BlockableEventLoop executor; + try { + executor = (BlockableEventLoop) chunkProviderExecutorField.get(serverWorld.getChunkSource()); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Couldn't get executor for chunk loading.", e); + } + executor.managedBlock(() -> { + // bail out early if a future fails + if (chunkLoadings.stream().anyMatch(ftr -> + ftr.isDone() && Futures.getUnchecked(ftr) == null + )) { + return false; + } + return chunkLoadings.stream().allMatch(CompletableFuture::isDone); + }); + Map chunks = new HashMap<>(); + for (CompletableFuture future : chunkLoadings) { + @Nullable + ChunkAccess chunk = future.getNow(null); + checkState(chunk != null, "Failed to generate a chunk, regen failed."); + chunks.put(chunk.getPos(), chunk); + } + + for (BlockVector3 vec : region) { + BlockPos pos = new BlockPos(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()); + ChunkAccess chunk = chunks.get(new ChunkPos(pos)); + final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(pos); + int internalId = Block.getId(blockData); + BlockStateHolder state = BlockStateIdAccess.getBlockStateById(internalId); + Objects.requireNonNull(state); + BlockEntity blockEntity = chunk.getBlockEntity(pos); + if (blockEntity != null) { + net.minecraft.nbt.CompoundTag tag = blockEntity.saveWithId(); + state = state.toBaseBlock(((CompoundTag) toNative(tag))); + } + extent.setBlock(vec, state.toBaseBlock()); + if (options.shouldRegenBiomes()) { + Biome origBiome = chunk.getNoiseBiome(vec.getX(), vec.getY(), vec.getZ()).value(); + BiomeType adaptedBiome = adapt(serverWorld, origBiome); + if (adaptedBiome != null) { + extent.setBiome(vec, adaptedBiome); + } + } + } + } + + @SuppressWarnings("unchecked") + private List> submitChunkLoadTasks(Region region, ServerLevel serverWorld) { + ServerChunkCache chunkManager = serverWorld.getChunkSource(); + List> chunkLoadings = new ArrayList<>(); + // Pre-gen all the chunks + for (BlockVector2 chunk : region.getChunks()) { + try { + //noinspection unchecked + chunkLoadings.add( + ((CompletableFuture>) + getChunkFutureMethod.invoke(chunkManager, chunk.getX(), chunk.getZ(), ChunkStatus.FEATURES, true)) + .thenApply(either -> either.left().orElse(null)) + ); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException("Couldn't load chunk for regen.", e); + } + } + return chunkLoadings; + } + + private ResourceKey getWorldDimKey(Environment env) { + switch (env) { + case NETHER: + return LevelStem.NETHER; + case THE_END: + return LevelStem.END; + case NORMAL: + default: + return LevelStem.OVERWORLD; + } + } + + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + SideEffect.NEIGHBORS, + SideEffect.LIGHTING, + SideEffect.VALIDATION, + SideEffect.ENTITY_AI, + SideEffect.EVENTS, + SideEffect.UPDATE + ); + + @Override + public Set getSupportedSideEffects() { + return SUPPORTED_SIDE_EFFECTS; + } + + @Override + public boolean clearContainerBlockContents(org.bukkit.World world, BlockVector3 pt) { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + + BlockEntity entity = originalWorld.getBlockEntity(new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ())); + if (entity instanceof Clearable) { + ((Clearable) entity).clearContent(); + return true; + } + return false; + } + + // ------------------------------------------------------------------------ + // Code that is less likely to break + // ------------------------------------------------------------------------ + + /** + * Converts from a non-native NMS NBT structure to a native WorldEdit NBT + * structure. + * + * @param foreign non-native NMS NBT structure + * @return native WorldEdit NBT structure + */ + Tag toNative(net.minecraft.nbt.Tag foreign) { + if (foreign == null) { + return null; + } + if (foreign instanceof net.minecraft.nbt.CompoundTag) { + Map values = new HashMap<>(); + Set foreignKeys = ((net.minecraft.nbt.CompoundTag) foreign).getAllKeys(); + + for (String str : foreignKeys) { + net.minecraft.nbt.Tag base = ((net.minecraft.nbt.CompoundTag) foreign).get(str); + values.put(str, toNative(base)); + } + return new CompoundTag(values); + } else if (foreign instanceof net.minecraft.nbt.ByteTag) { + return new ByteTag(((net.minecraft.nbt.ByteTag) foreign).getAsByte()); + } else if (foreign instanceof net.minecraft.nbt.ByteArrayTag) { + return new ByteArrayTag(((net.minecraft.nbt.ByteArrayTag) foreign).getAsByteArray()); + } else if (foreign instanceof net.minecraft.nbt.DoubleTag) { + return new DoubleTag(((net.minecraft.nbt.DoubleTag) foreign).getAsDouble()); + } else if (foreign instanceof net.minecraft.nbt.FloatTag) { + return new FloatTag(((net.minecraft.nbt.FloatTag) foreign).getAsFloat()); + } else if (foreign instanceof net.minecraft.nbt.IntTag) { + return new IntTag(((net.minecraft.nbt.IntTag) foreign).getAsInt()); + } else if (foreign instanceof net.minecraft.nbt.IntArrayTag) { + return new IntArrayTag(((net.minecraft.nbt.IntArrayTag) foreign).getAsIntArray()); + } else if (foreign instanceof net.minecraft.nbt.LongArrayTag) { + return new LongArrayTag(((net.minecraft.nbt.LongArrayTag) foreign).getAsLongArray()); + } else if (foreign instanceof net.minecraft.nbt.ListTag) { + try { + return toNativeList((net.minecraft.nbt.ListTag) foreign); + } catch (Throwable e) { + logger.log(Level.WARNING, "Failed to convert net.minecraft.nbt.ListTag", e); + return new ListTag(ByteTag.class, new ArrayList()); + } + } else if (foreign instanceof net.minecraft.nbt.LongTag) { + return new LongTag(((net.minecraft.nbt.LongTag) foreign).getAsLong()); + } else if (foreign instanceof net.minecraft.nbt.ShortTag) { + return new ShortTag(((net.minecraft.nbt.ShortTag) foreign).getAsShort()); + } else if (foreign instanceof net.minecraft.nbt.StringTag) { + return new StringTag(foreign.getAsString()); + } else if (foreign instanceof net.minecraft.nbt.EndTag) { + return new EndTag(); + } else { + throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); + } + } + + /** + * Convert a foreign NBT list tag into a native WorldEdit one. + * + * @param foreign the foreign tag + * @return the converted tag + * @throws SecurityException on error + * @throws IllegalArgumentException on error + */ + private ListTag toNativeList(net.minecraft.nbt.ListTag foreign) throws SecurityException, IllegalArgumentException { + List values = new ArrayList<>(); + int type = foreign.getElementType(); + + for (net.minecraft.nbt.Tag tag : foreign) { + values.add(toNative(tag)); + } + + Class cls = NBTConstants.getClassFromType(type); + return new ListTag(cls, values); + } + + /** + * Converts a WorldEdit-native NBT structure to a NMS structure. + * + * @param foreign structure to convert + * @return non-native structure + */ + net.minecraft.nbt.Tag fromNative(Tag foreign) { + if (foreign == null) { + return null; + } + if (foreign instanceof CompoundTag) { + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + for (Map.Entry entry : ((CompoundTag) foreign) + .getValue().entrySet()) { + tag.put(entry.getKey(), fromNative(entry.getValue())); + } + return tag; + } else if (foreign instanceof ByteTag) { + return net.minecraft.nbt.ByteTag.valueOf(((ByteTag) foreign).getValue()); + } else if (foreign instanceof ByteArrayTag) { + return new net.minecraft.nbt.ByteArrayTag(((ByteArrayTag) foreign).getValue()); + } else if (foreign instanceof DoubleTag) { + return net.minecraft.nbt.DoubleTag.valueOf(((DoubleTag) foreign).getValue()); + } else if (foreign instanceof FloatTag) { + return net.minecraft.nbt.FloatTag.valueOf(((FloatTag) foreign).getValue()); + } else if (foreign instanceof IntTag) { + return net.minecraft.nbt.IntTag.valueOf(((IntTag) foreign).getValue()); + } else if (foreign instanceof IntArrayTag) { + return new net.minecraft.nbt.IntArrayTag(((IntArrayTag) foreign).getValue()); + } else if (foreign instanceof LongArrayTag) { + return new net.minecraft.nbt.LongArrayTag(((LongArrayTag) foreign).getValue()); + } else if (foreign instanceof ListTag) { + net.minecraft.nbt.ListTag tag = new net.minecraft.nbt.ListTag(); + ListTag foreignList = (ListTag) foreign; + for (Tag t : foreignList.getValue()) { + tag.add(fromNative(t)); + } + return tag; + } else if (foreign instanceof LongTag) { + return net.minecraft.nbt.LongTag.valueOf(((LongTag) foreign).getValue()); + } else if (foreign instanceof ShortTag) { + return net.minecraft.nbt.ShortTag.valueOf(((ShortTag) foreign).getValue()); + } else if (foreign instanceof StringTag) { + return net.minecraft.nbt.StringTag.valueOf(((StringTag) foreign).getValue()); + } else if (foreign instanceof EndTag) { + return net.minecraft.nbt.EndTag.INSTANCE; + } else { + throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); + } + } + + @Override + public boolean supportsWatchdog() { + return watchdog != null; + } + + @Override + public void tickWatchdog() { + watchdog.tick(); + } + + private class SpigotWatchdog implements Watchdog { + private final Field instanceField; + private final Field lastTickField; + + SpigotWatchdog() throws NoSuchFieldException { + Field instanceField = WatchdogThread.class.getDeclaredField("instance"); + instanceField.setAccessible(true); + this.instanceField = instanceField; + + Field lastTickField = WatchdogThread.class.getDeclaredField("lastTick"); + lastTickField.setAccessible(true); + this.lastTickField = lastTickField; + } + + @Override + public void tick() { + try { + WatchdogThread instance = (WatchdogThread) this.instanceField.get(null); + if ((long) lastTickField.get(instance) != 0) { + WatchdogThread.tick(); + } + } catch (IllegalAccessException e) { + logger.log(Level.WARNING, "Failed to tick watchdog", e); + } + } + } + + private static class MojangWatchdog implements Watchdog { + private final DedicatedServer server; + private final Field tickField; + + MojangWatchdog(DedicatedServer server) throws NoSuchFieldException { + this.server = server; + Field tickField = MinecraftServer.class.getDeclaredField( + Refraction.pickName("nextTickTime", "ao") + ); + tickField.setAccessible(true); + this.tickField = tickField; + } + + @Override + public void tick() { + try { + tickField.set(server, Util.getMillis()); + } catch (IllegalAccessException ignored) { + } + } + } + + private static class NoOpWorldLoadListener implements ChunkProgressListener { + @Override + public void updateSpawnPos(ChunkPos spawnPos) { + } + + @Override + public void onStatusChange(ChunkPos pos, @org.jetbrains.annotations.Nullable ChunkStatus status) { + } + + @Override + public void start() { + } + + @Override + public void stop() { + } + + @Override + public void setChunkRadius(int radius) { + } + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightDataConverters.java b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightDataConverters.java new file mode 100644 index 0000000000..8678a34f5b --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightDataConverters.java @@ -0,0 +1,2795 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_18_R2; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.mojang.datafixers.DSL.TypeReference; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import com.sk89q.jnbt.CompoundTag; +import net.minecraft.core.Direction; +import net.minecraft.nbt.NbtOps; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.GsonHelper; +import net.minecraft.util.StringUtil; +import net.minecraft.util.datafix.DataFixers; +import net.minecraft.util.datafix.fixes.References; +import net.minecraft.world.item.DyeColor; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +/** + * Handles converting all Pre 1.13.2 data using the Legacy DataFix System (ported to 1.13.2) + * + * We register a DFU Fixer per Legacy Data Version and apply the fixes using legacy strategy + * which is safer, faster and cleaner code. + * + * The pre DFU code did not fail when the Source version was unknown. + * + * This class also provides util methods for converting compounds to wrap the update call to + * receive the source version in the compound + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +class PaperweightDataConverters extends DataFixerBuilder implements com.sk89q.worldedit.world.DataFixer { + + @SuppressWarnings("unchecked") + @Override + public T fixUp(FixType type, T original, int srcVer) { + if (type == FixTypes.CHUNK) { + return (T) fixChunk((CompoundTag) original, srcVer); + } else if (type == FixTypes.BLOCK_ENTITY) { + return (T) fixBlockEntity((CompoundTag) original, srcVer); + } else if (type == FixTypes.ENTITY) { + return (T) fixEntity((CompoundTag) original, srcVer); + } else if (type == FixTypes.BLOCK_STATE) { + return (T) fixBlockState((String) original, srcVer); + } else if (type == FixTypes.ITEM_TYPE) { + return (T) fixItemType((String) original, srcVer); + } else if (type == FixTypes.BIOME) { + return (T) fixBiome((String) original, srcVer); + } + return original; + } + + private CompoundTag fixChunk(CompoundTag originalChunk, int srcVer) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative(originalChunk); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer); + return (CompoundTag) adapter.toNative(fixed); + } + + private CompoundTag fixBlockEntity(CompoundTag origTileEnt, int srcVer) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative(origTileEnt); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer); + return (CompoundTag) adapter.toNative(fixed); + } + + private CompoundTag fixEntity(CompoundTag origEnt, int srcVer) { + net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) adapter.fromNative(origEnt); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer); + return (CompoundTag) adapter.toNative(fixed); + } + + private String fixBlockState(String blockState, int srcVer) { + net.minecraft.nbt.CompoundTag stateNBT = stateToNBT(blockState); + Dynamic dynamic = new Dynamic<>(OPS_NBT, stateNBT); + net.minecraft.nbt.CompoundTag fixed = (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(References.BLOCK_STATE, dynamic, srcVer, DATA_VERSION).getValue(); + return nbtToState(fixed); + } + + private String nbtToState(net.minecraft.nbt.CompoundTag tagCompound) { + StringBuilder sb = new StringBuilder(); + sb.append(tagCompound.getString("Name")); + if (tagCompound.contains("Properties", 10)) { + sb.append('['); + net.minecraft.nbt.CompoundTag props = tagCompound.getCompound("Properties"); + sb.append(props.getAllKeys().stream().map(k -> k + "=" + props.getString(k).replace("\"", "")).collect(Collectors.joining(","))); + sb.append(']'); + } + return sb.toString(); + } + + private static net.minecraft.nbt.CompoundTag stateToNBT(String blockState) { + int propIdx = blockState.indexOf('['); + net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); + if (propIdx < 0) { + tag.putString("Name", blockState); + } else { + tag.putString("Name", blockState.substring(0, propIdx)); + net.minecraft.nbt.CompoundTag propTag = new net.minecraft.nbt.CompoundTag(); + String props = blockState.substring(propIdx + 1, blockState.length() - 1); + String[] propArr = props.split(","); + for (String pair : propArr) { + final String[] split = pair.split("="); + propTag.putString(split[0], split[1]); + } + tag.put("Properties", propTag); + } + return tag; + } + + private String fixBiome(String key, int srcVer) { + return fixName(key, srcVer, References.BIOME); + } + + private String fixItemType(String key, int srcVer) { + return fixName(key, srcVer, References.ITEM_NAME); + } + + private static String fixName(String key, int srcVer, TypeReference type) { + return INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, net.minecraft.nbt.StringTag.valueOf(key)), srcVer, DATA_VERSION) + .getValue().getAsString(); + } + + private final PaperweightAdapter adapter; + + private static final NbtOps OPS_NBT = NbtOps.INSTANCE; + private static final int LEGACY_VERSION = 1343; + private static int DATA_VERSION; + static PaperweightDataConverters INSTANCE; + + private final Map> converters = new EnumMap<>(LegacyType.class); + private final Map> inspectors = new EnumMap<>(LegacyType.class); + + // Set on build + private DataFixer fixer; + private static final Map DFU_TO_LEGACY = new HashMap<>(); + + public enum LegacyType { + LEVEL(References.LEVEL), + PLAYER(References.PLAYER), + CHUNK(References.CHUNK), + BLOCK_ENTITY(References.BLOCK_ENTITY), + ENTITY(References.ENTITY), + ITEM_INSTANCE(References.ITEM_STACK), + OPTIONS(References.OPTIONS), + STRUCTURE(References.STRUCTURE); + + private final TypeReference type; + + LegacyType(TypeReference type) { + this.type = type; + DFU_TO_LEGACY.put(type.typeName(), this); + } + + public TypeReference getDFUType() { + return type; + } + } + + PaperweightDataConverters(int dataVersion, PaperweightAdapter adapter) { + super(dataVersion); + DATA_VERSION = dataVersion; + INSTANCE = this; + this.adapter = adapter; + registerConverters(); + registerInspectors(); + } + + + // Called after fixers are built and ready for FIXING + @Override + public DataFixer build(final Executor executor) { + return this.fixer = new WrappedDataFixer(DataFixers.getDataFixer()); + } + + @SuppressWarnings("unchecked") + private class WrappedDataFixer implements DataFixer { + private final DataFixer realFixer; + + WrappedDataFixer(DataFixer realFixer) { + this.realFixer = realFixer; + } + + @Override + public Dynamic update(TypeReference type, Dynamic dynamic, int sourceVer, int targetVer) { + LegacyType legacyType = DFU_TO_LEGACY.get(type.typeName()); + if (sourceVer < LEGACY_VERSION && legacyType != null) { + net.minecraft.nbt.CompoundTag cmp = (net.minecraft.nbt.CompoundTag) dynamic.getValue(); + int desiredVersion = Math.min(targetVer, LEGACY_VERSION); + + cmp = convert(legacyType, cmp, sourceVer, desiredVersion); + sourceVer = desiredVersion; + dynamic = new Dynamic(OPS_NBT, cmp); + } + return realFixer.update(type, dynamic, sourceVer, targetVer); + } + + private net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int desiredVersion) { + List converters = PaperweightDataConverters.this.converters.get(type); + if (converters != null && !converters.isEmpty()) { + for (DataConverter converter : converters) { + int dataVersion = converter.getDataVersion(); + if (dataVersion > sourceVer && dataVersion <= desiredVersion) { + cmp = converter.convert(cmp); + } + } + } + + List inspectors = PaperweightDataConverters.this.inspectors.get(type); + if (inspectors != null && !inspectors.isEmpty()) { + for (DataInspector inspector : inspectors) { + cmp = inspector.inspect(cmp, sourceVer, desiredVersion); + } + } + + return cmp; + } + + @Override + public Schema getSchema(int i) { + return realFixer.getSchema(i); + } + } + + public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp) { + return convert(type.getDFUType(), cmp); + } + + public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) { + return convert(type.getDFUType(), cmp, sourceVer); + } + + public static net.minecraft.nbt.CompoundTag convert(LegacyType type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + return convert(type.getDFUType(), cmp, sourceVer, targetVer); + } + + public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp) { + int i = cmp.contains("DataVersion", 99) ? cmp.getInt("DataVersion") : -1; + return convert(type, cmp, i); + } + + public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer) { + return convert(type, cmp, sourceVer, DATA_VERSION); + } + + public static net.minecraft.nbt.CompoundTag convert(TypeReference type, net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (sourceVer >= targetVer) { + return cmp; + } + return (net.minecraft.nbt.CompoundTag) INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, cmp), sourceVer, targetVer).getValue(); + } + + + public interface DataInspector { + net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer); + } + + public interface DataConverter { + + int getDataVersion(); + + net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp); + } + + + private void registerInspector(LegacyType type, DataInspector inspector) { + this.inspectors.computeIfAbsent(type, k -> new ArrayList<>()).add(inspector); + } + + private void registerConverter(LegacyType type, DataConverter converter) { + int version = converter.getDataVersion(); + + List list = this.converters.computeIfAbsent(type, k -> new ArrayList<>()); + if (!list.isEmpty() && list.get(list.size() - 1).getDataVersion() > version) { + for (int j = 0; j < list.size(); ++j) { + if (list.get(j).getDataVersion() > version) { + list.add(j, converter); + break; + } + } + } else { + list.add(converter); + } + } + + private void registerInspectors() { + registerEntityItemList("EntityHorseDonkey", "SaddleItem", "Items"); + registerEntityItemList("EntityHorseMule", "Items"); + registerEntityItemList("EntityMinecartChest", "Items"); + registerEntityItemList("EntityMinecartHopper", "Items"); + registerEntityItemList("EntityVillager", "Inventory"); + registerEntityItemListEquipment("EntityArmorStand"); + registerEntityItemListEquipment("EntityBat"); + registerEntityItemListEquipment("EntityBlaze"); + registerEntityItemListEquipment("EntityCaveSpider"); + registerEntityItemListEquipment("EntityChicken"); + registerEntityItemListEquipment("EntityCow"); + registerEntityItemListEquipment("EntityCreeper"); + registerEntityItemListEquipment("EntityEnderDragon"); + registerEntityItemListEquipment("EntityEnderman"); + registerEntityItemListEquipment("EntityEndermite"); + registerEntityItemListEquipment("EntityEvoker"); + registerEntityItemListEquipment("EntityGhast"); + registerEntityItemListEquipment("EntityGiantZombie"); + registerEntityItemListEquipment("EntityGuardian"); + registerEntityItemListEquipment("EntityGuardianElder"); + registerEntityItemListEquipment("EntityHorse"); + registerEntityItemListEquipment("EntityHorseDonkey"); + registerEntityItemListEquipment("EntityHorseMule"); + registerEntityItemListEquipment("EntityHorseSkeleton"); + registerEntityItemListEquipment("EntityHorseZombie"); + registerEntityItemListEquipment("EntityIronGolem"); + registerEntityItemListEquipment("EntityMagmaCube"); + registerEntityItemListEquipment("EntityMushroomCow"); + registerEntityItemListEquipment("EntityOcelot"); + registerEntityItemListEquipment("EntityPig"); + registerEntityItemListEquipment("EntityPigZombie"); + registerEntityItemListEquipment("EntityRabbit"); + registerEntityItemListEquipment("EntitySheep"); + registerEntityItemListEquipment("EntityShulker"); + registerEntityItemListEquipment("EntitySilverfish"); + registerEntityItemListEquipment("EntitySkeleton"); + registerEntityItemListEquipment("EntitySkeletonStray"); + registerEntityItemListEquipment("EntitySkeletonWither"); + registerEntityItemListEquipment("EntitySlime"); + registerEntityItemListEquipment("EntitySnowman"); + registerEntityItemListEquipment("EntitySpider"); + registerEntityItemListEquipment("EntitySquid"); + registerEntityItemListEquipment("EntityVex"); + registerEntityItemListEquipment("EntityVillager"); + registerEntityItemListEquipment("EntityVindicator"); + registerEntityItemListEquipment("EntityWitch"); + registerEntityItemListEquipment("EntityWither"); + registerEntityItemListEquipment("EntityWolf"); + registerEntityItemListEquipment("EntityZombie"); + registerEntityItemListEquipment("EntityZombieHusk"); + registerEntityItemListEquipment("EntityZombieVillager"); + registerEntityItemSingle("EntityFireworks", "FireworksItem"); + registerEntityItemSingle("EntityHorse", "ArmorItem"); + registerEntityItemSingle("EntityHorse", "SaddleItem"); + registerEntityItemSingle("EntityHorseMule", "SaddleItem"); + registerEntityItemSingle("EntityHorseSkeleton", "SaddleItem"); + registerEntityItemSingle("EntityHorseZombie", "SaddleItem"); + registerEntityItemSingle("EntityItem", "Item"); + registerEntityItemSingle("EntityItemFrame", "Item"); + registerEntityItemSingle("EntityPotion", "Potion"); + + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItem("TileEntityRecordPlayer", "RecordItem")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityBrewingStand", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityChest", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDispenser", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityDropper", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityFurnace", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityHopper", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorItemList("TileEntityShulkerBox", "Items")); + registerInspector(LegacyType.BLOCK_ENTITY, new DataInspectorMobSpawnerMobs()); + registerInspector(LegacyType.CHUNK, new DataInspectorChunks()); + registerInspector(LegacyType.ENTITY, new DataInspectorCommandBlock()); + registerInspector(LegacyType.ENTITY, new DataInspectorEntityPassengers()); + registerInspector(LegacyType.ENTITY, new DataInspectorMobSpawnerMinecart()); + registerInspector(LegacyType.ENTITY, new DataInspectorVillagers()); + registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorBlockEntity()); + registerInspector(LegacyType.ITEM_INSTANCE, new DataInspectorEntity()); + registerInspector(LegacyType.LEVEL, new DataInspectorLevelPlayer()); + registerInspector(LegacyType.PLAYER, new DataInspectorPlayer()); + registerInspector(LegacyType.PLAYER, new DataInspectorPlayerVehicle()); + registerInspector(LegacyType.STRUCTURE, new DataInspectorStructure()); + } + + private void registerConverters() { + registerConverter(LegacyType.ENTITY, new DataConverterEquipment()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterSignText()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterMaterialId()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionId()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterSpawnEgg()); + registerConverter(LegacyType.ENTITY, new DataConverterMinecart()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterMobSpawner()); + registerConverter(LegacyType.ENTITY, new DataConverterUUID()); + registerConverter(LegacyType.ENTITY, new DataConverterHealth()); + registerConverter(LegacyType.ENTITY, new DataConverterSaddle()); + registerConverter(LegacyType.ENTITY, new DataConverterHanging()); + registerConverter(LegacyType.ENTITY, new DataConverterDropChances()); + registerConverter(LegacyType.ENTITY, new DataConverterRiding()); + registerConverter(LegacyType.ENTITY, new DataConverterArmorStand()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBook()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterCookedFish()); + registerConverter(LegacyType.ENTITY, new DataConverterZombie()); + registerConverter(LegacyType.OPTIONS, new DataConverterVBO()); + registerConverter(LegacyType.ENTITY, new DataConverterGuardian()); + registerConverter(LegacyType.ENTITY, new DataConverterSkeleton()); + registerConverter(LegacyType.ENTITY, new DataConverterZombieType()); + registerConverter(LegacyType.ENTITY, new DataConverterHorse()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterTileEntity()); + registerConverter(LegacyType.ENTITY, new DataConverterEntity()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBanner()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterPotionWater()); + registerConverter(LegacyType.ENTITY, new DataConverterShulker()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterShulkerBoxItem()); + registerConverter(LegacyType.BLOCK_ENTITY, new DataConverterShulkerBoxBlock()); + registerConverter(LegacyType.OPTIONS, new DataConverterLang()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterTotem()); + registerConverter(LegacyType.CHUNK, new DataConverterBedBlock()); + registerConverter(LegacyType.ITEM_INSTANCE, new DataConverterBedItem()); + } + + private void registerEntityItemList(String type, String... keys) { + registerInspector(LegacyType.ENTITY, new DataInspectorItemList(type, keys)); + } + + private void registerEntityItemSingle(String type, String key) { + registerInspector(LegacyType.ENTITY, new DataInspectorItem(type, key)); + } + + private void registerEntityItemListEquipment(String type) { + registerEntityItemList(type, "ArmorItems", "HandItems"); + } + + private static final Map OLD_ID_TO_KEY_MAP = new HashMap<>(); + + static { + final Map map = OLD_ID_TO_KEY_MAP; + map.put("EntityItem", new ResourceLocation("item")); + map.put("EntityExperienceOrb", new ResourceLocation("xp_orb")); + map.put("EntityAreaEffectCloud", new ResourceLocation("area_effect_cloud")); + map.put("EntityGuardianElder", new ResourceLocation("elder_guardian")); + map.put("EntitySkeletonWither", new ResourceLocation("wither_skeleton")); + map.put("EntitySkeletonStray", new ResourceLocation("stray")); + map.put("EntityEgg", new ResourceLocation("egg")); + map.put("EntityLeash", new ResourceLocation("leash_knot")); + map.put("EntityPainting", new ResourceLocation("painting")); + map.put("EntityTippedArrow", new ResourceLocation("arrow")); + map.put("EntitySnowball", new ResourceLocation("snowball")); + map.put("EntityLargeFireball", new ResourceLocation("fireball")); + map.put("EntitySmallFireball", new ResourceLocation("small_fireball")); + map.put("EntityEnderPearl", new ResourceLocation("ender_pearl")); + map.put("EntityEnderSignal", new ResourceLocation("eye_of_ender_signal")); + map.put("EntityPotion", new ResourceLocation("potion")); + map.put("EntityThrownExpBottle", new ResourceLocation("xp_bottle")); + map.put("EntityItemFrame", new ResourceLocation("item_frame")); + map.put("EntityWitherSkull", new ResourceLocation("wither_skull")); + map.put("EntityTNTPrimed", new ResourceLocation("tnt")); + map.put("EntityFallingBlock", new ResourceLocation("falling_block")); + map.put("EntityFireworks", new ResourceLocation("fireworks_rocket")); + map.put("EntityZombieHusk", new ResourceLocation("husk")); + map.put("EntitySpectralArrow", new ResourceLocation("spectral_arrow")); + map.put("EntityShulkerBullet", new ResourceLocation("shulker_bullet")); + map.put("EntityDragonFireball", new ResourceLocation("dragon_fireball")); + map.put("EntityZombieVillager", new ResourceLocation("zombie_villager")); + map.put("EntityHorseSkeleton", new ResourceLocation("skeleton_horse")); + map.put("EntityHorseZombie", new ResourceLocation("zombie_horse")); + map.put("EntityArmorStand", new ResourceLocation("armor_stand")); + map.put("EntityHorseDonkey", new ResourceLocation("donkey")); + map.put("EntityHorseMule", new ResourceLocation("mule")); + map.put("EntityEvokerFangs", new ResourceLocation("evocation_fangs")); + map.put("EntityEvoker", new ResourceLocation("evocation_illager")); + map.put("EntityVex", new ResourceLocation("vex")); + map.put("EntityVindicator", new ResourceLocation("vindication_illager")); + map.put("EntityIllagerIllusioner", new ResourceLocation("illusion_illager")); + map.put("EntityMinecartCommandBlock", new ResourceLocation("commandblock_minecart")); + map.put("EntityBoat", new ResourceLocation("boat")); + map.put("EntityMinecartRideable", new ResourceLocation("minecart")); + map.put("EntityMinecartChest", new ResourceLocation("chest_minecart")); + map.put("EntityMinecartFurnace", new ResourceLocation("furnace_minecart")); + map.put("EntityMinecartTNT", new ResourceLocation("tnt_minecart")); + map.put("EntityMinecartHopper", new ResourceLocation("hopper_minecart")); + map.put("EntityMinecartMobSpawner", new ResourceLocation("spawner_minecart")); + map.put("EntityCreeper", new ResourceLocation("creeper")); + map.put("EntitySkeleton", new ResourceLocation("skeleton")); + map.put("EntitySpider", new ResourceLocation("spider")); + map.put("EntityGiantZombie", new ResourceLocation("giant")); + map.put("EntityZombie", new ResourceLocation("zombie")); + map.put("EntitySlime", new ResourceLocation("slime")); + map.put("EntityGhast", new ResourceLocation("ghast")); + map.put("EntityPigZombie", new ResourceLocation("zombie_pigman")); + map.put("EntityEnderman", new ResourceLocation("enderman")); + map.put("EntityCaveSpider", new ResourceLocation("cave_spider")); + map.put("EntitySilverfish", new ResourceLocation("silverfish")); + map.put("EntityBlaze", new ResourceLocation("blaze")); + map.put("EntityMagmaCube", new ResourceLocation("magma_cube")); + map.put("EntityEnderDragon", new ResourceLocation("ender_dragon")); + map.put("EntityWither", new ResourceLocation("wither")); + map.put("EntityBat", new ResourceLocation("bat")); + map.put("EntityWitch", new ResourceLocation("witch")); + map.put("EntityEndermite", new ResourceLocation("endermite")); + map.put("EntityGuardian", new ResourceLocation("guardian")); + map.put("EntityShulker", new ResourceLocation("shulker")); + map.put("EntityPig", new ResourceLocation("pig")); + map.put("EntitySheep", new ResourceLocation("sheep")); + map.put("EntityCow", new ResourceLocation("cow")); + map.put("EntityChicken", new ResourceLocation("chicken")); + map.put("EntitySquid", new ResourceLocation("squid")); + map.put("EntityWolf", new ResourceLocation("wolf")); + map.put("EntityMushroomCow", new ResourceLocation("mooshroom")); + map.put("EntitySnowman", new ResourceLocation("snowman")); + map.put("EntityOcelot", new ResourceLocation("ocelot")); + map.put("EntityIronGolem", new ResourceLocation("villager_golem")); + map.put("EntityHorse", new ResourceLocation("horse")); + map.put("EntityRabbit", new ResourceLocation("rabbit")); + map.put("EntityPolarBear", new ResourceLocation("polar_bear")); + map.put("EntityLlama", new ResourceLocation("llama")); + map.put("EntityLlamaSpit", new ResourceLocation("llama_spit")); + map.put("EntityParrot", new ResourceLocation("parrot")); + map.put("EntityVillager", new ResourceLocation("villager")); + map.put("EntityEnderCrystal", new ResourceLocation("ender_crystal")); + map.put("TileEntityFurnace", new ResourceLocation("furnace")); + map.put("TileEntityChest", new ResourceLocation("chest")); + map.put("TileEntityEnderChest", new ResourceLocation("ender_chest")); + map.put("TileEntityRecordPlayer", new ResourceLocation("jukebox")); + map.put("TileEntityDispenser", new ResourceLocation("dispenser")); + map.put("TileEntityDropper", new ResourceLocation("dropper")); + map.put("TileEntitySign", new ResourceLocation("sign")); + map.put("TileEntityMobSpawner", new ResourceLocation("mob_spawner")); + map.put("TileEntityNote", new ResourceLocation("noteblock")); + map.put("TileEntityPiston", new ResourceLocation("piston")); + map.put("TileEntityBrewingStand", new ResourceLocation("brewing_stand")); + map.put("TileEntityEnchantTable", new ResourceLocation("enchanting_table")); + map.put("TileEntityEnderPortal", new ResourceLocation("end_portal")); + map.put("TileEntityBeacon", new ResourceLocation("beacon")); + map.put("TileEntitySkull", new ResourceLocation("skull")); + map.put("TileEntityLightDetector", new ResourceLocation("daylight_detector")); + map.put("TileEntityHopper", new ResourceLocation("hopper")); + map.put("TileEntityComparator", new ResourceLocation("comparator")); + map.put("TileEntityFlowerPot", new ResourceLocation("flower_pot")); + map.put("TileEntityBanner", new ResourceLocation("banner")); + map.put("TileEntityStructure", new ResourceLocation("structure_block")); + map.put("TileEntityEndGateway", new ResourceLocation("end_gateway")); + map.put("TileEntityCommand", new ResourceLocation("command_block")); + map.put("TileEntityShulkerBox", new ResourceLocation("shulker_box")); + map.put("TileEntityBed", new ResourceLocation("bed")); + } + + private static ResourceLocation getKey(String type) { + final ResourceLocation key = OLD_ID_TO_KEY_MAP.get(type); + if (key == null) { + throw new IllegalArgumentException("Unknown mapping for " + type); + } + return key; + } + + private static void convertCompound(LegacyType type, net.minecraft.nbt.CompoundTag cmp, String key, int sourceVer, int targetVer) { + cmp.put(key, convert(type, cmp.getCompound(key), sourceVer, targetVer)); + } + + private static void convertItem(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) { + if (nbttagcompound.contains(key, 10)) { + convertCompound(LegacyType.ITEM_INSTANCE, nbttagcompound, key, sourceVer, targetVer); + } + } + + private static void convertItems(net.minecraft.nbt.CompoundTag nbttagcompound, String key, int sourceVer, int targetVer) { + if (nbttagcompound.contains(key, 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound.getList(key, 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ITEM_INSTANCE, nbttaglist.getCompound(j), sourceVer, targetVer)); + } + } + + } + + private static class DataConverterEquipment implements DataConverter { + + DataConverterEquipment() { + } + + public int getDataVersion() { + return 100; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Equipment", 10); + net.minecraft.nbt.ListTag nbttaglist1; + + if (!nbttaglist.isEmpty() && !cmp.contains("HandItems", 10)) { + nbttaglist1 = new net.minecraft.nbt.ListTag(); + nbttaglist1.add(nbttaglist.get(0)); + nbttaglist1.add(new net.minecraft.nbt.CompoundTag()); + cmp.put("HandItems", nbttaglist1); + } + + if (nbttaglist.size() > 1 && !cmp.contains("ArmorItem", 10)) { + nbttaglist1 = new net.minecraft.nbt.ListTag(); + nbttaglist1.add(nbttaglist.get(1)); + nbttaglist1.add(nbttaglist.get(2)); + nbttaglist1.add(nbttaglist.get(3)); + nbttaglist1.add(nbttaglist.get(4)); + cmp.put("ArmorItems", nbttaglist1); + } + + cmp.remove("Equipment"); + if (cmp.contains("DropChances", 9)) { + nbttaglist1 = cmp.getList("DropChances", 5); + net.minecraft.nbt.ListTag nbttaglist2; + + if (!cmp.contains("HandDropChances", 10)) { + nbttaglist2 = new net.minecraft.nbt.ListTag(); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(0))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(0.0F)); + cmp.put("HandDropChances", nbttaglist2); + } + + if (!cmp.contains("ArmorDropChances", 10)) { + nbttaglist2 = new net.minecraft.nbt.ListTag(); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(1))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(2))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(3))); + nbttaglist2.add(net.minecraft.nbt.FloatTag.valueOf(nbttaglist1.getFloat(4))); + cmp.put("ArmorDropChances", nbttaglist2); + } + + cmp.remove("DropChances"); + } + + return cmp; + } + } + + private static class DataInspectorBlockEntity implements DataInspector { + + private static final Map b = Maps.newHashMap(); + private static final Map c = Maps.newHashMap(); + + DataInspectorBlockEntity() { + } + + @Nullable + private static String convertEntityId(int i, String s) { + String key = new ResourceLocation(s).toString(); + if (i < 515 && DataInspectorBlockEntity.b.containsKey(key)) { + return DataInspectorBlockEntity.b.get(key); + } else { + return DataInspectorBlockEntity.c.get(key); + } + } + + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (!cmp.contains("tag", 10)) { + return cmp; + } else { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + String s = cmp.getString("id"); + String s1 = convertEntityId(sourceVer, s); + boolean flag; + + if (s1 == null) { + // CraftBukkit - Remove unnecessary warning (occurs when deserializing a Shulker Box item) + // DataInspectorBlockEntity.a.warn("Unable to resolve BlockEntity for ItemInstance: {}", s); + flag = false; + } else { + flag = !nbttagcompound2.contains("id"); + nbttagcompound2.putString("id", s1); + } + + convert(LegacyType.BLOCK_ENTITY, nbttagcompound2, sourceVer, targetVer); + if (flag) { + nbttagcompound2.remove("id"); + } + } + + return cmp; + } + } + + static { + Map map = DataInspectorBlockEntity.b; + + map.put("minecraft:furnace", "Furnace"); + map.put("minecraft:lit_furnace", "Furnace"); + map.put("minecraft:chest", "Chest"); + map.put("minecraft:trapped_chest", "Chest"); + map.put("minecraft:ender_chest", "EnderChest"); + map.put("minecraft:jukebox", "RecordPlayer"); + map.put("minecraft:dispenser", "Trap"); + map.put("minecraft:dropper", "Dropper"); + map.put("minecraft:sign", "Sign"); + map.put("minecraft:mob_spawner", "MobSpawner"); + map.put("minecraft:noteblock", "Music"); + map.put("minecraft:brewing_stand", "Cauldron"); + map.put("minecraft:enhanting_table", "EnchantTable"); + map.put("minecraft:command_block", "CommandBlock"); + map.put("minecraft:beacon", "Beacon"); + map.put("minecraft:skull", "Skull"); + map.put("minecraft:daylight_detector", "DLDetector"); + map.put("minecraft:hopper", "Hopper"); + map.put("minecraft:banner", "Banner"); + map.put("minecraft:flower_pot", "FlowerPot"); + map.put("minecraft:repeating_command_block", "CommandBlock"); + map.put("minecraft:chain_command_block", "CommandBlock"); + map.put("minecraft:standing_sign", "Sign"); + map.put("minecraft:wall_sign", "Sign"); + map.put("minecraft:piston_head", "Piston"); + map.put("minecraft:daylight_detector_inverted", "DLDetector"); + map.put("minecraft:unpowered_comparator", "Comparator"); + map.put("minecraft:powered_comparator", "Comparator"); + map.put("minecraft:wall_banner", "Banner"); + map.put("minecraft:standing_banner", "Banner"); + map.put("minecraft:structure_block", "Structure"); + map.put("minecraft:end_portal", "Airportal"); + map.put("minecraft:end_gateway", "EndGateway"); + map.put("minecraft:shield", "Shield"); + map = DataInspectorBlockEntity.c; + map.put("minecraft:furnace", "minecraft:furnace"); + map.put("minecraft:lit_furnace", "minecraft:furnace"); + map.put("minecraft:chest", "minecraft:chest"); + map.put("minecraft:trapped_chest", "minecraft:chest"); + map.put("minecraft:ender_chest", "minecraft:enderchest"); + map.put("minecraft:jukebox", "minecraft:jukebox"); + map.put("minecraft:dispenser", "minecraft:dispenser"); + map.put("minecraft:dropper", "minecraft:dropper"); + map.put("minecraft:sign", "minecraft:sign"); + map.put("minecraft:mob_spawner", "minecraft:mob_spawner"); + map.put("minecraft:noteblock", "minecraft:noteblock"); + map.put("minecraft:brewing_stand", "minecraft:brewing_stand"); + map.put("minecraft:enhanting_table", "minecraft:enchanting_table"); + map.put("minecraft:command_block", "minecraft:command_block"); + map.put("minecraft:beacon", "minecraft:beacon"); + map.put("minecraft:skull", "minecraft:skull"); + map.put("minecraft:daylight_detector", "minecraft:daylight_detector"); + map.put("minecraft:hopper", "minecraft:hopper"); + map.put("minecraft:banner", "minecraft:banner"); + map.put("minecraft:flower_pot", "minecraft:flower_pot"); + map.put("minecraft:repeating_command_block", "minecraft:command_block"); + map.put("minecraft:chain_command_block", "minecraft:command_block"); + map.put("minecraft:shulker_box", "minecraft:shulker_box"); + map.put("minecraft:white_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:orange_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:magenta_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:light_blue_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:yellow_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:lime_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:pink_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:gray_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:silver_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:cyan_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:purple_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:blue_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:brown_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:green_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:red_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:black_shulker_box", "minecraft:shulker_box"); + map.put("minecraft:bed", "minecraft:bed"); + map.put("minecraft:standing_sign", "minecraft:sign"); + map.put("minecraft:wall_sign", "minecraft:sign"); + map.put("minecraft:piston_head", "minecraft:piston"); + map.put("minecraft:daylight_detector_inverted", "minecraft:daylight_detector"); + map.put("minecraft:unpowered_comparator", "minecraft:comparator"); + map.put("minecraft:powered_comparator", "minecraft:comparator"); + map.put("minecraft:wall_banner", "minecraft:banner"); + map.put("minecraft:standing_banner", "minecraft:banner"); + map.put("minecraft:structure_block", "minecraft:structure_block"); + map.put("minecraft:end_portal", "minecraft:end_portal"); + map.put("minecraft:end_gateway", "minecraft:end_gateway"); + map.put("minecraft:shield", "minecraft:shield"); + } + } + + private static class DataInspectorEntity implements DataInspector { + + private static final Logger a = LogManager.getLogger(PaperweightDataConverters.class); + + DataInspectorEntity() { + } + + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("EntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag"); + String s = cmp.getString("id"); + String s1; + + if ("minecraft:armor_stand".equals(s)) { + s1 = sourceVer < 515 ? "ArmorStand" : "minecraft:armor_stand"; + } else { + if (!"minecraft:spawn_egg".equals(s)) { + return cmp; + } + + s1 = nbttagcompound2.getString("id"); + } + + boolean flag; + + flag = !nbttagcompound2.contains("id", 8); + nbttagcompound2.putString("id", s1); + + convert(LegacyType.ENTITY, nbttagcompound2, sourceVer, targetVer); + if (flag) { + nbttagcompound2.remove("id"); + } + } + + return cmp; + } + } + + + private abstract static class DataInspectorTagged implements DataInspector { + + private final ResourceLocation key; + + DataInspectorTagged(String type) { + this.key = getKey(type); + } + + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (this.key.equals(new ResourceLocation(cmp.getString("id")))) { + cmp = this.inspectChecked(cmp, sourceVer, targetVer); + } + + return cmp; + } + + abstract net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer); + } + + private static class DataInspectorItemList extends DataInspectorTagged { + + private final String[] keys; + + DataInspectorItemList(String oclass, String... astring) { + super(oclass); + this.keys = astring; + } + + net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) { + for (String s : this.keys) { + PaperweightDataConverters.convertItems(nbttagcompound, s, sourceVer, targetVer); + } + + return nbttagcompound; + } + } + + private static class DataInspectorItem extends DataInspectorTagged { + + private final String[] keys; + + DataInspectorItem(String oclass, String... astring) { + super(oclass); + this.keys = astring; + } + + net.minecraft.nbt.CompoundTag inspectChecked(net.minecraft.nbt.CompoundTag nbttagcompound, int sourceVer, int targetVer) { + for (String key : this.keys) { + PaperweightDataConverters.convertItem(nbttagcompound, key, sourceVer, targetVer); + } + + return nbttagcompound; + } + } + + private static class DataConverterMaterialId implements DataConverter { + + private static final String[] materials = new String[2268]; + + DataConverterMaterialId() { + } + + public int getDataVersion() { + return 102; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("id", 99)) { + short short0 = cmp.getShort("id"); + + if (short0 > 0 && short0 < materials.length && materials[short0] != null) { + cmp.putString("id", materials[short0]); + } + } + + return cmp; + } + + static { + materials[1] = "minecraft:stone"; + materials[2] = "minecraft:grass"; + materials[3] = "minecraft:dirt"; + materials[4] = "minecraft:cobblestone"; + materials[5] = "minecraft:planks"; + materials[6] = "minecraft:sapling"; + materials[7] = "minecraft:bedrock"; + materials[8] = "minecraft:flowing_water"; + materials[9] = "minecraft:water"; + materials[10] = "minecraft:flowing_lava"; + materials[11] = "minecraft:lava"; + materials[12] = "minecraft:sand"; + materials[13] = "minecraft:gravel"; + materials[14] = "minecraft:gold_ore"; + materials[15] = "minecraft:iron_ore"; + materials[16] = "minecraft:coal_ore"; + materials[17] = "minecraft:log"; + materials[18] = "minecraft:leaves"; + materials[19] = "minecraft:sponge"; + materials[20] = "minecraft:glass"; + materials[21] = "minecraft:lapis_ore"; + materials[22] = "minecraft:lapis_block"; + materials[23] = "minecraft:dispenser"; + materials[24] = "minecraft:sandstone"; + materials[25] = "minecraft:noteblock"; + materials[27] = "minecraft:golden_rail"; + materials[28] = "minecraft:detector_rail"; + materials[29] = "minecraft:sticky_piston"; + materials[30] = "minecraft:web"; + materials[31] = "minecraft:tallgrass"; + materials[32] = "minecraft:deadbush"; + materials[33] = "minecraft:piston"; + materials[35] = "minecraft:wool"; + materials[37] = "minecraft:yellow_flower"; + materials[38] = "minecraft:red_flower"; + materials[39] = "minecraft:brown_mushroom"; + materials[40] = "minecraft:red_mushroom"; + materials[41] = "minecraft:gold_block"; + materials[42] = "minecraft:iron_block"; + materials[43] = "minecraft:double_stone_slab"; + materials[44] = "minecraft:stone_slab"; + materials[45] = "minecraft:brick_block"; + materials[46] = "minecraft:tnt"; + materials[47] = "minecraft:bookshelf"; + materials[48] = "minecraft:mossy_cobblestone"; + materials[49] = "minecraft:obsidian"; + materials[50] = "minecraft:torch"; + materials[51] = "minecraft:fire"; + materials[52] = "minecraft:mob_spawner"; + materials[53] = "minecraft:oak_stairs"; + materials[54] = "minecraft:chest"; + materials[56] = "minecraft:diamond_ore"; + materials[57] = "minecraft:diamond_block"; + materials[58] = "minecraft:crafting_table"; + materials[60] = "minecraft:farmland"; + materials[61] = "minecraft:furnace"; + materials[62] = "minecraft:lit_furnace"; + materials[65] = "minecraft:ladder"; + materials[66] = "minecraft:rail"; + materials[67] = "minecraft:stone_stairs"; + materials[69] = "minecraft:lever"; + materials[70] = "minecraft:stone_pressure_plate"; + materials[72] = "minecraft:wooden_pressure_plate"; + materials[73] = "minecraft:redstone_ore"; + materials[76] = "minecraft:redstone_torch"; + materials[77] = "minecraft:stone_button"; + materials[78] = "minecraft:snow_layer"; + materials[79] = "minecraft:ice"; + materials[80] = "minecraft:snow"; + materials[81] = "minecraft:cactus"; + materials[82] = "minecraft:clay"; + materials[84] = "minecraft:jukebox"; + materials[85] = "minecraft:fence"; + materials[86] = "minecraft:pumpkin"; + materials[87] = "minecraft:netherrack"; + materials[88] = "minecraft:soul_sand"; + materials[89] = "minecraft:glowstone"; + materials[90] = "minecraft:portal"; + materials[91] = "minecraft:lit_pumpkin"; + materials[95] = "minecraft:stained_glass"; + materials[96] = "minecraft:trapdoor"; + materials[97] = "minecraft:monster_egg"; + materials[98] = "minecraft:stonebrick"; + materials[99] = "minecraft:brown_mushroom_block"; + materials[100] = "minecraft:red_mushroom_block"; + materials[101] = "minecraft:iron_bars"; + materials[102] = "minecraft:glass_pane"; + materials[103] = "minecraft:melon_block"; + materials[106] = "minecraft:vine"; + materials[107] = "minecraft:fence_gate"; + materials[108] = "minecraft:brick_stairs"; + materials[109] = "minecraft:stone_brick_stairs"; + materials[110] = "minecraft:mycelium"; + materials[111] = "minecraft:waterlily"; + materials[112] = "minecraft:nether_brick"; + materials[113] = "minecraft:nether_brick_fence"; + materials[114] = "minecraft:nether_brick_stairs"; + materials[116] = "minecraft:enchanting_table"; + materials[119] = "minecraft:end_portal"; + materials[120] = "minecraft:end_portal_frame"; + materials[121] = "minecraft:end_stone"; + materials[122] = "minecraft:dragon_egg"; + materials[123] = "minecraft:redstone_lamp"; + materials[125] = "minecraft:double_wooden_slab"; + materials[126] = "minecraft:wooden_slab"; + materials[127] = "minecraft:cocoa"; + materials[128] = "minecraft:sandstone_stairs"; + materials[129] = "minecraft:emerald_ore"; + materials[130] = "minecraft:ender_chest"; + materials[131] = "minecraft:tripwire_hook"; + materials[133] = "minecraft:emerald_block"; + materials[134] = "minecraft:spruce_stairs"; + materials[135] = "minecraft:birch_stairs"; + materials[136] = "minecraft:jungle_stairs"; + materials[137] = "minecraft:command_block"; + materials[138] = "minecraft:beacon"; + materials[139] = "minecraft:cobblestone_wall"; + materials[141] = "minecraft:carrots"; + materials[142] = "minecraft:potatoes"; + materials[143] = "minecraft:wooden_button"; + materials[145] = "minecraft:anvil"; + materials[146] = "minecraft:trapped_chest"; + materials[147] = "minecraft:light_weighted_pressure_plate"; + materials[148] = "minecraft:heavy_weighted_pressure_plate"; + materials[151] = "minecraft:daylight_detector"; + materials[152] = "minecraft:redstone_block"; + materials[153] = "minecraft:quartz_ore"; + materials[154] = "minecraft:hopper"; + materials[155] = "minecraft:quartz_block"; + materials[156] = "minecraft:quartz_stairs"; + materials[157] = "minecraft:activator_rail"; + materials[158] = "minecraft:dropper"; + materials[159] = "minecraft:stained_hardened_clay"; + materials[160] = "minecraft:stained_glass_pane"; + materials[161] = "minecraft:leaves2"; + materials[162] = "minecraft:log2"; + materials[163] = "minecraft:acacia_stairs"; + materials[164] = "minecraft:dark_oak_stairs"; + materials[170] = "minecraft:hay_block"; + materials[171] = "minecraft:carpet"; + materials[172] = "minecraft:hardened_clay"; + materials[173] = "minecraft:coal_block"; + materials[174] = "minecraft:packed_ice"; + materials[175] = "minecraft:double_plant"; + materials[256] = "minecraft:iron_shovel"; + materials[257] = "minecraft:iron_pickaxe"; + materials[258] = "minecraft:iron_axe"; + materials[259] = "minecraft:flint_and_steel"; + materials[260] = "minecraft:apple"; + materials[261] = "minecraft:bow"; + materials[262] = "minecraft:arrow"; + materials[263] = "minecraft:coal"; + materials[264] = "minecraft:diamond"; + materials[265] = "minecraft:iron_ingot"; + materials[266] = "minecraft:gold_ingot"; + materials[267] = "minecraft:iron_sword"; + materials[268] = "minecraft:wooden_sword"; + materials[269] = "minecraft:wooden_shovel"; + materials[270] = "minecraft:wooden_pickaxe"; + materials[271] = "minecraft:wooden_axe"; + materials[272] = "minecraft:stone_sword"; + materials[273] = "minecraft:stone_shovel"; + materials[274] = "minecraft:stone_pickaxe"; + materials[275] = "minecraft:stone_axe"; + materials[276] = "minecraft:diamond_sword"; + materials[277] = "minecraft:diamond_shovel"; + materials[278] = "minecraft:diamond_pickaxe"; + materials[279] = "minecraft:diamond_axe"; + materials[280] = "minecraft:stick"; + materials[281] = "minecraft:bowl"; + materials[282] = "minecraft:mushroom_stew"; + materials[283] = "minecraft:golden_sword"; + materials[284] = "minecraft:golden_shovel"; + materials[285] = "minecraft:golden_pickaxe"; + materials[286] = "minecraft:golden_axe"; + materials[287] = "minecraft:string"; + materials[288] = "minecraft:feather"; + materials[289] = "minecraft:gunpowder"; + materials[290] = "minecraft:wooden_hoe"; + materials[291] = "minecraft:stone_hoe"; + materials[292] = "minecraft:iron_hoe"; + materials[293] = "minecraft:diamond_hoe"; + materials[294] = "minecraft:golden_hoe"; + materials[295] = "minecraft:wheat_seeds"; + materials[296] = "minecraft:wheat"; + materials[297] = "minecraft:bread"; + materials[298] = "minecraft:leather_helmet"; + materials[299] = "minecraft:leather_chestplate"; + materials[300] = "minecraft:leather_leggings"; + materials[301] = "minecraft:leather_boots"; + materials[302] = "minecraft:chainmail_helmet"; + materials[303] = "minecraft:chainmail_chestplate"; + materials[304] = "minecraft:chainmail_leggings"; + materials[305] = "minecraft:chainmail_boots"; + materials[306] = "minecraft:iron_helmet"; + materials[307] = "minecraft:iron_chestplate"; + materials[308] = "minecraft:iron_leggings"; + materials[309] = "minecraft:iron_boots"; + materials[310] = "minecraft:diamond_helmet"; + materials[311] = "minecraft:diamond_chestplate"; + materials[312] = "minecraft:diamond_leggings"; + materials[313] = "minecraft:diamond_boots"; + materials[314] = "minecraft:golden_helmet"; + materials[315] = "minecraft:golden_chestplate"; + materials[316] = "minecraft:golden_leggings"; + materials[317] = "minecraft:golden_boots"; + materials[318] = "minecraft:flint"; + materials[319] = "minecraft:porkchop"; + materials[320] = "minecraft:cooked_porkchop"; + materials[321] = "minecraft:painting"; + materials[322] = "minecraft:golden_apple"; + materials[323] = "minecraft:sign"; + materials[324] = "minecraft:wooden_door"; + materials[325] = "minecraft:bucket"; + materials[326] = "minecraft:water_bucket"; + materials[327] = "minecraft:lava_bucket"; + materials[328] = "minecraft:minecart"; + materials[329] = "minecraft:saddle"; + materials[330] = "minecraft:iron_door"; + materials[331] = "minecraft:redstone"; + materials[332] = "minecraft:snowball"; + materials[333] = "minecraft:boat"; + materials[334] = "minecraft:leather"; + materials[335] = "minecraft:milk_bucket"; + materials[336] = "minecraft:brick"; + materials[337] = "minecraft:clay_ball"; + materials[338] = "minecraft:reeds"; + materials[339] = "minecraft:paper"; + materials[340] = "minecraft:book"; + materials[341] = "minecraft:slime_ball"; + materials[342] = "minecraft:chest_minecart"; + materials[343] = "minecraft:furnace_minecart"; + materials[344] = "minecraft:egg"; + materials[345] = "minecraft:compass"; + materials[346] = "minecraft:fishing_rod"; + materials[347] = "minecraft:clock"; + materials[348] = "minecraft:glowstone_dust"; + materials[349] = "minecraft:fish"; + materials[350] = "minecraft:cooked_fish"; // Paper - cooked_fished -> cooked_fish + materials[351] = "minecraft:dye"; + materials[352] = "minecraft:bone"; + materials[353] = "minecraft:sugar"; + materials[354] = "minecraft:cake"; + materials[355] = "minecraft:bed"; + materials[356] = "minecraft:repeater"; + materials[357] = "minecraft:cookie"; + materials[358] = "minecraft:filled_map"; + materials[359] = "minecraft:shears"; + materials[360] = "minecraft:melon"; + materials[361] = "minecraft:pumpkin_seeds"; + materials[362] = "minecraft:melon_seeds"; + materials[363] = "minecraft:beef"; + materials[364] = "minecraft:cooked_beef"; + materials[365] = "minecraft:chicken"; + materials[366] = "minecraft:cooked_chicken"; + materials[367] = "minecraft:rotten_flesh"; + materials[368] = "minecraft:ender_pearl"; + materials[369] = "minecraft:blaze_rod"; + materials[370] = "minecraft:ghast_tear"; + materials[371] = "minecraft:gold_nugget"; + materials[372] = "minecraft:nether_wart"; + materials[373] = "minecraft:potion"; + materials[374] = "minecraft:glass_bottle"; + materials[375] = "minecraft:spider_eye"; + materials[376] = "minecraft:fermented_spider_eye"; + materials[377] = "minecraft:blaze_powder"; + materials[378] = "minecraft:magma_cream"; + materials[379] = "minecraft:brewing_stand"; + materials[380] = "minecraft:cauldron"; + materials[381] = "minecraft:ender_eye"; + materials[382] = "minecraft:speckled_melon"; + materials[383] = "minecraft:spawn_egg"; + materials[384] = "minecraft:experience_bottle"; + materials[385] = "minecraft:fire_charge"; + materials[386] = "minecraft:writable_book"; + materials[387] = "minecraft:written_book"; + materials[388] = "minecraft:emerald"; + materials[389] = "minecraft:item_frame"; + materials[390] = "minecraft:flower_pot"; + materials[391] = "minecraft:carrot"; + materials[392] = "minecraft:potato"; + materials[393] = "minecraft:baked_potato"; + materials[394] = "minecraft:poisonous_potato"; + materials[395] = "minecraft:map"; + materials[396] = "minecraft:golden_carrot"; + materials[397] = "minecraft:skull"; + materials[398] = "minecraft:carrot_on_a_stick"; + materials[399] = "minecraft:nether_star"; + materials[400] = "minecraft:pumpkin_pie"; + materials[401] = "minecraft:fireworks"; + materials[402] = "minecraft:firework_charge"; + materials[403] = "minecraft:enchanted_book"; + materials[404] = "minecraft:comparator"; + materials[405] = "minecraft:netherbrick"; + materials[406] = "minecraft:quartz"; + materials[407] = "minecraft:tnt_minecart"; + materials[408] = "minecraft:hopper_minecart"; + materials[417] = "minecraft:iron_horse_armor"; + materials[418] = "minecraft:golden_horse_armor"; + materials[419] = "minecraft:diamond_horse_armor"; + materials[420] = "minecraft:lead"; + materials[421] = "minecraft:name_tag"; + materials[422] = "minecraft:command_block_minecart"; + materials[2256] = "minecraft:record_13"; + materials[2257] = "minecraft:record_cat"; + materials[2258] = "minecraft:record_blocks"; + materials[2259] = "minecraft:record_chirp"; + materials[2260] = "minecraft:record_far"; + materials[2261] = "minecraft:record_mall"; + materials[2262] = "minecraft:record_mellohi"; + materials[2263] = "minecraft:record_stal"; + materials[2264] = "minecraft:record_strad"; + materials[2265] = "minecraft:record_ward"; + materials[2266] = "minecraft:record_11"; + materials[2267] = "minecraft:record_wait"; + // Paper start + materials[409] = "minecraft:prismarine_shard"; + materials[410] = "minecraft:prismarine_crystals"; + materials[411] = "minecraft:rabbit"; + materials[412] = "minecraft:cooked_rabbit"; + materials[413] = "minecraft:rabbit_stew"; + materials[414] = "minecraft:rabbit_foot"; + materials[415] = "minecraft:rabbit_hide"; + materials[416] = "minecraft:armor_stand"; + materials[423] = "minecraft:mutton"; + materials[424] = "minecraft:cooked_mutton"; + materials[425] = "minecraft:banner"; + materials[426] = "minecraft:end_crystal"; + materials[427] = "minecraft:spruce_door"; + materials[428] = "minecraft:birch_door"; + materials[429] = "minecraft:jungle_door"; + materials[430] = "minecraft:acacia_door"; + materials[431] = "minecraft:dark_oak_door"; + materials[432] = "minecraft:chorus_fruit"; + materials[433] = "minecraft:chorus_fruit_popped"; + materials[434] = "minecraft:beetroot"; + materials[435] = "minecraft:beetroot_seeds"; + materials[436] = "minecraft:beetroot_soup"; + materials[437] = "minecraft:dragon_breath"; + materials[438] = "minecraft:splash_potion"; + materials[439] = "minecraft:spectral_arrow"; + materials[440] = "minecraft:tipped_arrow"; + materials[441] = "minecraft:lingering_potion"; + materials[442] = "minecraft:shield"; + materials[443] = "minecraft:elytra"; + materials[444] = "minecraft:spruce_boat"; + materials[445] = "minecraft:birch_boat"; + materials[446] = "minecraft:jungle_boat"; + materials[447] = "minecraft:acacia_boat"; + materials[448] = "minecraft:dark_oak_boat"; + materials[449] = "minecraft:totem_of_undying"; + materials[450] = "minecraft:shulker_shell"; + materials[452] = "minecraft:iron_nugget"; + materials[453] = "minecraft:knowledge_book"; + // Paper end + } + } + + private static class DataConverterArmorStand implements DataConverter { + + DataConverterArmorStand() { + } + + public int getDataVersion() { + return 147; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("ArmorStand".equals(cmp.getString("id")) && cmp.getBoolean("Silent") && !cmp.getBoolean("Marker")) { + cmp.remove("Silent"); + } + + return cmp; + } + } + + private static class DataConverterBanner implements DataConverter { + + DataConverterBanner() { + } + + public int getDataVersion() { + return 804; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:banner".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + + if (nbttagcompound2.contains("Base", 99)) { + cmp.putShort("Damage", (short) (nbttagcompound2.getShort("Base") & 15)); + if (nbttagcompound1.contains("display", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound1.getCompound("display"); + + if (nbttagcompound3.contains("Lore", 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound3.getList("Lore", 8); + + if (nbttaglist.size() == 1 && "(+NBT)".equals(nbttaglist.getString(0))) { + return cmp; + } + } + } + + nbttagcompound2.remove("Base"); + if (nbttagcompound2.isEmpty()) { + nbttagcompound1.remove("BlockEntityTag"); + } + + if (nbttagcompound1.isEmpty()) { + cmp.remove("tag"); + } + } + } + } + + return cmp; + } + } + + private static class DataConverterPotionId implements DataConverter { + + private static final String[] potions = new String[128]; + + DataConverterPotionId() { + } + + public int getDataVersion() { + return 102; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:potion".equals(cmp.getString("id"))) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + short short0 = cmp.getShort("Damage"); + + if (!nbttagcompound1.contains("Potion", 8)) { + String s = DataConverterPotionId.potions[short0 & 127]; + + nbttagcompound1.putString("Potion", s == null ? "minecraft:water" : s); + cmp.put("tag", nbttagcompound1); + if ((short0 & 16384) == 16384) { + cmp.putString("id", "minecraft:splash_potion"); + } + } + + if (short0 != 0) { + cmp.putShort("Damage", (short) 0); + } + } + + return cmp; + } + + static { + DataConverterPotionId.potions[0] = "minecraft:water"; + DataConverterPotionId.potions[1] = "minecraft:regeneration"; + DataConverterPotionId.potions[2] = "minecraft:swiftness"; + DataConverterPotionId.potions[3] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[4] = "minecraft:poison"; + DataConverterPotionId.potions[5] = "minecraft:healing"; + DataConverterPotionId.potions[6] = "minecraft:night_vision"; + DataConverterPotionId.potions[7] = null; + DataConverterPotionId.potions[8] = "minecraft:weakness"; + DataConverterPotionId.potions[9] = "minecraft:strength"; + DataConverterPotionId.potions[10] = "minecraft:slowness"; + DataConverterPotionId.potions[11] = "minecraft:leaping"; + DataConverterPotionId.potions[12] = "minecraft:harming"; + DataConverterPotionId.potions[13] = "minecraft:water_breathing"; + DataConverterPotionId.potions[14] = "minecraft:invisibility"; + DataConverterPotionId.potions[15] = null; + DataConverterPotionId.potions[16] = "minecraft:awkward"; + DataConverterPotionId.potions[17] = "minecraft:regeneration"; + DataConverterPotionId.potions[18] = "minecraft:swiftness"; + DataConverterPotionId.potions[19] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[20] = "minecraft:poison"; + DataConverterPotionId.potions[21] = "minecraft:healing"; + DataConverterPotionId.potions[22] = "minecraft:night_vision"; + DataConverterPotionId.potions[23] = null; + DataConverterPotionId.potions[24] = "minecraft:weakness"; + DataConverterPotionId.potions[25] = "minecraft:strength"; + DataConverterPotionId.potions[26] = "minecraft:slowness"; + DataConverterPotionId.potions[27] = "minecraft:leaping"; + DataConverterPotionId.potions[28] = "minecraft:harming"; + DataConverterPotionId.potions[29] = "minecraft:water_breathing"; + DataConverterPotionId.potions[30] = "minecraft:invisibility"; + DataConverterPotionId.potions[31] = null; + DataConverterPotionId.potions[32] = "minecraft:thick"; + DataConverterPotionId.potions[33] = "minecraft:strong_regeneration"; + DataConverterPotionId.potions[34] = "minecraft:strong_swiftness"; + DataConverterPotionId.potions[35] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[36] = "minecraft:strong_poison"; + DataConverterPotionId.potions[37] = "minecraft:strong_healing"; + DataConverterPotionId.potions[38] = "minecraft:night_vision"; + DataConverterPotionId.potions[39] = null; + DataConverterPotionId.potions[40] = "minecraft:weakness"; + DataConverterPotionId.potions[41] = "minecraft:strong_strength"; + DataConverterPotionId.potions[42] = "minecraft:slowness"; + DataConverterPotionId.potions[43] = "minecraft:strong_leaping"; + DataConverterPotionId.potions[44] = "minecraft:strong_harming"; + DataConverterPotionId.potions[45] = "minecraft:water_breathing"; + DataConverterPotionId.potions[46] = "minecraft:invisibility"; + DataConverterPotionId.potions[47] = null; + DataConverterPotionId.potions[48] = null; + DataConverterPotionId.potions[49] = "minecraft:strong_regeneration"; + DataConverterPotionId.potions[50] = "minecraft:strong_swiftness"; + DataConverterPotionId.potions[51] = "minecraft:fire_resistance"; + DataConverterPotionId.potions[52] = "minecraft:strong_poison"; + DataConverterPotionId.potions[53] = "minecraft:strong_healing"; + DataConverterPotionId.potions[54] = "minecraft:night_vision"; + DataConverterPotionId.potions[55] = null; + DataConverterPotionId.potions[56] = "minecraft:weakness"; + DataConverterPotionId.potions[57] = "minecraft:strong_strength"; + DataConverterPotionId.potions[58] = "minecraft:slowness"; + DataConverterPotionId.potions[59] = "minecraft:strong_leaping"; + DataConverterPotionId.potions[60] = "minecraft:strong_harming"; + DataConverterPotionId.potions[61] = "minecraft:water_breathing"; + DataConverterPotionId.potions[62] = "minecraft:invisibility"; + DataConverterPotionId.potions[63] = null; + DataConverterPotionId.potions[64] = "minecraft:mundane"; + DataConverterPotionId.potions[65] = "minecraft:long_regeneration"; + DataConverterPotionId.potions[66] = "minecraft:long_swiftness"; + DataConverterPotionId.potions[67] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[68] = "minecraft:long_poison"; + DataConverterPotionId.potions[69] = "minecraft:healing"; + DataConverterPotionId.potions[70] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[71] = null; + DataConverterPotionId.potions[72] = "minecraft:long_weakness"; + DataConverterPotionId.potions[73] = "minecraft:long_strength"; + DataConverterPotionId.potions[74] = "minecraft:long_slowness"; + DataConverterPotionId.potions[75] = "minecraft:long_leaping"; + DataConverterPotionId.potions[76] = "minecraft:harming"; + DataConverterPotionId.potions[77] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[78] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[79] = null; + DataConverterPotionId.potions[80] = "minecraft:awkward"; + DataConverterPotionId.potions[81] = "minecraft:long_regeneration"; + DataConverterPotionId.potions[82] = "minecraft:long_swiftness"; + DataConverterPotionId.potions[83] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[84] = "minecraft:long_poison"; + DataConverterPotionId.potions[85] = "minecraft:healing"; + DataConverterPotionId.potions[86] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[87] = null; + DataConverterPotionId.potions[88] = "minecraft:long_weakness"; + DataConverterPotionId.potions[89] = "minecraft:long_strength"; + DataConverterPotionId.potions[90] = "minecraft:long_slowness"; + DataConverterPotionId.potions[91] = "minecraft:long_leaping"; + DataConverterPotionId.potions[92] = "minecraft:harming"; + DataConverterPotionId.potions[93] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[94] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[95] = null; + DataConverterPotionId.potions[96] = "minecraft:thick"; + DataConverterPotionId.potions[97] = "minecraft:regeneration"; + DataConverterPotionId.potions[98] = "minecraft:swiftness"; + DataConverterPotionId.potions[99] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[100] = "minecraft:poison"; + DataConverterPotionId.potions[101] = "minecraft:strong_healing"; + DataConverterPotionId.potions[102] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[103] = null; + DataConverterPotionId.potions[104] = "minecraft:long_weakness"; + DataConverterPotionId.potions[105] = "minecraft:strength"; + DataConverterPotionId.potions[106] = "minecraft:long_slowness"; + DataConverterPotionId.potions[107] = "minecraft:leaping"; + DataConverterPotionId.potions[108] = "minecraft:strong_harming"; + DataConverterPotionId.potions[109] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[110] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[111] = null; + DataConverterPotionId.potions[112] = null; + DataConverterPotionId.potions[113] = "minecraft:regeneration"; + DataConverterPotionId.potions[114] = "minecraft:swiftness"; + DataConverterPotionId.potions[115] = "minecraft:long_fire_resistance"; + DataConverterPotionId.potions[116] = "minecraft:poison"; + DataConverterPotionId.potions[117] = "minecraft:strong_healing"; + DataConverterPotionId.potions[118] = "minecraft:long_night_vision"; + DataConverterPotionId.potions[119] = null; + DataConverterPotionId.potions[120] = "minecraft:long_weakness"; + DataConverterPotionId.potions[121] = "minecraft:strength"; + DataConverterPotionId.potions[122] = "minecraft:long_slowness"; + DataConverterPotionId.potions[123] = "minecraft:leaping"; + DataConverterPotionId.potions[124] = "minecraft:strong_harming"; + DataConverterPotionId.potions[125] = "minecraft:long_water_breathing"; + DataConverterPotionId.potions[126] = "minecraft:long_invisibility"; + DataConverterPotionId.potions[127] = null; + } + } + + private static class DataConverterSpawnEgg implements DataConverter { + + private static final String[] eggs = new String[256]; + + DataConverterSpawnEgg() { + } + + public int getDataVersion() { + return 105; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:spawn_egg".equals(cmp.getString("id"))) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("EntityTag"); + short short0 = cmp.getShort("Damage"); + + if (!nbttagcompound2.contains("id", 8)) { + String s = DataConverterSpawnEgg.eggs[short0 & 255]; + + if (s != null) { + nbttagcompound2.putString("id", s); + nbttagcompound1.put("EntityTag", nbttagcompound2); + cmp.put("tag", nbttagcompound1); + } + } + + if (short0 != 0) { + cmp.putShort("Damage", (short) 0); + } + } + + return cmp; + } + + static { + + DataConverterSpawnEgg.eggs[1] = "Item"; + DataConverterSpawnEgg.eggs[2] = "XPOrb"; + DataConverterSpawnEgg.eggs[7] = "ThrownEgg"; + DataConverterSpawnEgg.eggs[8] = "LeashKnot"; + DataConverterSpawnEgg.eggs[9] = "Painting"; + DataConverterSpawnEgg.eggs[10] = "Arrow"; + DataConverterSpawnEgg.eggs[11] = "Snowball"; + DataConverterSpawnEgg.eggs[12] = "Fireball"; + DataConverterSpawnEgg.eggs[13] = "SmallFireball"; + DataConverterSpawnEgg.eggs[14] = "ThrownEnderpearl"; + DataConverterSpawnEgg.eggs[15] = "EyeOfEnderSignal"; + DataConverterSpawnEgg.eggs[16] = "ThrownPotion"; + DataConverterSpawnEgg.eggs[17] = "ThrownExpBottle"; + DataConverterSpawnEgg.eggs[18] = "ItemFrame"; + DataConverterSpawnEgg.eggs[19] = "WitherSkull"; + DataConverterSpawnEgg.eggs[20] = "PrimedTnt"; + DataConverterSpawnEgg.eggs[21] = "FallingSand"; + DataConverterSpawnEgg.eggs[22] = "FireworksRocketEntity"; + DataConverterSpawnEgg.eggs[23] = "TippedArrow"; + DataConverterSpawnEgg.eggs[24] = "SpectralArrow"; + DataConverterSpawnEgg.eggs[25] = "ShulkerBullet"; + DataConverterSpawnEgg.eggs[26] = "DragonFireball"; + DataConverterSpawnEgg.eggs[30] = "ArmorStand"; + DataConverterSpawnEgg.eggs[41] = "Boat"; + DataConverterSpawnEgg.eggs[42] = "MinecartRideable"; + DataConverterSpawnEgg.eggs[43] = "MinecartChest"; + DataConverterSpawnEgg.eggs[44] = "MinecartFurnace"; + DataConverterSpawnEgg.eggs[45] = "MinecartTNT"; + DataConverterSpawnEgg.eggs[46] = "MinecartHopper"; + DataConverterSpawnEgg.eggs[47] = "MinecartSpawner"; + DataConverterSpawnEgg.eggs[40] = "MinecartCommandBlock"; + DataConverterSpawnEgg.eggs[48] = "Mob"; + DataConverterSpawnEgg.eggs[49] = "Monster"; + DataConverterSpawnEgg.eggs[50] = "Creeper"; + DataConverterSpawnEgg.eggs[51] = "Skeleton"; + DataConverterSpawnEgg.eggs[52] = "Spider"; + DataConverterSpawnEgg.eggs[53] = "Giant"; + DataConverterSpawnEgg.eggs[54] = "Zombie"; + DataConverterSpawnEgg.eggs[55] = "Slime"; + DataConverterSpawnEgg.eggs[56] = "Ghast"; + DataConverterSpawnEgg.eggs[57] = "PigZombie"; + DataConverterSpawnEgg.eggs[58] = "Enderman"; + DataConverterSpawnEgg.eggs[59] = "CaveSpider"; + DataConverterSpawnEgg.eggs[60] = "Silverfish"; + DataConverterSpawnEgg.eggs[61] = "Blaze"; + DataConverterSpawnEgg.eggs[62] = "LavaSlime"; + DataConverterSpawnEgg.eggs[63] = "EnderDragon"; + DataConverterSpawnEgg.eggs[64] = "WitherBoss"; + DataConverterSpawnEgg.eggs[65] = "Bat"; + DataConverterSpawnEgg.eggs[66] = "Witch"; + DataConverterSpawnEgg.eggs[67] = "Endermite"; + DataConverterSpawnEgg.eggs[68] = "Guardian"; + DataConverterSpawnEgg.eggs[69] = "Shulker"; + DataConverterSpawnEgg.eggs[90] = "Pig"; + DataConverterSpawnEgg.eggs[91] = "Sheep"; + DataConverterSpawnEgg.eggs[92] = "Cow"; + DataConverterSpawnEgg.eggs[93] = "Chicken"; + DataConverterSpawnEgg.eggs[94] = "Squid"; + DataConverterSpawnEgg.eggs[95] = "Wolf"; + DataConverterSpawnEgg.eggs[96] = "MushroomCow"; + DataConverterSpawnEgg.eggs[97] = "SnowMan"; + DataConverterSpawnEgg.eggs[98] = "Ozelot"; + DataConverterSpawnEgg.eggs[99] = "VillagerGolem"; + DataConverterSpawnEgg.eggs[100] = "EntityHorse"; + DataConverterSpawnEgg.eggs[101] = "Rabbit"; + DataConverterSpawnEgg.eggs[120] = "Villager"; + DataConverterSpawnEgg.eggs[200] = "EnderCrystal"; + } + } + + private static class DataConverterMinecart implements DataConverter { + + private static final List a = Lists.newArrayList("MinecartRideable", "MinecartChest", "MinecartFurnace", "MinecartTNT", "MinecartSpawner", "MinecartHopper", "MinecartCommandBlock"); + + DataConverterMinecart() { + } + + public int getDataVersion() { + return 106; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Minecart".equals(cmp.getString("id"))) { + String s = "MinecartRideable"; + int i = cmp.getInt("Type"); + + if (i > 0 && i < DataConverterMinecart.a.size()) { + s = DataConverterMinecart.a.get(i); + } + + cmp.putString("id", s); + cmp.remove("Type"); + } + + return cmp; + } + } + + private static class DataConverterMobSpawner implements DataConverter { + + DataConverterMobSpawner() { + } + + public int getDataVersion() { + return 107; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (!"MobSpawner".equals(cmp.getString("id"))) { + return cmp; + } else { + if (cmp.contains("EntityId", 8)) { + String s = cmp.getString("EntityId"); + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("SpawnData"); + + nbttagcompound1.putString("id", s.isEmpty() ? "Pig" : s); + cmp.put("SpawnData", nbttagcompound1); + cmp.remove("EntityId"); + } + + if (cmp.contains("SpawnPotentials", 9)) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(i); + + if (nbttagcompound2.contains("Type", 8)) { + net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound2.getCompound("Properties"); + + nbttagcompound3.putString("id", nbttagcompound2.getString("Type")); + nbttagcompound2.put("Entity", nbttagcompound3); + nbttagcompound2.remove("Type"); + nbttagcompound2.remove("Properties"); + } + } + } + + return cmp; + } + } + } + + private static class DataConverterUUID implements DataConverter { + + DataConverterUUID() { + } + + public int getDataVersion() { + return 108; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("UUID", 8)) { + cmp.putUUID("UUID", UUID.fromString(cmp.getString("UUID"))); + } + + return cmp; + } + } + + private static class DataConverterHealth implements DataConverter { + + private static final Set a = Sets.newHashSet("ArmorStand", "Bat", "Blaze", "CaveSpider", "Chicken", "Cow", "Creeper", "EnderDragon", "Enderman", "Endermite", "EntityHorse", "Ghast", "Giant", "Guardian", "LavaSlime", "MushroomCow", "Ozelot", "Pig", "PigZombie", "Rabbit", "Sheep", "Shulker", "Silverfish", "Skeleton", "Slime", "SnowMan", "Spider", "Squid", "Villager", "VillagerGolem", "Witch", "WitherBoss", "Wolf", "Zombie"); + + DataConverterHealth() { + } + + public int getDataVersion() { + return 109; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (DataConverterHealth.a.contains(cmp.getString("id"))) { + float f; + + if (cmp.contains("HealF", 99)) { + f = cmp.getFloat("HealF"); + cmp.remove("HealF"); + } else { + if (!cmp.contains("Health", 99)) { + return cmp; + } + + f = cmp.getFloat("Health"); + } + + cmp.putFloat("Health", f); + } + + return cmp; + } + } + + private static class DataConverterSaddle implements DataConverter { + + DataConverterSaddle() { + } + + public int getDataVersion() { + return 110; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("EntityHorse".equals(cmp.getString("id")) && !cmp.contains("SaddleItem", 10) && cmp.getBoolean("Saddle")) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = new net.minecraft.nbt.CompoundTag(); + + nbttagcompound1.putString("id", "minecraft:saddle"); + nbttagcompound1.putByte("Count", (byte) 1); + nbttagcompound1.putShort("Damage", (short) 0); + cmp.put("SaddleItem", nbttagcompound1); + cmp.remove("Saddle"); + } + + return cmp; + } + } + + private static class DataConverterHanging implements DataConverter { + + DataConverterHanging() { + } + + public int getDataVersion() { + return 111; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = cmp.getString("id"); + boolean flag = "Painting".equals(s); + boolean flag1 = "ItemFrame".equals(s); + + if ((flag || flag1) && !cmp.contains("Facing", 99)) { + Direction enumdirection; + + if (cmp.contains("Direction", 99)) { + enumdirection = Direction.from2DDataValue(cmp.getByte("Direction")); + cmp.putInt("TileX", cmp.getInt("TileX") + enumdirection.getStepX()); + cmp.putInt("TileY", cmp.getInt("TileY") + enumdirection.getStepY()); + cmp.putInt("TileZ", cmp.getInt("TileZ") + enumdirection.getStepZ()); + cmp.remove("Direction"); + if (flag1 && cmp.contains("ItemRotation", 99)) { + cmp.putByte("ItemRotation", (byte) (cmp.getByte("ItemRotation") * 2)); + } + } else { + enumdirection = Direction.from2DDataValue(cmp.getByte("Dir")); + cmp.remove("Dir"); + } + + cmp.putByte("Facing", (byte) enumdirection.get2DDataValue()); + } + + return cmp; + } + } + + private static class DataConverterDropChances implements DataConverter { + + DataConverterDropChances() { + } + + public int getDataVersion() { + return 113; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + net.minecraft.nbt.ListTag nbttaglist; + + if (cmp.contains("HandDropChances", 9)) { + nbttaglist = cmp.getList("HandDropChances", 5); + if (nbttaglist.size() == 2 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F) { + cmp.remove("HandDropChances"); + } + } + + if (cmp.contains("ArmorDropChances", 9)) { + nbttaglist = cmp.getList("ArmorDropChances", 5); + if (nbttaglist.size() == 4 && nbttaglist.getFloat(0) == 0.0F && nbttaglist.getFloat(1) == 0.0F && nbttaglist.getFloat(2) == 0.0F && nbttaglist.getFloat(3) == 0.0F) { + cmp.remove("ArmorDropChances"); + } + } + + return cmp; + } + } + + private static class DataConverterRiding implements DataConverter { + + DataConverterRiding() { + } + + public int getDataVersion() { + return 135; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + while (cmp.contains("Riding", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = this.b(cmp); + + this.convert(cmp, nbttagcompound1); + cmp = nbttagcompound1; + } + + return cmp; + } + + protected void convert(net.minecraft.nbt.CompoundTag nbttagcompound, net.minecraft.nbt.CompoundTag nbttagcompound1) { + net.minecraft.nbt.ListTag nbttaglist = new net.minecraft.nbt.ListTag(); + + nbttaglist.add(nbttagcompound); + nbttagcompound1.put("Passengers", nbttaglist); + } + + protected net.minecraft.nbt.CompoundTag b(net.minecraft.nbt.CompoundTag nbttagcompound) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttagcompound.getCompound("Riding"); + + nbttagcompound.remove("Riding"); + return nbttagcompound1; + } + } + + private static class DataConverterBook implements DataConverter { + + DataConverterBook() { + } + + public int getDataVersion() { + return 165; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:written_book".equals(cmp.getString("id"))) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("pages", 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("pages", 8); + + for (int i = 0; i < nbttaglist.size(); ++i) { + String s = nbttaglist.getString(i); + Component object = null; + + if (!"null".equals(s) && !StringUtil.isNullOrEmpty(s)) { + if ((s.charAt(0) != 34 || s.charAt(s.length() - 1) != 34) && (s.charAt(0) != 123 || s.charAt(s.length() - 1) != 125)) { + object = new TextComponent(s); + } else { + try { + object = GsonHelper.fromJson(DataConverterSignText.a, s, Component.class, true); + if (object == null) { + object = new TextComponent(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = Component.Serializer.fromJson(s); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = Component.Serializer.fromJsonLenient(s); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = new TextComponent(s); + } + } + } else { + object = new TextComponent(""); + } + + nbttaglist.set(i, net.minecraft.nbt.StringTag.valueOf(Component.Serializer.toJson(object))); + } + + nbttagcompound1.put("pages", nbttaglist); + } + } + + return cmp; + } + } + + private static class DataConverterCookedFish implements DataConverter { + + private static final ResourceLocation a = new ResourceLocation("cooked_fished"); + + DataConverterCookedFish() { + } + + public int getDataVersion() { + return 502; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("id", 8) && DataConverterCookedFish.a.equals(new ResourceLocation(cmp.getString("id")))) { + cmp.putString("id", "minecraft:cooked_fish"); + } + + return cmp; + } + } + + private static class DataConverterZombie implements DataConverter { + + private static final Random a = new Random(); + + DataConverterZombie() { + } + + public int getDataVersion() { + return 502; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Zombie".equals(cmp.getString("id")) && cmp.getBoolean("IsVillager")) { + if (!cmp.contains("ZombieType", 99)) { + int i = -1; + + if (cmp.contains("VillagerProfession", 99)) { + try { + i = this.convert(cmp.getInt("VillagerProfession")); + } catch (RuntimeException runtimeexception) { + ; + } + } + + if (i == -1) { + i = this.convert(DataConverterZombie.a.nextInt(6)); + } + + cmp.putInt("ZombieType", i); + } + + cmp.remove("IsVillager"); + } + + return cmp; + } + + private int convert(int i) { + return i >= 0 && i < 6 ? i : -1; + } + } + + private static class DataConverterVBO implements DataConverter { + + DataConverterVBO() { + } + + public int getDataVersion() { + return 505; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + cmp.putString("useVbo", "true"); + return cmp; + } + } + + private static class DataConverterGuardian implements DataConverter { + + DataConverterGuardian() { + } + + public int getDataVersion() { + return 700; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Guardian".equals(cmp.getString("id"))) { + if (cmp.getBoolean("Elder")) { + cmp.putString("id", "ElderGuardian"); + } + + cmp.remove("Elder"); + } + + return cmp; + } + } + + private static class DataConverterSkeleton implements DataConverter { + + DataConverterSkeleton() { + } + + public int getDataVersion() { + return 701; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = cmp.getString("id"); + + if ("Skeleton".equals(s)) { + int i = cmp.getInt("SkeletonType"); + + if (i == 1) { + cmp.putString("id", "WitherSkeleton"); + } else if (i == 2) { + cmp.putString("id", "Stray"); + } + + cmp.remove("SkeletonType"); + } + + return cmp; + } + } + + private static class DataConverterZombieType implements DataConverter { + + DataConverterZombieType() { + } + + public int getDataVersion() { + return 702; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Zombie".equals(cmp.getString("id"))) { + int i = cmp.getInt("ZombieType"); + + switch (i) { + case 0: + default: + break; + + case 1: + case 2: + case 3: + case 4: + case 5: + cmp.putString("id", "ZombieVillager"); + cmp.putInt("Profession", i - 1); + break; + + case 6: + cmp.putString("id", "Husk"); + } + + cmp.remove("ZombieType"); + } + + return cmp; + } + } + + private static class DataConverterHorse implements DataConverter { + + DataConverterHorse() { + } + + public int getDataVersion() { + return 703; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("EntityHorse".equals(cmp.getString("id"))) { + int i = cmp.getInt("Type"); + + switch (i) { + case 0: + default: + cmp.putString("id", "Horse"); + break; + + case 1: + cmp.putString("id", "Donkey"); + break; + + case 2: + cmp.putString("id", "Mule"); + break; + + case 3: + cmp.putString("id", "ZombieHorse"); + break; + + case 4: + cmp.putString("id", "SkeletonHorse"); + } + + cmp.remove("Type"); + } + + return cmp; + } + } + + private static class DataConverterTileEntity implements DataConverter { + + private static final Map a = Maps.newHashMap(); + + DataConverterTileEntity() { + } + + public int getDataVersion() { + return 704; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = DataConverterTileEntity.a.get(cmp.getString("id")); + + if (s != null) { + cmp.putString("id", s); + } + + return cmp; + } + + static { + DataConverterTileEntity.a.put("Airportal", "minecraft:end_portal"); + DataConverterTileEntity.a.put("Banner", "minecraft:banner"); + DataConverterTileEntity.a.put("Beacon", "minecraft:beacon"); + DataConverterTileEntity.a.put("Cauldron", "minecraft:brewing_stand"); + DataConverterTileEntity.a.put("Chest", "minecraft:chest"); + DataConverterTileEntity.a.put("Comparator", "minecraft:comparator"); + DataConverterTileEntity.a.put("Control", "minecraft:command_block"); + DataConverterTileEntity.a.put("DLDetector", "minecraft:daylight_detector"); + DataConverterTileEntity.a.put("Dropper", "minecraft:dropper"); + DataConverterTileEntity.a.put("EnchantTable", "minecraft:enchanting_table"); + DataConverterTileEntity.a.put("EndGateway", "minecraft:end_gateway"); + DataConverterTileEntity.a.put("EnderChest", "minecraft:ender_chest"); + DataConverterTileEntity.a.put("FlowerPot", "minecraft:flower_pot"); + DataConverterTileEntity.a.put("Furnace", "minecraft:furnace"); + DataConverterTileEntity.a.put("Hopper", "minecraft:hopper"); + DataConverterTileEntity.a.put("MobSpawner", "minecraft:mob_spawner"); + DataConverterTileEntity.a.put("Music", "minecraft:noteblock"); + DataConverterTileEntity.a.put("Piston", "minecraft:piston"); + DataConverterTileEntity.a.put("RecordPlayer", "minecraft:jukebox"); + DataConverterTileEntity.a.put("Sign", "minecraft:sign"); + DataConverterTileEntity.a.put("Skull", "minecraft:skull"); + DataConverterTileEntity.a.put("Structure", "minecraft:structure_block"); + DataConverterTileEntity.a.put("Trap", "minecraft:dispenser"); + } + } + + private static class DataConverterEntity implements DataConverter { + + private static final Map a = Maps.newHashMap(); + + DataConverterEntity() { + } + + public int getDataVersion() { + return 704; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = DataConverterEntity.a.get(cmp.getString("id")); + + if (s != null) { + cmp.putString("id", s); + } + + return cmp; + } + + static { + DataConverterEntity.a.put("AreaEffectCloud", "minecraft:area_effect_cloud"); + DataConverterEntity.a.put("ArmorStand", "minecraft:armor_stand"); + DataConverterEntity.a.put("Arrow", "minecraft:arrow"); + DataConverterEntity.a.put("Bat", "minecraft:bat"); + DataConverterEntity.a.put("Blaze", "minecraft:blaze"); + DataConverterEntity.a.put("Boat", "minecraft:boat"); + DataConverterEntity.a.put("CaveSpider", "minecraft:cave_spider"); + DataConverterEntity.a.put("Chicken", "minecraft:chicken"); + DataConverterEntity.a.put("Cow", "minecraft:cow"); + DataConverterEntity.a.put("Creeper", "minecraft:creeper"); + DataConverterEntity.a.put("Donkey", "minecraft:donkey"); + DataConverterEntity.a.put("DragonFireball", "minecraft:dragon_fireball"); + DataConverterEntity.a.put("ElderGuardian", "minecraft:elder_guardian"); + DataConverterEntity.a.put("EnderCrystal", "minecraft:ender_crystal"); + DataConverterEntity.a.put("EnderDragon", "minecraft:ender_dragon"); + DataConverterEntity.a.put("Enderman", "minecraft:enderman"); + DataConverterEntity.a.put("Endermite", "minecraft:endermite"); + DataConverterEntity.a.put("EyeOfEnderSignal", "minecraft:eye_of_ender_signal"); + DataConverterEntity.a.put("FallingSand", "minecraft:falling_block"); + DataConverterEntity.a.put("Fireball", "minecraft:fireball"); + DataConverterEntity.a.put("FireworksRocketEntity", "minecraft:fireworks_rocket"); + DataConverterEntity.a.put("Ghast", "minecraft:ghast"); + DataConverterEntity.a.put("Giant", "minecraft:giant"); + DataConverterEntity.a.put("Guardian", "minecraft:guardian"); + DataConverterEntity.a.put("Horse", "minecraft:horse"); + DataConverterEntity.a.put("Husk", "minecraft:husk"); + DataConverterEntity.a.put("Item", "minecraft:item"); + DataConverterEntity.a.put("ItemFrame", "minecraft:item_frame"); + DataConverterEntity.a.put("LavaSlime", "minecraft:magma_cube"); + DataConverterEntity.a.put("LeashKnot", "minecraft:leash_knot"); + DataConverterEntity.a.put("MinecartChest", "minecraft:chest_minecart"); + DataConverterEntity.a.put("MinecartCommandBlock", "minecraft:commandblock_minecart"); + DataConverterEntity.a.put("MinecartFurnace", "minecraft:furnace_minecart"); + DataConverterEntity.a.put("MinecartHopper", "minecraft:hopper_minecart"); + DataConverterEntity.a.put("MinecartRideable", "minecraft:minecart"); + DataConverterEntity.a.put("MinecartSpawner", "minecraft:spawner_minecart"); + DataConverterEntity.a.put("MinecartTNT", "minecraft:tnt_minecart"); + DataConverterEntity.a.put("Mule", "minecraft:mule"); + DataConverterEntity.a.put("MushroomCow", "minecraft:mooshroom"); + DataConverterEntity.a.put("Ozelot", "minecraft:ocelot"); + DataConverterEntity.a.put("Painting", "minecraft:painting"); + DataConverterEntity.a.put("Pig", "minecraft:pig"); + DataConverterEntity.a.put("PigZombie", "minecraft:zombie_pigman"); + DataConverterEntity.a.put("PolarBear", "minecraft:polar_bear"); + DataConverterEntity.a.put("PrimedTnt", "minecraft:tnt"); + DataConverterEntity.a.put("Rabbit", "minecraft:rabbit"); + DataConverterEntity.a.put("Sheep", "minecraft:sheep"); + DataConverterEntity.a.put("Shulker", "minecraft:shulker"); + DataConverterEntity.a.put("ShulkerBullet", "minecraft:shulker_bullet"); + DataConverterEntity.a.put("Silverfish", "minecraft:silverfish"); + DataConverterEntity.a.put("Skeleton", "minecraft:skeleton"); + DataConverterEntity.a.put("SkeletonHorse", "minecraft:skeleton_horse"); + DataConverterEntity.a.put("Slime", "minecraft:slime"); + DataConverterEntity.a.put("SmallFireball", "minecraft:small_fireball"); + DataConverterEntity.a.put("SnowMan", "minecraft:snowman"); + DataConverterEntity.a.put("Snowball", "minecraft:snowball"); + DataConverterEntity.a.put("SpectralArrow", "minecraft:spectral_arrow"); + DataConverterEntity.a.put("Spider", "minecraft:spider"); + DataConverterEntity.a.put("Squid", "minecraft:squid"); + DataConverterEntity.a.put("Stray", "minecraft:stray"); + DataConverterEntity.a.put("ThrownEgg", "minecraft:egg"); + DataConverterEntity.a.put("ThrownEnderpearl", "minecraft:ender_pearl"); + DataConverterEntity.a.put("ThrownExpBottle", "minecraft:xp_bottle"); + DataConverterEntity.a.put("ThrownPotion", "minecraft:potion"); + DataConverterEntity.a.put("Villager", "minecraft:villager"); + DataConverterEntity.a.put("VillagerGolem", "minecraft:villager_golem"); + DataConverterEntity.a.put("Witch", "minecraft:witch"); + DataConverterEntity.a.put("WitherBoss", "minecraft:wither"); + DataConverterEntity.a.put("WitherSkeleton", "minecraft:wither_skeleton"); + DataConverterEntity.a.put("WitherSkull", "minecraft:wither_skull"); + DataConverterEntity.a.put("Wolf", "minecraft:wolf"); + DataConverterEntity.a.put("XPOrb", "minecraft:xp_orb"); + DataConverterEntity.a.put("Zombie", "minecraft:zombie"); + DataConverterEntity.a.put("ZombieHorse", "minecraft:zombie_horse"); + DataConverterEntity.a.put("ZombieVillager", "minecraft:zombie_villager"); + } + } + + private static class DataConverterPotionWater implements DataConverter { + + DataConverterPotionWater() { + } + + public int getDataVersion() { + return 806; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + String s = cmp.getString("id"); + + if ("minecraft:potion".equals(s) || "minecraft:splash_potion".equals(s) || "minecraft:lingering_potion".equals(s) || "minecraft:tipped_arrow".equals(s)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (!nbttagcompound1.contains("Potion", 8)) { + nbttagcompound1.putString("Potion", "minecraft:water"); + } + + if (!cmp.contains("tag", 10)) { + cmp.put("tag", nbttagcompound1); + } + } + + return cmp; + } + } + + private static class DataConverterShulker implements DataConverter { + + DataConverterShulker() { + } + + public int getDataVersion() { + return 808; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:shulker".equals(cmp.getString("id")) && !cmp.contains("Color", 99)) { + cmp.putByte("Color", (byte) 10); + } + + return cmp; + } + } + + private static class DataConverterShulkerBoxItem implements DataConverter { + + public static final String[] a = new String[] { "minecraft:white_shulker_box", "minecraft:orange_shulker_box", "minecraft:magenta_shulker_box", "minecraft:light_blue_shulker_box", "minecraft:yellow_shulker_box", "minecraft:lime_shulker_box", "minecraft:pink_shulker_box", "minecraft:gray_shulker_box", "minecraft:silver_shulker_box", "minecraft:cyan_shulker_box", "minecraft:purple_shulker_box", "minecraft:blue_shulker_box", "minecraft:brown_shulker_box", "minecraft:green_shulker_box", "minecraft:red_shulker_box", "minecraft:black_shulker_box" }; + + DataConverterShulkerBoxItem() { + } + + public int getDataVersion() { + return 813; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:shulker_box".equals(cmp.getString("id")) && cmp.contains("tag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.contains("BlockEntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + + if (nbttagcompound2.getList("Items", 10).isEmpty()) { + nbttagcompound2.remove("Items"); + } + + int i = nbttagcompound2.getInt("Color"); + + nbttagcompound2.remove("Color"); + if (nbttagcompound2.isEmpty()) { + nbttagcompound1.remove("BlockEntityTag"); + } + + if (nbttagcompound1.isEmpty()) { + cmp.remove("tag"); + } + + cmp.putString("id", DataConverterShulkerBoxItem.a[i % 16]); + } + } + + return cmp; + } + } + + private static class DataConverterShulkerBoxBlock implements DataConverter { + + DataConverterShulkerBoxBlock() { + } + + public int getDataVersion() { + return 813; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:shulker".equals(cmp.getString("id"))) { + cmp.remove("Color"); + } + + return cmp; + } + } + + private static class DataConverterLang implements DataConverter { + + DataConverterLang() { + } + + public int getDataVersion() { + return 816; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.contains("lang", 8)) { + cmp.putString("lang", cmp.getString("lang").toLowerCase(Locale.ROOT)); + } + + return cmp; + } + } + + private static class DataConverterTotem implements DataConverter { + + DataConverterTotem() { + } + + public int getDataVersion() { + return 820; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:totem".equals(cmp.getString("id"))) { + cmp.putString("id", "minecraft:totem_of_undying"); + } + + return cmp; + } + } + + private static class DataConverterBedBlock implements DataConverter { + + private static final Logger a = LogManager.getLogger(PaperweightDataConverters.class); + + DataConverterBedBlock() { + } + + public int getDataVersion() { + return 1125; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + try { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); + int i = nbttagcompound1.getInt("xPos"); + int j = nbttagcompound1.getInt("zPos"); + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("TileEntities", 10); + net.minecraft.nbt.ListTag nbttaglist1 = nbttagcompound1.getList("Sections", 10); + + for (int k = 0; k < nbttaglist1.size(); ++k) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist1.getCompound(k); + byte b0 = nbttagcompound2.getByte("Y"); + byte[] abyte = nbttagcompound2.getByteArray("Blocks"); + + for (int l = 0; l < abyte.length; ++l) { + if (416 == (abyte[l] & 255) << 4) { + int i1 = l & 15; + int j1 = l >> 8 & 15; + int k1 = l >> 4 & 15; + net.minecraft.nbt.CompoundTag nbttagcompound3 = new net.minecraft.nbt.CompoundTag(); + + nbttagcompound3.putString("id", "bed"); + nbttagcompound3.putInt("x", i1 + (i << 4)); + nbttagcompound3.putInt("y", j1 + (b0 << 4)); + nbttagcompound3.putInt("z", k1 + (j << 4)); + nbttaglist.add(nbttagcompound3); + } + } + } + } catch (Exception exception) { + DataConverterBedBlock.a.warn("Unable to datafix Bed blocks, level format may be missing tags."); + } + + return cmp; + } + } + + private static class DataConverterBedItem implements DataConverter { + + DataConverterBedItem() { + } + + public int getDataVersion() { + return 1125; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("minecraft:bed".equals(cmp.getString("id")) && cmp.getShort("Damage") == 0) { + cmp.putShort("Damage", (short) DyeColor.RED.getId()); + } + + return cmp; + } + } + + private static class DataConverterSignText implements DataConverter { + + public static final Gson a = new GsonBuilder().registerTypeAdapter(Component.class, new JsonDeserializer() { + MutableComponent a(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + if (jsonelement.isJsonPrimitive()) { + return new TextComponent(jsonelement.getAsString()); + } else if (jsonelement.isJsonArray()) { + JsonArray jsonarray = jsonelement.getAsJsonArray(); + MutableComponent ichatbasecomponent = null; + Iterator iterator = jsonarray.iterator(); + + while (iterator.hasNext()) { + JsonElement jsonelement1 = (JsonElement) iterator.next(); + MutableComponent ichatbasecomponent1 = this.a(jsonelement1, jsonelement1.getClass(), jsondeserializationcontext); + + if (ichatbasecomponent == null) { + ichatbasecomponent = ichatbasecomponent1; + } else { + ichatbasecomponent.append(ichatbasecomponent1); + } + } + + return ichatbasecomponent; + } else { + throw new JsonParseException("Don't know how to turn " + jsonelement + " into a Component"); + } + } + + public Object deserialize(JsonElement jsonelement, Type type, JsonDeserializationContext jsondeserializationcontext) throws JsonParseException { + return this.a(jsonelement, type, jsondeserializationcontext); + } + }).create(); + + DataConverterSignText() { + } + + public int getDataVersion() { + return 101; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if ("Sign".equals(cmp.getString("id"))) { + this.convert(cmp, "Text1"); + this.convert(cmp, "Text2"); + this.convert(cmp, "Text3"); + this.convert(cmp, "Text4"); + } + + return cmp; + } + + private void convert(net.minecraft.nbt.CompoundTag nbttagcompound, String s) { + String s1 = nbttagcompound.getString(s); + Component object = null; + + if (!"null".equals(s1) && !StringUtil.isNullOrEmpty(s1)) { + if ((s1.charAt(0) != 34 || s1.charAt(s1.length() - 1) != 34) && (s1.charAt(0) != 123 || s1.charAt(s1.length() - 1) != 125)) { + object = new TextComponent(s1); + } else { + try { + object = GsonHelper.fromJson(DataConverterSignText.a, s1, Component.class, true); + if (object == null) { + object = new TextComponent(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = Component.Serializer.fromJson(s1); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = Component.Serializer.fromJsonLenient(s1); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = new TextComponent(s1); + } + } + } else { + object = new TextComponent(""); + } + + nbttagcompound.putString(s, Component.Serializer.toJson(object)); + } + } + + private static class DataInspectorPlayerVehicle implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("RootVehicle", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("RootVehicle"); + + if (nbttagcompound1.contains("Entity", 10)) { + convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); + } + } + + return cmp; + } + } + + private static class DataInspectorLevelPlayer implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("Player", 10)) { + convertCompound(LegacyType.PLAYER, cmp, "Player", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorStructure implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + net.minecraft.nbt.ListTag nbttaglist; + int j; + net.minecraft.nbt.CompoundTag nbttagcompound1; + + if (cmp.contains("entities", 9)) { + nbttaglist = cmp.getList("entities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); + if (nbttagcompound1.contains("nbt", 10)) { + convertCompound(LegacyType.ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); + } + } + } + + if (cmp.contains("blocks", 9)) { + nbttaglist = cmp.getList("blocks", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); + if (nbttagcompound1.contains("nbt", 10)) { + convertCompound(LegacyType.BLOCK_ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); + } + } + } + + return cmp; + } + } + + private static class DataInspectorChunks implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("Level", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); + net.minecraft.nbt.ListTag nbttaglist; + int j; + + if (nbttagcompound1.contains("Entities", 9)) { + nbttaglist = nbttagcompound1.getList("Entities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer)); + } + } + + if (nbttagcompound1.contains("TileEntities", 9)) { + nbttaglist = nbttagcompound1.getList("TileEntities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.BLOCK_ENTITY, (net.minecraft.nbt.CompoundTag) nbttaglist.get(j), sourceVer, targetVer)); + } + } + } + + return cmp; + } + } + + private static class DataInspectorEntityPassengers implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (cmp.contains("Passengers", 9)) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("Passengers", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ENTITY, nbttaglist.getCompound(j), sourceVer, targetVer)); + } + } + + return cmp; + } + } + + private static class DataInspectorPlayer implements DataInspector { + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + convertItems(cmp, "Inventory", sourceVer, targetVer); + convertItems(cmp, "EnderItems", sourceVer, targetVer); + if (cmp.contains("ShoulderEntityLeft", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityLeft", sourceVer, targetVer); + } + + if (cmp.contains("ShoulderEntityRight", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityRight", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorVillagers implements DataInspector { + ResourceLocation entityVillager = getKey("EntityVillager"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (entityVillager.equals(new ResourceLocation(cmp.getString("id"))) && cmp.contains("Offers", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Offers"); + + if (nbttagcompound1.contains("Recipes", 9)) { + net.minecraft.nbt.ListTag nbttaglist = nbttagcompound1.getList("Recipes", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompound(j); + + convertItem(nbttagcompound2, "buy", sourceVer, targetVer); + convertItem(nbttagcompound2, "buyB", sourceVer, targetVer); + convertItem(nbttagcompound2, "sell", sourceVer, targetVer); + nbttaglist.set(j, nbttagcompound2); + } + } + } + + return cmp; + } + } + + private static class DataInspectorMobSpawnerMinecart implements DataInspector { + ResourceLocation entityMinecartMobSpawner = getKey("EntityMinecartMobSpawner"); + ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + String s = cmp.getString("id"); + if (entityMinecartMobSpawner.equals(new ResourceLocation(s))) { + cmp.putString("id", tileEntityMobSpawner.toString()); + convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); + cmp.putString("id", s); + } + + return cmp; + } + } + + private static class DataInspectorMobSpawnerMobs implements DataInspector { + ResourceLocation tileEntityMobSpawner = getKey("TileEntityMobSpawner"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (tileEntityMobSpawner.equals(new ResourceLocation(cmp.getString("id")))) { + if (cmp.contains("SpawnPotentials", 9)) { + net.minecraft.nbt.ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttaglist.getCompound(j); + + convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); + } + } + + convertCompound(LegacyType.ENTITY, cmp, "SpawnData", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorCommandBlock implements DataInspector { + ResourceLocation tileEntityCommand = getKey("TileEntityCommand"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (tileEntityCommand.equals(new ResourceLocation(cmp.getString("id")))) { + cmp.putString("id", "Control"); + convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); + cmp.putString("id", "MinecartCommandBlock"); + } + + return cmp; + } + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightFakePlayer.java b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightFakePlayer.java new file mode 100644 index 0000000000..f3097a98bb --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightFakePlayer.java @@ -0,0 +1,98 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_18_R2; + +import com.mojang.authlib.GameProfile; +import net.minecraft.network.chat.ChatType; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.game.ServerboundClientInformationPacket; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.stats.Stat; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.phys.Vec3; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import java.util.OptionalInt; +import java.util.UUID; + +class PaperweightFakePlayer extends ServerPlayer { + private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]"); + private static final Vec3 ORIGIN = new Vec3(0.0D, 0.0D, 0.0D); + + PaperweightFakePlayer(ServerLevel world) { + super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE); + } + + @Override + public Vec3 position() { + return ORIGIN; + } + + @Override + public void tick() { + } + + @Override + public void die(DamageSource damagesource) { + } + + @Override + public Entity changeDimension(ServerLevel worldserver, TeleportCause cause) { + return this; + } + + @Override + public OptionalInt openMenu(MenuProvider factory) { + return OptionalInt.empty(); + } + + @Override + public void updateOptions(ServerboundClientInformationPacket packet) { + } + + @Override + public void displayClientMessage(Component message, boolean actionBar) { + } + + @Override + public void sendMessage(Component message, ChatType type, UUID sender) { + } + + @Override + public void awardStat(Stat stat, int amount) { + } + + @Override + public void awardStat(Stat stat) { + } + + @Override + public boolean isInvulnerableTo(DamageSource damageSource) { + return true; + } + + @Override + public void openTextEdit(SignBlockEntity sign) { + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightWorldNativeAccess.java new file mode 100644 index 0000000000..be9cdb98e7 --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.18.2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R2/PaperweightWorldNativeAccess.java @@ -0,0 +1,183 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_18_R2; + +import com.sk89q.jnbt.AdventureNBTConverter; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import com.sk89q.worldedit.world.block.BlockState; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.Tag; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.chunk.LevelChunk; +import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData; +import org.bukkit.event.block.BlockPhysicsEvent; + +import java.lang.ref.WeakReference; +import java.util.Objects; +import javax.annotation.Nullable; + +public class PaperweightWorldNativeAccess implements WorldNativeAccess { + private static final int UPDATE = 1; + private static final int NOTIFY = 2; + + private final PaperweightAdapter adapter; + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public PaperweightWorldNativeAccess(PaperweightAdapter adapter, WeakReference world) { + this.adapter = adapter; + this.world = world; + } + + private ServerLevel getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public LevelChunk getChunk(int x, int z) { + return getWorld().getChunk(x, z); + } + + @Override + public net.minecraft.world.level.block.state.BlockState toNative(BlockState state) { + int stateId = BlockStateIdAccess.getBlockStateId(state); + return BlockStateIdAccess.isValidInternalId(stateId) + ? Block.stateById(stateId) + : ((CraftBlockData) BukkitAdapter.adapt(state)).getState(); + } + + @Override + public net.minecraft.world.level.block.state.BlockState getBlockState(LevelChunk chunk, BlockPos position) { + return chunk.getBlockState(position); + } + + @Nullable + @Override + public net.minecraft.world.level.block.state.BlockState setBlockState(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState state) { + return chunk.setBlockState(position, state, false, this.sideEffectSet.shouldApply(SideEffect.UPDATE)); + } + + @Override + public net.minecraft.world.level.block.state.BlockState getValidBlockForPosition(net.minecraft.world.level.block.state.BlockState block, BlockPos position) { + return Block.updateFromNeighbourShapes(block, getWorld(), position); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos position) { + getWorld().getChunkSource().getLightEngine().checkBlock(position); + } + + @Override + public boolean updateTileEntity(BlockPos position, CompoundBinaryTag tag) { + // We will assume that the tile entity was created for us + BlockEntity tileEntity = getWorld().getBlockEntity(position); + if (tileEntity == null) { + return false; + } + Tag nativeTag = adapter.fromNative(AdventureNBTConverter.fromAdventure(tag)); + PaperweightAdapter.readTagIntoTileEntity((net.minecraft.nbt.CompoundTag) nativeTag, tileEntity); + return true; + } + + @Override + public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { + getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); + } + } + + @Override + public boolean isChunkTicking(LevelChunk chunk) { + return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING); + } + + @Override + public void markBlockChanged(LevelChunk chunk, BlockPos position) { + if (chunk.getSections()[getWorld().getSectionIndex(position.getY())] != null) { + getWorld().getChunkSource().blockChanged(position); + } + } + + @Override + public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + ServerLevel world = getWorld(); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + world.updateNeighborsAt(pos, oldState.getBlock()); + } else { + // When we don't want events, manually run the physics without them. + Block block = oldState.getBlock(); + fireNeighborChanged(pos, world, block, pos.west()); + fireNeighborChanged(pos, world, block, pos.east()); + fireNeighborChanged(pos, world, block, pos.below()); + fireNeighborChanged(pos, world, block, pos.above()); + fireNeighborChanged(pos, world, block, pos.north()); + fireNeighborChanged(pos, world, block, pos.south()); + } + if (newState.hasAnalogOutputSignal()) { + world.updateNeighbourForOutputSignal(pos, newState.getBlock()); + } + } + + private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { + world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false); + } + + @Override + public void updateNeighbors(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState, int recursionLimit) { + ServerLevel world = getWorld(); + // a == updateNeighbors + // b == updateDiagonalNeighbors + oldState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); + if (sideEffectSet.shouldApply(SideEffect.EVENTS)) { + CraftWorld craftWorld = world.getWorld(); + BlockPhysicsEvent event = new BlockPhysicsEvent(craftWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), CraftBlockData.fromData(newState)); + world.getCraftServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + } + newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); + newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); + } + + @Override + public void onBlockStateChange(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + getWorld().onBlockStateChange(pos, oldState, newState); + } +} diff --git a/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightAdapter.java index 5284346d3b..f4bbab0c5f 100644 --- a/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightAdapter.java @@ -23,12 +23,9 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.Futures; import com.mojang.datafixers.util.Either; -import com.mojang.serialization.Codec; -import com.mojang.serialization.Dynamic; import com.mojang.serialization.Lifecycle; import com.sk89q.jnbt.AdventureNBTConverter; import com.sk89q.jnbt.ByteArrayTag; @@ -85,11 +82,8 @@ import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; -import net.minecraft.nbt.NbtOps; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; -import net.minecraft.resources.RegistryReadOps; -import net.minecraft.resources.RegistryWriteOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -120,7 +114,6 @@ import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.dimension.LevelStem; import net.minecraft.world.level.levelgen.WorldGenSettings; import net.minecraft.world.level.storage.LevelStorageSource; @@ -152,13 +145,14 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.OptionalInt; +import java.util.OptionalLong; import java.util.Set; +import java.util.TreeMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; @@ -420,6 +414,11 @@ public BaseEntity getEntity(org.bukkit.entity.Entity entity) { CraftEntity craftEntity = ((CraftEntity) entity); Entity mcEntity = craftEntity.getHandle(); + // Do not allow creating of passenger entity snapshots, passengers are included in the vehicle entity + if (mcEntity.isPassenger()) { + return null; + } + String id = getEntityId(mcEntity); net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); @@ -436,27 +435,48 @@ public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state CraftWorld craftWorld = ((CraftWorld) location.getWorld()); ServerLevel worldServer = craftWorld.getHandle(); - Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle()); + String entityId = state.getType().getId(); - if (createdEntity != null) { - CompoundTag nativeTag = state.getNbtData(); - if (nativeTag != null) { - net.minecraft.nbt.CompoundTag tag = (net.minecraft.nbt.CompoundTag) fromNative(nativeTag); - for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - readTagIntoEntity(tag, createdEntity); - } + CompoundTag nativeTag = state.getNbtData(); + net.minecraft.nbt.CompoundTag tag; + if (nativeTag != null) { + tag = (net.minecraft.nbt.CompoundTag) fromNative(nativeTag); + removeUnwantedEntityTagsRecursively(tag); + } else { + tag = new net.minecraft.nbt.CompoundTag(); + } + + tag.putString("id", entityId); - createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + Entity createdEntity = EntityType.loadEntityRecursive(tag, craftWorld.getHandle(), (loadedEntity) -> { + loadedEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + return loadedEntity; + }); - worldServer.addFreshEntity(createdEntity, SpawnReason.CUSTOM); + if (createdEntity != null) { + worldServer.addFreshEntityWithPassengers(createdEntity, SpawnReason.CUSTOM); return createdEntity.getBukkitEntity(); } else { return null; } } + // This removes all unwanted tags from the main entity and all its passengers + private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + + // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive + if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) { + net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND); + + for (int i = 0; i < nbttaglist.size(); ++i) { + removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); + } + } + } + @Override public Component getRichBlockName(BlockType blockType) { return TranslatableComponent.of(getBlockFromType(blockType).getDescriptionId()); @@ -473,28 +493,34 @@ public Component getRichItemName(BaseItemStack itemStack) { } @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public Map> getProperties(BlockType blockType) { - Map> properties = Maps.newTreeMap(String::compareTo); - Block block = getBlockFromType(blockType); - StateDefinition blockStateList = - block.getStateDefinition(); - for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { - Property property; + private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() { + @Override + public Property load(net.minecraft.world.level.block.state.properties.Property state) throws Exception { if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { - property = new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); } else if (state instanceof DirectionProperty) { - property = new DirectionalProperty(state.getName(), + return new DirectionalProperty(state.getName(), (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - property = new EnumProperty(state.getName(), + return new EnumProperty(state.getName(), (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList())); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { - property = new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); } else { throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName()); } + } + }); + @SuppressWarnings({ "rawtypes" }) + @Override + public Map> getProperties(BlockType blockType) { + Map> properties = new TreeMap<>(); + Block block = getBlockFromType(blockType); + StateDefinition blockStateList = + block.getStateDefinition(); + for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { + Property property = PROPERTY_CACHE.getUnchecked(state); properties.put(property.getName(), property); } return properties; @@ -604,7 +630,7 @@ private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, long seed = options.getSeed().orElse(originalWorld.getSeed()); WorldGenSettings newOpts = options.getSeed().isPresent() - ? replaceSeed(originalWorld, seed, originalOpts) + ? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) : originalOpts; LevelSettings newWorldSettings = new LevelSettings( @@ -649,49 +675,6 @@ private void doRegen(org.bukkit.World bukkitWorld, Region region, Extent extent, } } - private WorldGenSettings replaceSeed(ServerLevel originalWorld, long seed, WorldGenSettings originalOpts) { - RegistryWriteOps nbtReadRegOps = RegistryWriteOps.create( - NbtOps.INSTANCE, - originalWorld.getServer().registryAccess() - ); - RegistryReadOps nbtRegOps = RegistryReadOps.createAndLoad( - NbtOps.INSTANCE, - originalWorld.getServer().getResourceManager(), - originalWorld.getServer().registryAccess() - ); - Codec dimCodec = WorldGenSettings.CODEC; - return dimCodec - .encodeStart(nbtReadRegOps, originalOpts) - .flatMap(tag -> - dimCodec.parse( - recursivelySetSeed(new Dynamic<>(nbtRegOps, tag), seed, new HashSet<>()) - ) - ) - .get() - .map( - l -> l, - error -> { - throw new IllegalStateException("Unable to map GeneratorOptions: " + error.message()); - } - ); - } - - @SuppressWarnings("unchecked") - private Dynamic recursivelySetSeed(Dynamic dynamic, long seed, Set> seen) { - if (!seen.add(dynamic)) { - return dynamic; - } - return dynamic.updateMapValues(pair -> { - if (pair.getFirst().asString("").equals("seed")) { - return pair.mapSecond(v -> v.createLong(seed)); - } - if (pair.getSecond().getValue() instanceof net.minecraft.nbt.CompoundTag) { - return pair.mapSecond(v -> recursivelySetSeed((Dynamic) v, seed, seen)); - } - return pair; - }); - } - private BiomeType adapt(ServerLevel serverWorld, Biome origBiome) { ResourceLocation key = serverWorld.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY).getKey(origBiome); if (key == null) { @@ -740,9 +723,7 @@ private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld } extent.setBlock(vec, state.toBaseBlock()); if (options.shouldRegenBiomes()) { - PalettedContainer biomeIndex = chunk.getSection(chunk.getSectionIndex(vec.getBlockY())) - .getBiomes(); - Biome origBiome = biomeIndex.get(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()); + Biome origBiome = chunk.getNoiseBiome(vec.getX(), vec.getY(), vec.getZ()); BiomeType adaptedBiome = adapt(serverWorld, origBiome); if (adaptedBiome != null) { extent.setBiome(vec, adaptedBiome); diff --git a/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightWorldNativeAccess.java index f2839de501..41ce6fcb79 100644 --- a/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1.18/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_18_R1/PaperweightWorldNativeAccess.java @@ -154,6 +154,12 @@ public void notifyNeighbors(BlockPos pos, net.minecraft.world.level.block.state. } } + @Override + public void updateBlock(BlockPos pos, net.minecraft.world.level.block.state.BlockState oldState, net.minecraft.world.level.block.state.BlockState newState) { + ServerLevel world = getWorld(); + newState.onPlace(world, pos, oldState, false); + } + private void fireNeighborChanged(BlockPos pos, ServerLevel world, Block block, BlockPos neighborPos) { world.getBlockState(neighborPos).neighborChanged(world, neighborPos, block, pos, false); } diff --git a/worldedit-bukkit/adapters/adapter-legacy/src/main/resources/worldedit-adapters.jar b/worldedit-bukkit/adapters/adapter-legacy/src/main/resources/worldedit-adapters.jar new file mode 100644 index 0000000000..b5a31a462c Binary files /dev/null and b/worldedit-bukkit/adapters/adapter-legacy/src/main/resources/worldedit-adapters.jar differ diff --git a/worldedit-bukkit/build.gradle.kts b/worldedit-bukkit/build.gradle.kts index 5b37794ca8..9f8205a970 100644 --- a/worldedit-bukkit/build.gradle.kts +++ b/worldedit-bukkit/build.gradle.kts @@ -51,7 +51,7 @@ dependencies { } "implementation"("io.papermc:paperlib:1.0.7") "compileOnly"("com.sk89q:dummypermscompat:1.10") - "implementation"("org.bstats:bstats-bukkit:2.1.0") + "implementation"("org.bstats:bstats-bukkit:2.2.1") "implementation"("it.unimi.dsi:fastutil") "testImplementation"("org.mockito:mockito-core:1.9.0-rc1") @@ -84,20 +84,17 @@ tasks.named("shadowJar") { // In tandem with not bundling log4j, we shouldn't relocate base package here. // relocate("org.apache.logging", "com.sk89q.worldedit.log4j") relocate("org.antlr.v4", "com.sk89q.worldedit.antlr4") - include(dependency(":worldedit-core")) // Purposefully not included, we assume (even though no API exposes it) that Log4J will be present at runtime // If it turns out not to be true for Spigot/Paper, our only two official platforms, this can be uncommented. // include(dependency("org.apache.logging.log4j:log4j-api")) include(dependency("org.antlr:antlr4-runtime")) - relocate("org.bstats", "com.sk89q.worldedit.bstats") { - include(dependency("org.bstats:")) - } - relocate("io.papermc.lib", "com.sk89q.worldedit.bukkit.paperlib") { - include(dependency("io.papermc:paperlib")) - } - relocate("it.unimi.dsi.fastutil", "com.sk89q.worldedit.bukkit.fastutil") { - include(dependency("it.unimi.dsi:fastutil")) - } + include(dependency("org.bstats:")) + include(dependency("io.papermc:paperlib")) + include(dependency("it.unimi.dsi:fastutil")) + + relocate("org.bstats", "com.sk89q.worldedit.bstats") + relocate("io.papermc.lib", "com.sk89q.worldedit.bukkit.paperlib") + relocate("it.unimi.dsi.fastutil", "com.sk89q.worldedit.bukkit.fastutil") } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/ClassSourceValidator.java b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/ClassSourceValidator.java index e0789e3e7a..bc06d4845c 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/ClassSourceValidator.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/ClassSourceValidator.java @@ -21,11 +21,11 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; -import java.security.CodeSource; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -40,12 +40,27 @@ */ public class ClassSourceValidator { - private static final Logger LOGGER = LogManagerCompat.getLogger(); private static final String SEPARATOR_LINE = Strings.repeat("*", 46); + private static final Method loadClass; + private static Class pluginClassLoaderClass; + + static { + Method tmp; + try { + pluginClassLoaderClass = Class.forName("org.bukkit.plugin.java.PluginClassLoader", false, + Bukkit.class.getClassLoader()); + tmp = pluginClassLoaderClass.getDeclaredMethod("loadClass0", + String.class, boolean.class, boolean.class, boolean.class); + tmp.setAccessible(true); + } catch (NoSuchMethodException | ClassNotFoundException e) { + tmp = null; + } + loadClass = tmp; + } private final Plugin plugin; @Nullable - private final CodeSource expectedCodeSource; + private final ClassLoader expectedClassLoader; /** * Create a new instance. @@ -55,7 +70,10 @@ public class ClassSourceValidator { public ClassSourceValidator(Plugin plugin) { checkNotNull(plugin, "plugin"); this.plugin = plugin; - this.expectedCodeSource = plugin.getClass().getProtectionDomain().getCodeSource(); + this.expectedClassLoader = plugin.getClass().getClassLoader(); + if (loadClass == null) { + plugin.getLogger().info("Bukkit PluginClassLoader seems to have changed. Class source validation will be skipped."); + } } /** @@ -64,19 +82,33 @@ public ClassSourceValidator(Plugin plugin) { * @param classes A list of classes to check * @return The results */ - public Map, CodeSource> findMismatches(List> classes) { + public Map, Plugin> findMismatches(List> classes) { checkNotNull(classes, "classes"); - if (expectedCodeSource == null) { + if (expectedClassLoader == null || loadClass == null) { return ImmutableMap.of(); } - Map, CodeSource> mismatches = new HashMap<>(); + Map, Plugin> mismatches = new HashMap<>(); - for (Class testClass : classes) { - CodeSource testSource = testClass.getProtectionDomain().getCodeSource(); - if (!expectedCodeSource.equals(testSource)) { - mismatches.put(testClass, testSource); + for (Plugin target : Bukkit.getPluginManager().getPlugins()) { + if (target == plugin) { + continue; + } + ClassLoader targetLoader = target.getClass().getClassLoader(); + if (!(pluginClassLoaderClass.isAssignableFrom(targetLoader.getClass()))) { + continue; + } + for (Class testClass : classes) { + Class targetClass; + try { + targetClass = (Class) loadClass.invoke(targetLoader, testClass.getName(), false, false, false); + } catch (IllegalAccessException | InvocationTargetException ignored) { + continue; + } + if (targetClass.getClassLoader() != expectedClassLoader) { + mismatches.putIfAbsent(testClass, targetClass.getClassLoader() == targetLoader ? target : null); + } } } @@ -94,7 +126,7 @@ public void reportMismatches(List> classes) { if (Boolean.getBoolean("enginehub.disable.class.source.validation")) { return; } - Map, CodeSource> mismatches = findMismatches(classes); + Map, Plugin> mismatches = findMismatches(classes); if (mismatches.isEmpty()) { return; @@ -117,15 +149,17 @@ public void reportMismatches(List> classes) { builder.append("**\n"); builder.append("** Here are some files that have been overridden:\n"); builder.append("** \n"); - for (Map.Entry, CodeSource> entry : mismatches.entrySet()) { - CodeSource codeSource = entry.getValue(); - String url = codeSource != null ? codeSource.getLocation().toExternalForm() : "(unknown)"; + for (Map.Entry, Plugin> entry : mismatches.entrySet()) { + Plugin badPlugin = entry.getValue(); + String url = badPlugin == null + ? "(unknown)" + : badPlugin.getName() + " (" + badPlugin.getClass().getProtectionDomain().getCodeSource().getLocation() + ")"; builder.append("** '").append(entry.getKey().getSimpleName()).append("' came from '").append(url).append("'\n"); } builder.append("**\n"); builder.append("** Please report this to the plugins' developers.\n"); builder.append(SEPARATOR_LINE).append("\n"); - LOGGER.error(builder.toString()); + plugin.getLogger().severe(builder.toString()); } } diff --git a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java index d9c0ef37d0..e24078e11d 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/bukkit/util/CommandRegistration.java @@ -75,7 +75,7 @@ public boolean register(List registered) { } for (CommandInfo command : registered) { DynamicPluginCommand cmd = new DynamicPluginCommand(command.getAliases(), - command.getDesc(), "/" + command.getAliases()[0] + " " + command.getUsage(), executor, command.getRegisteredWith(), plugin); + command.getDesc(), command.getUsage(), executor, command.getRegisteredWith(), plugin); cmd.setPermissions(command.getPermissions()); commandMap.register(plugin.getDescription().getName(), cmd); } diff --git a/worldedit-core/build.gradle.kts b/worldedit-core/build.gradle.kts index 0e7c38abfb..0b7d45f1c4 100644 --- a/worldedit-core/build.gradle.kts +++ b/worldedit-core/build.gradle.kts @@ -39,7 +39,7 @@ dependencies { "implementation"("org.mozilla:rhino-runtime:1.7.13") "implementation"("org.yaml:snakeyaml:1.26") "implementation"("com.google.guava:guava") - "implementation"("com.google.code.findbugs:jsr305:1.3.9") + "compileOnlyApi"("com.google.code.findbugs:jsr305:1.3.9") "implementation"("com.google.code.gson:gson") "implementation"("org.apache.logging.log4j:log4j-api:${Versions.LOG4J}") { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java index 0212884a0e..a42f88f24d 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BiomeCommands.java @@ -30,6 +30,7 @@ import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; +import com.sk89q.worldedit.extension.platform.Locatable; import com.sk89q.worldedit.function.RegionFunction; import com.sk89q.worldedit.function.RegionMaskingFilter; import com.sk89q.worldedit.function.biome.BiomeReplace; @@ -106,7 +107,7 @@ public void biomeList(Actor actor, descFooter = "By default, uses all blocks in your selection." ) @CommandPermissions("worldedit.biome.info") - public void biomeInfo(Player player, LocalSession session, + public void biomeInfo(Actor actor, World world, LocalSession session, @Switch(name = 't', desc = "Use the block you are looking at.") boolean useLineOfSight, @Switch(name = 'p', desc = "Use the block you are currently in.") @@ -117,23 +118,32 @@ public void biomeInfo(Player player, LocalSession session, String messageKey; if (useLineOfSight) { - Location blockPosition = player.getBlockTrace(300); - if (blockPosition == null) { - player.printError(TranslatableComponent.of("worldedit.raytrace.noblock")); + if (actor instanceof Player) { + Location blockPosition = ((Player) actor).getBlockTrace(300); + if (blockPosition == null) { + actor.printError(TranslatableComponent.of("worldedit.raytrace.noblock")); + return; + } + + BiomeType biome = world.getBiome(blockPosition.toVector().toBlockPoint()); + biomes.add(biome); + + messageKey = "worldedit.biomeinfo.lineofsight"; + } else { + actor.printError(TranslatableComponent.of("worldedit.raytrace.require-player")); return; } - - BiomeType biome = player.getWorld().getBiome(blockPosition.toVector().toBlockPoint()); - biomes.add(biome); - - messageKey = "worldedit.biomeinfo.lineofsight"; } else if (usePosition) { - BiomeType biome = player.getWorld().getBiome(player.getLocation().toVector().toBlockPoint()); - biomes.add(biome); + if (actor instanceof Locatable) { + BiomeType biome = world.getBiome(((Locatable) actor).getLocation().toVector().toBlockPoint()); + biomes.add(biome); - messageKey = "worldedit.biomeinfo.position"; + messageKey = "worldedit.biomeinfo.position"; + } else { + actor.printError(TranslatableComponent.of("worldedit.biomeinfo.not-locatable")); + return; + } } else { - World world = player.getWorld(); Region region = session.getSelection(world); for (BlockVector3 pt : region) { @@ -148,7 +158,7 @@ public void biomeInfo(Player player, LocalSession session, HoverEvent.showText(TextComponent.of(biome.getId())) ) ).collect(Collectors.toList()); - player.printInfo(TranslatableComponent.of(messageKey, TextUtils.join(components, TextComponent.of(", ")))); + actor.printInfo(TranslatableComponent.of(messageKey, TextUtils.join(components, TextComponent.of(", ")))); } @Command( @@ -158,17 +168,21 @@ public void biomeInfo(Player player, LocalSession session, ) @Logging(REGION) @CommandPermissions("worldedit.biome.set") - public void setBiome(Player player, LocalSession session, EditSession editSession, + public void setBiome(Actor actor, World world, LocalSession session, EditSession editSession, @Arg(desc = "Biome type.") BiomeType target, @Switch(name = 'p', desc = "Use your current position") boolean atPosition) throws WorldEditException { - World world = player.getWorld(); Region region; Mask mask = editSession.getMask(); if (atPosition) { - final BlockVector3 pos = player.getLocation().toVector().toBlockPoint(); - region = new CuboidRegion(pos, pos); + if (actor instanceof Locatable) { + final BlockVector3 pos = ((Locatable) actor).getLocation().toVector().toBlockPoint(); + region = new CuboidRegion(pos, pos); + } else { + actor.printError(TranslatableComponent.of("worldedit.setbiome.not-locatable")); + return; + } } else { region = session.getSelection(world); } @@ -180,7 +194,7 @@ public void setBiome(Player player, LocalSession session, EditSession editSessio RegionVisitor visitor = new RegionVisitor(region, replace); Operations.completeLegacy(visitor); - player.printInfo(TranslatableComponent.of( + actor.printInfo(TranslatableComponent.of( "worldedit.setbiome.changed", TextComponent.of(visitor.getAffected()) ) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index e0c858f487..c1f769d859 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -276,7 +276,7 @@ public int pyramid(Actor actor, LocalSession session, EditSession editSession, name = "/generate", aliases = { "/gen", "/g" }, desc = "Generates a shape according to a formula.", - descFooter = "See also https://tinyurl.com/weexpr." + descFooter = "For details, see https://ehub.to/we/expr" ) @CommandPermissions("worldedit.generation.shape") @Logging(ALL) @@ -345,7 +345,7 @@ public int generate(Actor actor, LocalSession session, EditSession editSession, name = "/generatebiome", aliases = { "/genbiome", "/gb" }, desc = "Sets biome according to a formula.", - descFooter = "See also https://tinyurl.com/weexpr." + descFooter = "For details, see https://ehub.to/we/expr" ) @CommandPermissions("worldedit.generation.shape.biome") @Logging(ALL) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java index 7a006cd83a..addf03c289 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/RegionCommands.java @@ -394,6 +394,8 @@ public int stack(Actor actor, World world, EditSession editSession, LocalSession boolean blockUnits, @ArgFlag(name = 'm', desc = "Set the include mask, non-matching blocks become air") Mask mask) throws WorldEditException { + checkCommandArgument(count >= 1, "Count must be >= 1"); + Mask combinedMask; if (ignoreAirBlocks) { if (mask == null) { @@ -467,7 +469,7 @@ void regenerate(Actor actor, World world, LocalSession session, EditSession edit desc = "Deforms a selected region with an expression", descFooter = "The expression is executed for each block and is expected\n" + "to modify the variables x, y and z to point to a new block\n" - + "to fetch. See also https://tinyurl.com/weexpr" + + "to fetch. For details, see https://ehub.to/we/expr" ) @CommandPermissions("worldedit.region.deform") @Logging(ALL) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/Chunk3dVectorConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/Chunk3dVectorConverter.java index 4cfccd4786..68fd64f817 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/Chunk3dVectorConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/Chunk3dVectorConverter.java @@ -56,7 +56,7 @@ public static void register(CommandManager commandManager) { } throw new AssertionError("Expected 2 or 3 components"); }, - "block vector with x,z or x,y,z" + "block vector in the form x,z or x,y,z" )); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/VectorConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/VectorConverter.java index 91ddfa2e4e..31436b6238 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/VectorConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/VectorConverter.java @@ -48,7 +48,7 @@ public class VectorConverter implements ArgumentConverter { INT_CONVERTER, 3, cmps -> BlockVector3.at(cmps.get(0), cmps.get(1), cmps.get(2)), - "block vector with x, y, and z" + "block vector in the form x,y,z" ); public static void register(CommandManager commandManager) { @@ -58,14 +58,14 @@ public static void register(CommandManager commandManager) { INT_CONVERTER, 2, cmps -> BlockVector2.at(cmps.get(0), cmps.get(1)), - "block vector with x and z" + "block vector in the form x,z" )); commandManager.registerConverter(Key.of(Vector2.class), new VectorConverter<>( doubleConverter, 2, cmps -> Vector2.at(cmps.get(0), cmps.get(1)), - "vector with x and z" + "vector in the form x,z" )); commandManager.registerConverter(Key.of(BlockVector3.class), BLOCK_VECTOR_3_CONVERTER); commandManager.registerConverter(Key.of(Vector3.class), @@ -73,7 +73,7 @@ public static void register(CommandManager commandManager) { doubleConverter, 3, cmps -> Vector3.at(cmps.get(0), cmps.get(1), cmps.get(2)), - "vector with x, y, and z" + "vector in the form x,y,z" )); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java index 4e02358603..8919146cc4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/mask/BlockStateMask.java @@ -28,6 +28,7 @@ import com.sk89q.worldedit.world.block.BlockType; import java.util.Map; +import java.util.Objects; import javax.annotation.Nullable; public class BlockStateMask extends AbstractExtentMask { @@ -58,7 +59,11 @@ public boolean test(BlockVector3 vector) { if (strict && checkProps.isEmpty()) { return false; } - return checkProps.entrySet().stream() - .allMatch(entry -> block.getState(entry.getKey()) == entry.getValue()); + for (Map.Entry, Object> entry : checkProps.entrySet()) { + if (!Objects.equals(block.getState(entry.getKey()), entry.getValue())) { + return false; + } + } + return true; } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/BlockStateIdAccess.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/BlockStateIdAccess.java index 387a25f9a7..5a4ca155c4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/BlockStateIdAccess.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/block/BlockStateIdAccess.java @@ -32,7 +32,7 @@ public final class BlockStateIdAccess { private static final int INVALID_ID = -1; - private static final int EXPECTED_BLOCK_COUNT = 2 << 13; + private static final int EXPECTED_BLOCK_COUNT = 2 << 14; private static final Int2ObjectOpenHashMap TO_STATE = new Int2ObjectOpenHashMap<>(EXPECTED_BLOCK_COUNT); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverterHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverterHelper.java index bab3a3936e..dd48e0f0fa 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverterHelper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/command/exception/ExceptionConverterHelper.java @@ -78,7 +78,7 @@ public void convert(Throwable t) throws CommandException { throw (CommandException) e.getCause(); } if (e.getCause() instanceof com.sk89q.minecraft.util.commands.CommandException) { - throw new CommandException(e.getCause(), ImmutableList.of()); + throw new CommandExecutionException(e.getCause(), ImmutableList.of()); } throw new CommandExecutionException(e, ImmutableList.of()); } catch (IllegalArgumentException | IllegalAccessException e) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java index 0accaa6427..24b6157480 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java @@ -101,6 +101,10 @@ default void applySideEffects(BlockVector3 position, BlockState previousType, Si NBS oldData = toNative(previousType); NBS newData = getBlockState(chunk, pos); + if (sideEffectSet.shouldApply(SideEffect.UPDATE)) { + updateBlock(pos, oldData, newData); + } + if (sideEffectSet.getState(SideEffect.LIGHTING) == SideEffect.State.ON) { updateLightingForBlock(pos); } @@ -150,6 +154,9 @@ default void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { void notifyNeighbors(NP pos, NBS oldState, NBS newState); + default void updateBlock(NP pos, NBS oldState, NBS newState) { + } + void updateNeighbors(NP pos, NBS oldState, NBS newState, int recursionLimit); void onBlockStateChange(NP pos, NBS oldState, NBS newState); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java index e4c764c0f9..b9fe5ed7e3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Registry.java @@ -45,14 +45,14 @@ public String getName() { @Nullable public V get(final String key) { - checkState(key.equals(key.toLowerCase(Locale.ROOT)), "key must be lowercase"); + checkState(key.equals(key.toLowerCase(Locale.ROOT)), "key must be lowercase: %s", key); return this.map.get(key); } public V register(final String key, final V value) { requireNonNull(key, "key"); requireNonNull(value, "value"); - checkState(key.equals(key.toLowerCase(Locale.ROOT)), "key must be lowercase"); + checkState(key.equals(key.toLowerCase(Locale.ROOT)), "key must be lowercase: %s", key); checkState(!this.map.containsKey(key), "key '%s' already has an associated %s", key, this.name); this.map.put(key, value); return value; diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java index 6131be919a..789bdc4d39 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/McRegionReader.java @@ -126,7 +126,7 @@ public synchronized InputStream getChunkInputStream(BlockVector2 position) throw // The chunk hasn't been generated if (offset == 0) { - throw new DataException("The chunk at " + x + "," + z + " is not generated"); + throw new DataException("The chunk at " + position + " is not generated"); } int sectorNumber = offset >> 8; diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index ab6386f34b..41b8436b11 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -6,6 +6,7 @@ "worldedit.biomeinfo.lineofsight": "Biomes at line of sight point: {0}", "worldedit.biomeinfo.position": "Biomes at your position: {0}", "worldedit.biomeinfo.selection": "Biomes in your selection: {0}", + "worldedit.biomeinfo.not-locatable": "Command sender must be present in the world to use the -p flag.", "worldedit.brush.radius-too-large": "Maximum allowed brush radius: {0}", "worldedit.brush.apply.description": "Apply brush, apply a function to every block", @@ -41,6 +42,7 @@ "worldedit.setbiome.changed": "Biomes were changed for approximately {0} blocks.", "worldedit.setbiome.warning": "You may have to re-join your game (or close and re-open your world) to see changes.", + "worldedit.setbiome.not-locatable": "Command sender must be present in the world to use the -p flag.", "worldedit.drawsel.disabled": "Server CUI disabled.", "worldedit.drawsel.enabled": "Server CUI enabled. This only supports cuboid regions, with a maximum size of {0}x{1}x{2}.", @@ -86,6 +88,7 @@ "worldedit.clearhistory.cleared": "History cleared.", "worldedit.raytrace.noblock": "No block in sight!", + "worldedit.raytrace.require-player": "Raytracing commands require a player!", "worldedit.restore.not-configured": "Snapshot/backup restore is not configured.", "worldedit.restore.not-available": "That snapshot does not exist or is not available.", diff --git a/worldedit-fabric/build.gradle.kts b/worldedit-fabric/build.gradle.kts index f7b1525882..bff20558f7 100644 --- a/worldedit-fabric/build.gradle.kts +++ b/worldedit-fabric/build.gradle.kts @@ -21,8 +21,8 @@ applyShadowConfiguration() apply(plugin = "fabric-loom") apply(plugin = "java-library") -val minecraftVersion = "1.18.1" -val loaderVersion = "0.12.9" +val minecraftVersion = "1.18.2" +val loaderVersion = "0.13.3" val fabricApiConfiguration: Configuration = configurations.create("fabricApi") @@ -48,7 +48,7 @@ dependencies { "modImplementation"("net.fabricmc:fabric-loader:$loaderVersion") // [1] declare fabric-api dependency... - "fabricApi"("net.fabricmc.fabric-api:fabric-api:0.44.0+1.18") + "fabricApi"("net.fabricmc.fabric-api:fabric-api:0.47.9+1.18.2") // [2] Load the API dependencies from the fabric mod json... @Suppress("UNCHECKED_CAST") @@ -58,6 +58,7 @@ dependencies { val wantedDependencies = (fabricModJson["depends"] ?: error("no depends in fabric.mod.json")).keys .filter { it == "fabric-api-base" || it.contains(Regex("v\\d$")) } .map { "net.fabricmc.fabric-api:$it" } + .toSet() logger.lifecycle("Looking for these dependencies:") for (wantedDependency in wantedDependencies) { logger.lifecycle(wantedDependency) @@ -103,7 +104,7 @@ dependencies { "annotationProcessor"("net.fabricmc:fabric-loom:${project.versions.loom}") // Silence some warnings, since apparently this isn't on the compile classpath like it should be. - "compileOnly"("com.google.errorprone:error_prone_annotations:2.10.0") + "compileOnly"("com.google.errorprone:error_prone_annotations:2.11.0") } configure { @@ -139,10 +140,9 @@ tasks.named("shadowJar") { tasks.register("remapShadowJar") { val shadowJar = tasks.getByName("shadowJar") dependsOn(shadowJar) - input.set(shadowJar.archiveFile) + inputFile.set(shadowJar.archiveFile) archiveFileName.set(shadowJar.archiveFileName.get().replace(Regex("-dev\\.jar$"), ".jar")) addNestedDependencies.set(true) - remapAccessWidener.set(true) } tasks.named("assemble").configure { diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java index b9d9c06413..5c80a7c66a 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java @@ -21,22 +21,22 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.registry.BlockCategoryRegistry; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.Tag; +import net.minecraft.tags.TagKey; -import java.util.Collections; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; public class FabricBlockCategoryRegistry implements BlockCategoryRegistry { @Override public Set getCategorisedByName(String category) { - return Optional.ofNullable(BlockTags.getAllTags().getTag(new ResourceLocation(category))) - .map(Tag::getValues) - .orElse(Collections.emptyList()) + return Registry.BLOCK.getTag(TagKey.create(Registry.BLOCK_REGISTRY, new ResourceLocation(category))) .stream() + .flatMap(HolderSet.Named::stream) + .map(Holder::value) .map(FabricAdapter::adapt) .collect(Collectors.toSet()); } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java index 51ab18ea77..f60c0667e2 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java @@ -50,14 +50,13 @@ class FabricEntity implements Entity { @Override public BaseEntity getState() { net.minecraft.world.entity.Entity entity = entityRef.get(); - if (entity != null) { - ResourceLocation id = Registry.ENTITY_TYPE.getKey(entity.getType()); - CompoundTag tag = new CompoundTag(); - entity.saveWithoutId(tag); - return new BaseEntity(EntityTypes.get(id.toString()), LazyReference.from(() -> NBTConverter.fromNative(tag))); - } else { + if (entity == null || entity.isPassenger()) { return null; } + ResourceLocation id = Registry.ENTITY_TYPE.getKey(entity.getType()); + CompoundTag tag = new CompoundTag(); + entity.saveWithoutId(tag); + return new BaseEntity(EntityTypes.get(id.toString()), LazyReference.from(() -> NBTConverter.fromNative(tag))); } @Override diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java index b71ed33d81..e43544361c 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java @@ -21,22 +21,22 @@ import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.ItemCategoryRegistry; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.ItemTags; -import net.minecraft.tags.Tag; +import net.minecraft.tags.TagKey; -import java.util.Collections; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; public class FabricItemCategoryRegistry implements ItemCategoryRegistry { @Override public Set getCategorisedByName(String category) { - return Optional.ofNullable(ItemTags.getAllTags().getTag(new ResourceLocation(category))) - .map(Tag::getValues) - .orElse(Collections.emptyList()) + return Registry.ITEM.getTag(TagKey.create(Registry.ITEM_REGISTRY, new ResourceLocation(category))) .stream() + .flatMap(HolderSet.Named::stream) + .map(Holder::value) .map(FabricAdapter::adapt) .collect(Collectors.toSet()); } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java index 29199e413c..c5a12f6145 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java @@ -26,7 +26,7 @@ import com.google.common.collect.Sets; import com.google.common.collect.Streams; import com.google.common.util.concurrent.Futures; -import com.mojang.serialization.Dynamic; +import com.sk89q.jnbt.NBTConstants; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; @@ -37,8 +37,12 @@ import com.sk89q.worldedit.fabric.internal.ExtendedMinecraftServer; import com.sk89q.worldedit.fabric.internal.FabricWorldNativeAccess; import com.sk89q.worldedit.fabric.internal.NBTConverter; +import com.sk89q.worldedit.fabric.mixin.AccessorDerivedLevelData; import com.sk89q.worldedit.fabric.mixin.AccessorPrimaryLevelData; import com.sk89q.worldedit.fabric.mixin.AccessorServerChunkCache; +import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Mask2D; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; @@ -63,13 +67,10 @@ import com.sk89q.worldedit.world.weather.WeatherTypes; import net.minecraft.Util; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.data.worldgen.features.EndFeatures; import net.minecraft.data.worldgen.features.TreeFeatures; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.RegistryReadOps; -import net.minecraft.resources.RegistryWriteOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; @@ -85,6 +86,7 @@ import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkSource; @@ -106,11 +108,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.OptionalLong; import java.util.Random; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -221,7 +223,9 @@ public BiomeType getBiome(BlockVector3 position) { } private BiomeType getBiomeInChunk(BlockVector3 position, ChunkAccess chunk) { - return FabricAdapter.adapt(chunk.getNoiseBiome(position.getX() >> 2, position.getY() >> 2, position.getZ() >> 2)); + return FabricAdapter.adapt( + chunk.getNoiseBiome(position.getX() >> 2, position.getY() >> 2, position.getZ() >> 2).value() + ); } @Override @@ -230,10 +234,12 @@ public boolean setBiome(BlockVector3 position, BiomeType biome) { checkNotNull(biome); ChunkAccess chunk = getWorld().getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4); - PalettedContainer biomeArray = chunk.getSection(chunk.getSectionIndex(position.getY())).getBiomes(); + PalettedContainer> biomeArray = chunk.getSection(chunk.getSectionIndex(position.getY())).getBiomes(); biomeArray.getAndSetUnchecked( position.getX() & 3, position.getY() & 3, position.getZ() & 3, - FabricAdapter.adapt(biome) + getWorld().registryAccess().registry(Registry.BIOME_REGISTRY) + .orElseThrow() + .getHolderOrThrow(ResourceKey.create(Registry.BIOME_REGISTRY, new ResourceLocation(biome.getId()))) ); chunk.setUnsaved(true); return true; @@ -317,13 +323,17 @@ private void doRegen(Region region, Extent extent, RegenOptions options) throws LevelStorageSource levelStorage = LevelStorageSource.createDefault(tempDir); try (LevelStorageSource.LevelStorageAccess session = levelStorage.createAccess("WorldEditTempGen")) { ServerLevel originalWorld = (ServerLevel) getWorld(); - AccessorPrimaryLevelData levelProperties = (AccessorPrimaryLevelData) - originalWorld.getLevelData(); + AccessorPrimaryLevelData levelProperties; + if (originalWorld.getLevelData() instanceof AccessorDerivedLevelData derivedLevelData) { + levelProperties = (AccessorPrimaryLevelData) derivedLevelData.getWrapped(); + } else { + levelProperties = (AccessorPrimaryLevelData) originalWorld.getLevelData(); + } WorldGenSettings originalOpts = levelProperties.worldGenSettings(); long seed = options.getSeed().orElse(originalWorld.getSeed()); WorldGenSettings newOpts = options.getSeed().isPresent() - ? replaceSeed(originalWorld, seed, originalOpts) + ? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) : originalOpts; levelProperties.setWorldGenSettings(newOpts); @@ -334,7 +344,7 @@ private void doRegen(Region region, Extent extent, RegenOptions options) throws originalWorld.getServer(), Util.backgroundExecutor(), session, ((ServerLevelData) originalWorld.getLevelData()), worldRegKey, - originalWorld.dimensionType(), + originalWorld.dimensionTypeRegistration(), new WorldEditGenListener(), dimGenOpts.generator(), originalWorld.isDebug(), @@ -358,49 +368,6 @@ private void doRegen(Region region, Extent extent, RegenOptions options) throws } } - private WorldGenSettings replaceSeed(ServerLevel originalWorld, long seed, WorldGenSettings originalOpts) { - RegistryWriteOps nbtRegReadOps = RegistryWriteOps.create( - NbtOps.INSTANCE, - originalWorld.getServer().registryAccess() - ); - RegistryReadOps nbtRegOps = RegistryReadOps.create( - NbtOps.INSTANCE, - ((ExtendedMinecraftServer) originalWorld.getServer()) - .getResources().getResourceManager(), - originalWorld.getServer().registryAccess() - ); - return WorldGenSettings.CODEC - .encodeStart(nbtRegReadOps, originalOpts) - .flatMap(tag -> - WorldGenSettings.CODEC.parse( - recursivelySetSeed(new Dynamic<>(nbtRegOps, tag), seed, new HashSet<>()) - ) - ) - .get() - .map( - l -> l, - error -> { - throw new IllegalStateException("Unable to map GeneratorOptions: " + error.message()); - } - ); - } - - @SuppressWarnings("unchecked") - private Dynamic recursivelySetSeed(Dynamic dynamic, long seed, Set> seen) { - if (!seen.add(dynamic)) { - return dynamic; - } - return dynamic.updateMapValues(pair -> { - if (pair.getFirst().asString("").equals("seed")) { - return pair.mapSecond(v -> v.createLong(seed)); - } - if (pair.getSecond().getValue() instanceof net.minecraft.nbt.CompoundTag) { - return pair.mapSecond(v -> recursivelySetSeed((Dynamic) v, seed, seen)); - } - return pair; - }); - } - private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld, RegenOptions options) throws WorldEditException { List> chunkLoadings = submitChunkLoadTasks(region, serverWorld); @@ -457,7 +424,7 @@ private List> submitChunkLoadTasks(Region region, } @Nullable - private static ConfiguredFeature createTreeFeatureGenerator(TreeType type) { + private static Holder> createTreeFeatureGenerator(TreeType type) { return switch (type) { // Based off of the SaplingGenerator class, as well as uses of DefaultBiomeFeatures fields case TREE -> TreeFeatures.OAK; @@ -486,7 +453,9 @@ private List> submitChunkLoadTasks(Region region, @Override public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position) { - ConfiguredFeature generator = createTreeFeatureGenerator(type); + ConfiguredFeature generator = Optional.ofNullable(createTreeFeatureGenerator(type)) + .map(Holder::value) + .orElse(null); ServerLevel world = (ServerLevel) getWorld(); ServerChunkCache chunkManager = world.getChunkSource(); if (type == TreeType.CHORUS_PLANT) { @@ -664,29 +633,63 @@ public List getEntities() { @Nullable @Override public Entity createEntity(Location location, BaseEntity entity) { - Level world = getWorld(); - final Optional> entityType = EntityType.byString(entity.getType().getId()); + ServerLevel world = (ServerLevel) getWorld(); + String entityId = entity.getType().getId(); + final Optional> entityType = EntityType.byString(entityId); if (entityType.isEmpty()) { return null; } - net.minecraft.world.entity.Entity createdEntity = entityType.get().create(world); + CompoundBinaryTag nativeTag = entity.getNbt(); + net.minecraft.nbt.CompoundTag tag; + if (nativeTag != null) { + tag = NBTConverter.toNative(nativeTag); + removeUnwantedEntityTagsRecursively(tag); + } else { + tag = new net.minecraft.nbt.CompoundTag(); + } + tag.putString("id", entityId); + + net.minecraft.world.entity.Entity createdEntity = EntityType.loadEntityRecursive(tag, world, (loadedEntity) -> { + loadedEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + return loadedEntity; + }); if (createdEntity != null) { - CompoundBinaryTag nativeTag = entity.getNbt(); - if (nativeTag != null) { - net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(nativeTag); - for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - createdEntity.load(tag); - } + world.addFreshEntityWithPassengers(createdEntity); + return new FabricEntity(createdEntity); + } + return null; + } - createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } - world.addFreshEntity(createdEntity); - return new FabricEntity(createdEntity); - } else { - return null; + // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive + if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) { + net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND); + + for (int i = 0; i < nbttaglist.size(); ++i) { + removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); + } } } + @Override + public Mask createLiquidMask() { + return new AbstractExtentMask(this) { + @Override + public boolean test(BlockVector3 vector) { + return FabricAdapter.adapt(getExtent().getBlock(vector)).getBlock() instanceof LiquidBlock; + } + + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } + }; + } + + } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java index 7950821ff4..f3be6ea106 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java @@ -60,8 +60,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.network.ServerGamePacketListenerImpl; -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResultHolder; @@ -83,7 +82,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.worldedit.fabric.FabricAdapter.adaptPlayer; import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME; -import static java.util.stream.Collectors.toList; /** * The Fabric implementation of WorldEdit. @@ -163,7 +161,7 @@ private void registerCommands(CommandDispatcher dispatcher, } List commands = manager.getPlatformCommandManager().getCommandManager() - .getAllCommands().collect(toList()); + .getAllCommands().toList(); for (Command command : commands) { CommandWrapper.register(dispatcher, command); Set perms = command.getCondition().as(PermissionCondition.class) @@ -212,16 +210,16 @@ private void setupRegistries(MinecraftServer server) { } } // Tags - for (ResourceLocation name : BlockTags.getAllTags().getAvailableTags()) { + Registry.BLOCK.getTagNames().map(TagKey::location).forEach(name -> { if (BlockCategory.REGISTRY.get(name.toString()) == null) { BlockCategory.REGISTRY.register(name.toString(), new BlockCategory(name.toString())); } - } - for (ResourceLocation name : ItemTags.getAllTags().getAvailableTags()) { + }); + Registry.ITEM.getTagNames().map(TagKey::location).forEach(name -> { if (ItemCategory.REGISTRY.get(name.toString()) == null) { ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(name.toString())); } - } + }); } private void onStartingServer(MinecraftServer minecraftServer) { diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/ExtendedMinecraftServer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/ExtendedMinecraftServer.java index 0a476fffdb..22376275a2 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/ExtendedMinecraftServer.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/ExtendedMinecraftServer.java @@ -19,7 +19,6 @@ package com.sk89q.worldedit.fabric.internal; -import net.minecraft.server.ServerResources; import net.minecraft.world.level.Level; import java.nio.file.Path; @@ -28,6 +27,4 @@ public interface ExtendedMinecraftServer { Path getStoragePath(Level world); - ServerResources getResources(); - } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricTransmogrifier.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricTransmogrifier.java index fec99ee384..e29956ba48 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricTransmogrifier.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricTransmogrifier.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.fabric.internal; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.fabric.FabricAdapter; import com.sk89q.worldedit.registry.state.BooleanProperty; @@ -44,26 +47,34 @@ */ public class FabricTransmogrifier { - public static Property transmogToWorldEditProperty(net.minecraft.world.level.block.state.properties.Property property) { - if (property instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { - return new BooleanProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.BooleanProperty) property).getPossibleValues())); - } - if (property instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { - return new IntegerProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.IntegerProperty) property).getPossibleValues())); - } - if (property instanceof DirectionProperty) { - return new DirectionalProperty(property.getName(), ((DirectionProperty) property).getPossibleValues().stream() - .map(FabricAdapter::adaptEnumFacing) - .collect(Collectors.toList())); - } - if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - // Note: do not make x.asString a method reference. - // It will cause runtime bootstrap exceptions. - return new EnumProperty(property.getName(), ((net.minecraft.world.level.block.state.properties.EnumProperty) property).getPossibleValues().stream() - .map(x -> x.getSerializedName()) - .collect(Collectors.toList())); + private static final LoadingCache, Property> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { + @Override + public Property load(net.minecraft.world.level.block.state.properties.Property property) throws Exception { + if (property instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { + return new BooleanProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.BooleanProperty) property).getPossibleValues())); + } + if (property instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { + return new IntegerProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.IntegerProperty) property).getPossibleValues())); + } + if (property instanceof DirectionProperty) { + return new DirectionalProperty(property.getName(), ((DirectionProperty) property).getPossibleValues().stream() + .map(FabricAdapter::adaptEnumFacing) + .collect(Collectors.toList())); + } + if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + // Note: do not make x.asString a method reference. + // It will cause runtime bootstrap exceptions. + //noinspection Convert2MethodRef + return new EnumProperty(property.getName(), ((net.minecraft.world.level.block.state.properties.EnumProperty) property).getPossibleValues().stream() + .map(x -> x.getSerializedName()) + .collect(Collectors.toList())); + } + return new PropertyAdapter<>(property); } - return new PropertyAdapter<>(property); + }); + + public static Property transmogToWorldEditProperty(net.minecraft.world.level.block.state.properties.Property property) { + return PROPERTY_CACHE.getUnchecked(property); } private static Map, Object> transmogToWorldEditProperties(BlockType block, Map, Comparable> mcProps) { diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java index ef1a134717..f1295c0ab5 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricWorldNativeAccess.java @@ -142,6 +142,12 @@ public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newSta } } + @Override + public void updateBlock(BlockPos pos, BlockState oldState, BlockState newState) { + Level world = getWorld(); + newState.onPlace(world, pos, oldState, false); + } + @Override public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState, int recursionLimit) { Level world = getWorld(); diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/AccessorDerivedLevelData.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/AccessorDerivedLevelData.java new file mode 100644 index 0000000000..8af17bc613 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/AccessorDerivedLevelData.java @@ -0,0 +1,32 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.mixin; + +import net.minecraft.world.level.storage.DerivedLevelData; +import net.minecraft.world.level.storage.ServerLevelData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(DerivedLevelData.class) +public interface AccessorDerivedLevelData extends ServerLevelData { + + @Accessor + ServerLevelData getWrapped(); +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java index 17ae3eea91..c2fa3b572f 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinMinecraftServer.java @@ -23,13 +23,11 @@ import com.sk89q.worldedit.fabric.internal.ExtendedMinecraftServer; import net.minecraft.Util; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.ServerResources; import net.minecraft.world.level.Level; import net.minecraft.world.level.storage.LevelStorageSource; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.gen.Accessor; import java.nio.file.Path; @@ -52,7 +50,4 @@ public Path getStoragePath(Level world) { return storageSource.getDimensionPath(world.dimension()); } - @Accessor - @Override - public abstract ServerResources getResources(); } diff --git a/worldedit-fabric/src/main/resources/fabric.mod.json b/worldedit-fabric/src/main/resources/fabric.mod.json index d380affc9f..81174ad6b9 100644 --- a/worldedit-fabric/src/main/resources/fabric.mod.json +++ b/worldedit-fabric/src/main/resources/fabric.mod.json @@ -39,7 +39,7 @@ "fabric-permissions-api-v0": "*" }, "mixins": [ - "worldedit.mixins.json" + "worldedit-fabric.mixins.json" ], "accessWidener" : "worldedit.accesswidener" } diff --git a/worldedit-fabric/src/main/resources/worldedit.mixins.json b/worldedit-fabric/src/main/resources/worldedit-fabric.mixins.json similarity index 93% rename from worldedit-fabric/src/main/resources/worldedit.mixins.json rename to worldedit-fabric/src/main/resources/worldedit-fabric.mixins.json index 37f3c7809b..076670106b 100644 --- a/worldedit-fabric/src/main/resources/worldedit.mixins.json +++ b/worldedit-fabric/src/main/resources/worldedit-fabric.mixins.json @@ -5,6 +5,7 @@ "mixins": [ "AccessorClientboundBlockEntityDataPacket", "AccessorPrimaryLevelData", + "AccessorDerivedLevelData", "AccessorServerChunkCache", "MixinLevelChunkSetBlockHook", "MixinMinecraftServer", diff --git a/worldedit-forge/build.gradle.kts b/worldedit-forge/build.gradle.kts index c9fb12b1cc..1109aa3a10 100644 --- a/worldedit-forge/build.gradle.kts +++ b/worldedit-forge/build.gradle.kts @@ -5,17 +5,18 @@ import net.minecraftforge.gradle.userdev.tasks.RenameJarInPlace plugins { id("net.minecraftforge.gradle") + id("org.spongepowered.mixin") `java-library` } applyPlatformAndCoreConfiguration(javaRelease = 17) applyShadowConfiguration() -val minecraftVersion = "1.18.1" +val minecraftVersion = "1.18.2" val nextMajorMinecraftVersion: String = minecraftVersion.split('.').let { (useless, major) -> "$useless.${major.toInt() + 1}" } -val forgeVersion = "39.0.0" +val forgeVersion = "40.0.18" val apiClasspath = configurations.create("apiClasspath") { isCanBeResolved = true @@ -29,6 +30,7 @@ dependencies { }) "minecraft"("net.minecraftforge:forge:$minecraftVersion-$forgeVersion") + "annotationProcessor"("org.spongepowered:mixin:0.8.5:processor") } configure { @@ -54,6 +56,11 @@ configure { } +configure { + add(sourceSets["main"], "worldedit-forge.mixins.refmap.json") + config("worldedit-forge.mixins.json") +} + configure { archivesBaseName = "$archivesBaseName-mc$minecraftVersion" } @@ -158,7 +165,5 @@ tasks.named("shadowJar") { } } -afterEvaluate { - val reobf = extensions.getByName>("reobf") - reobf.create("shadowJar") -} +val reobf = extensions.getByName>("reobf") +reobf.create("shadowJar") diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeBlockCategoryRegistry.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeBlockCategoryRegistry.java index ecf819b015..b613cd882c 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeBlockCategoryRegistry.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeBlockCategoryRegistry.java @@ -19,22 +19,32 @@ package com.sk89q.worldedit.forge; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.registry.BlockCategoryRegistry; +import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.Tag; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.block.Block; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.tags.ITagManager; -import java.util.Collections; -import java.util.Optional; +import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; public class ForgeBlockCategoryRegistry implements BlockCategoryRegistry { @Override public Set getCategorisedByName(String category) { - return Optional.ofNullable(BlockTags.getAllTags().getTag(new ResourceLocation(category))) - .map(Tag::getValues).orElse(Collections.emptyList()) - .stream().map(ForgeAdapter::adapt).collect(Collectors.toSet()); + ITagManager tags = Objects.requireNonNull( + ForgeRegistries.BLOCKS.tags(), "no block tags registry" + ); + return tags + .getTag(TagKey.create( + Registry.BLOCK_REGISTRY, + new ResourceLocation(category) + )) + .stream() + .map(ForgeAdapter::adapt) + .collect(ImmutableSet.toImmutableSet()); } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java index fd5f992d13..47690688b7 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeEntity.java @@ -50,21 +50,16 @@ class ForgeEntity implements Entity { @Override public BaseEntity getState() { net.minecraft.world.entity.Entity entity = entityRef.get(); - if (entity != null) { - ResourceLocation id = entity.getType().getRegistryName(); - if (id != null) { - CompoundTag tag = new CompoundTag(); - entity.saveWithoutId(tag); - return new BaseEntity( - EntityTypes.get(id.toString()), - LazyReference.from(() -> NBTConverter.fromNative(tag)) - ); - } else { - return null; - } - } else { + if (entity == null || entity.isPassenger()) { + return null; + } + ResourceLocation id = entity.getType().getRegistryName(); + if (id == null) { return null; } + CompoundTag tag = new CompoundTag(); + entity.saveWithoutId(tag); + return new BaseEntity(EntityTypes.get(id.toString()), LazyReference.from(() -> NBTConverter.fromNative(tag))); } @Override diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeItemCategoryRegistry.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeItemCategoryRegistry.java index a060994101..79191caff2 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeItemCategoryRegistry.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeItemCategoryRegistry.java @@ -19,22 +19,32 @@ package com.sk89q.worldedit.forge; +import com.google.common.collect.ImmutableSet; import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.registry.ItemCategoryRegistry; +import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.ItemTags; -import net.minecraft.tags.Tag; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraftforge.registries.ForgeRegistries; +import net.minecraftforge.registries.tags.ITagManager; -import java.util.Collections; -import java.util.Optional; +import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; public class ForgeItemCategoryRegistry implements ItemCategoryRegistry { @Override public Set getCategorisedByName(String category) { - return Optional.ofNullable(ItemTags.getAllTags().getTag(new ResourceLocation(category))) - .map(Tag::getValues).orElse(Collections.emptyList()) - .stream().map(ForgeAdapter::adapt).collect(Collectors.toSet()); + ITagManager tags = Objects.requireNonNull( + ForgeRegistries.ITEMS.tags(), "no item tags registry" + ); + return tags + .getTag(TagKey.create( + Registry.ITEM_REGISTRY, + new ResourceLocation(category) + )) + .stream() + .map(ForgeAdapter::adapt) + .collect(ImmutableSet.toImmutableSet()); } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java index 17c27c299b..dc9e986690 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlatform.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.forge; +import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.command.util.PermissionCondition; @@ -28,6 +29,7 @@ import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; +import com.sk89q.worldedit.forge.internal.ExtendedChunk; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.util.io.ResourceLoader; import com.sk89q.worldedit.world.DataFixer; @@ -41,6 +43,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.PlayerList; +import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.storage.ServerLevelData; import net.minecraftforge.server.ServerLifecycleHooks; import org.enginehub.piston.Command; @@ -217,7 +220,7 @@ public Map getCapabilities() { return capabilities; } - private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + private static final Set SUPPORTED_SIDE_EFFECTS_NO_MIXIN = Sets.immutableEnumSet( SideEffect.VALIDATION, SideEffect.ENTITY_AI, SideEffect.LIGHTING, @@ -225,9 +228,15 @@ public Map getCapabilities() { SideEffect.EVENTS ); + private static final Set SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( + Iterables.concat(SUPPORTED_SIDE_EFFECTS_NO_MIXIN, Collections.singleton(SideEffect.UPDATE)) + ); + @Override public Set getSupportedSideEffects() { - return SUPPORTED_SIDE_EFFECTS; + return ExtendedChunk.class.isAssignableFrom(LevelChunk.class) + ? SUPPORTED_SIDE_EFFECTS + : SUPPORTED_SIDE_EFFECTS_NO_MIXIN; } @Override diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java index cd8c4d5917..3376da2f9f 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -23,12 +23,10 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.google.common.collect.Streams; import com.google.common.util.concurrent.Futures; -import com.mojang.serialization.Codec; -import com.mojang.serialization.Dynamic; +import com.sk89q.jnbt.NBTConstants; import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; @@ -39,8 +37,10 @@ import com.sk89q.worldedit.forge.internal.ForgeWorldNativeAccess; import com.sk89q.worldedit.forge.internal.NBTConverter; import com.sk89q.worldedit.forge.internal.TileEntityUtils; +import com.sk89q.worldedit.function.mask.AbstractExtentMask; +import com.sk89q.worldedit.function.mask.Mask; +import com.sk89q.worldedit.function.mask.Mask2D; import com.sk89q.worldedit.internal.Constants; -import com.sk89q.worldedit.internal.util.BiomeMath; import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; @@ -64,13 +64,10 @@ import com.sk89q.worldedit.world.weather.WeatherTypes; import net.minecraft.Util; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.data.worldgen.features.EndFeatures; import net.minecraft.data.worldgen.features.TreeFeatures; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.RegistryReadOps; -import net.minecraft.resources.RegistryWriteOps; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerChunkCache; @@ -86,6 +83,7 @@ import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkStatus; @@ -106,11 +104,11 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.OptionalLong; import java.util.Random; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -219,7 +217,9 @@ public BiomeType getBiome(BlockVector3 position) { } private BiomeType getBiomeInChunk(BlockVector3 position, ChunkAccess chunk) { - return ForgeAdapter.adapt(chunk.getNoiseBiome(position.getX() >> 2, position.getY() >> 2, position.getZ() >> 2)); + return ForgeAdapter.adapt( + chunk.getNoiseBiome(position.getX() >> 2, position.getY() >> 2, position.getZ() >> 2).value() + ); } @Override @@ -228,10 +228,12 @@ public boolean setBiome(BlockVector3 position, BiomeType biome) { checkNotNull(biome); LevelChunk chunk = getWorld().getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4); - PalettedContainer biomes = chunk.getSection(chunk.getSectionIndex(position.getY())).getBiomes(); + PalettedContainer> biomes = chunk.getSection(chunk.getSectionIndex(position.getY())).getBiomes(); biomes.getAndSetUnchecked( position.getX() & 3, position.getY() & 3, position.getZ() & 3, - ForgeAdapter.adapt(biome) + getWorld().registryAccess().registry(Registry.BIOME_REGISTRY) + .orElseThrow() + .getHolderOrThrow(ResourceKey.create(Registry.BIOME_REGISTRY, new ResourceLocation(biome.getId()))) ); chunk.setUnsaved(true); return true; @@ -320,7 +322,7 @@ private void doRegen(Region region, Extent extent, RegenOptions options) throws long seed = options.getSeed().orElse(originalWorld.getSeed()); WorldGenSettings newOpts = options.getSeed().isPresent() - ? replaceSeed(originalWorld, seed, originalOpts) + ? originalOpts.withSeed(levelProperties.isHardcore(), OptionalLong.of(seed)) : originalOpts; levelProperties.worldGenSettings = newOpts; @@ -331,7 +333,7 @@ private void doRegen(Region region, Extent extent, RegenOptions options) throws originalWorld.getServer(), Util.backgroundExecutor(), session, ((ServerLevelData) originalWorld.getLevelData()), worldRegKey, - originalWorld.dimensionType(), + originalWorld.dimensionTypeRegistration(), new WorldEditGenListener(), dimGenOpts.generator(), originalWorld.isDebug(), @@ -355,49 +357,6 @@ private void doRegen(Region region, Extent extent, RegenOptions options) throws } } - private WorldGenSettings replaceSeed(ServerLevel originalWorld, long seed, WorldGenSettings originalOpts) { - RegistryWriteOps nbtReadRegOps = RegistryWriteOps.create( - NbtOps.INSTANCE, - originalWorld.getServer().registryAccess() - ); - RegistryReadOps nbtRegOps = RegistryReadOps.createAndLoad( - NbtOps.INSTANCE, - originalWorld.getServer().getResourceManager(), - originalWorld.getServer().registryAccess() - ); - Codec dimCodec = WorldGenSettings.CODEC; - return dimCodec - .encodeStart(nbtReadRegOps, originalOpts) - .flatMap(tag -> - dimCodec.parse( - recursivelySetSeed(new Dynamic<>(nbtRegOps, tag), seed, new HashSet<>()) - ) - ) - .get() - .map( - l -> l, - error -> { - throw new IllegalStateException("Unable to map GeneratorOptions: " + error.message()); - } - ); - } - - @SuppressWarnings("unchecked") - private Dynamic recursivelySetSeed(Dynamic dynamic, long seed, Set> seen) { - if (!seen.add(dynamic)) { - return dynamic; - } - return dynamic.updateMapValues(pair -> { - if (pair.getFirst().asString("").equals("seed")) { - return pair.mapSecond(v -> v.createLong(seed)); - } - if (pair.getSecond().getValue() instanceof net.minecraft.nbt.CompoundTag) { - return pair.mapSecond(v -> recursivelySetSeed((Dynamic) v, seed, seen)); - } - return pair; - }); - } - private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld, RegenOptions options) throws WorldEditException { List> chunkLoadings = submitChunkLoadTasks(region, serverWorld); @@ -428,9 +387,7 @@ private void regenForWorld(Region region, Extent extent, ServerLevel serverWorld BlockStateHolder state = ForgeAdapter.adapt(chunk.getBlockState(pos)); BlockEntity blockEntity = chunk.getBlockEntity(pos); if (blockEntity != null) { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - blockEntity.save(tag); - state = state.toBaseBlock(LazyReference.from(() -> NBTConverter.fromNative(tag))); + state = state.toBaseBlock(LazyReference.from(() -> NBTConverter.fromNative(blockEntity.saveWithFullMetadata()))); } extent.setBlock(vec, state.toBaseBlock()); @@ -454,7 +411,7 @@ private List> submitChunkLoadTasks(Region region, } @Nullable - private static ConfiguredFeature createTreeFeatureGenerator(TreeType type) { + private static Holder> createTreeFeatureGenerator(TreeType type) { return switch (type) { case TREE -> TreeFeatures.OAK; case BIG_TREE -> TreeFeatures.FANCY_OAK; @@ -482,7 +439,9 @@ private List> submitChunkLoadTasks(Region region, @Override public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position) { - ConfiguredFeature generator = createTreeFeatureGenerator(type); + ConfiguredFeature generator = Optional.ofNullable(createTreeFeatureGenerator(type)) + .map(Holder::value) + .orElse(null); ServerLevel world = getWorld(); ServerChunkCache chunkManager = world.getChunkSource(); if (type == TreeType.CHORUS_PLANT) { @@ -572,7 +531,7 @@ public int getMinY() { @Override public int getMaxY() { - return getWorld().getHeight() - 1; + return getWorld().getMaxBuildHeight() - 1; } @Override @@ -618,8 +577,7 @@ public int hashCode() { public boolean equals(Object o) { if (o == null) { return false; - } else if ((o instanceof ForgeWorld)) { - ForgeWorld other = ((ForgeWorld) o); + } else if ((o instanceof ForgeWorld other)) { Level otherWorld = other.worldRef.get(); Level thisWorld = worldRef.get(); return otherWorld != null && otherWorld.equals(thisWorld); @@ -642,47 +600,75 @@ public List getEntities(Region region) { box, e -> region.contains(ForgeAdapter.adapt(e.blockPosition())) ); - return ImmutableList.copyOf(Lists.transform( - nmsEntities, - ForgeEntity::new - )); + return nmsEntities.stream().map(ForgeEntity::new).collect(ImmutableList.toImmutableList()); } @Override public List getEntities() { final ServerLevel world = getWorld(); - return ImmutableList.copyOf(Iterables.transform( - world.getAllEntities(), - ForgeEntity::new - )); + return Streams.stream(world.getAllEntities()) + .map(ForgeEntity::new) + .collect(ImmutableList.toImmutableList()); } @Nullable @Override public Entity createEntity(Location location, BaseEntity entity) { ServerLevel world = getWorld(); - final Optional> entityType = EntityType.byString(entity.getType().getId()); - if (!entityType.isPresent()) { + String entityId = entity.getType().getId(); + final Optional> entityType = EntityType.byString(entityId); + if (entityType.isEmpty()) { return null; } - net.minecraft.world.entity.Entity createdEntity = entityType.get().create(world); + CompoundBinaryTag nativeTag = entity.getNbt(); + net.minecraft.nbt.CompoundTag tag; + if (nativeTag != null) { + tag = NBTConverter.toNative(nativeTag); + removeUnwantedEntityTagsRecursively(tag); + } else { + tag = new net.minecraft.nbt.CompoundTag(); + } + tag.putString("id", entityId); + + net.minecraft.world.entity.Entity createdEntity = EntityType.loadEntityRecursive(tag, world, (loadedEntity) -> { + loadedEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + return loadedEntity; + }); if (createdEntity != null) { - CompoundBinaryTag nativeTag = entity.getNbt(); - if (nativeTag != null) { - net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(nativeTag); - for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { - tag.remove(name); - } - createdEntity.load(tag); - } + world.addFreshEntityWithPassengers(createdEntity); + return new ForgeEntity(createdEntity); + } + return null; + } - createdEntity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + private void removeUnwantedEntityTagsRecursively(net.minecraft.nbt.CompoundTag tag) { + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } - world.addFreshEntity(createdEntity); - return new ForgeEntity(createdEntity); - } else { - return null; + // Adapted from net.minecraft.world.entity.EntityType#loadEntityRecursive + if (tag.contains("Passengers", NBTConstants.TYPE_LIST)) { + net.minecraft.nbt.ListTag nbttaglist = tag.getList("Passengers", NBTConstants.TYPE_COMPOUND); + + for (int i = 0; i < nbttaglist.size(); ++i) { + removeUnwantedEntityTagsRecursively(nbttaglist.getCompound(i)); + } } } + @Override + public Mask createLiquidMask() { + return new AbstractExtentMask(this) { + @Override + public boolean test(BlockVector3 vector) { + return ForgeAdapter.adapt(getExtent().getBlock(vector)).getBlock() instanceof LiquidBlock; + } + + @Nullable + @Override + public Mask2D toMask2D() { + return null; + } + }; + } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 1287e7f2b4..3cba00de79 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -50,8 +50,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.ItemTags; +import net.minecraft.tags.TagKey; import net.minecraft.world.InteractionHand; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.CommandEvent; @@ -173,6 +172,8 @@ private void setupPlatform() { // } } + // TODO clean this up once Forge adds a proper API for this + @SuppressWarnings("deprecation") private void setupRegistries(MinecraftServer server) { // Blocks for (ResourceLocation name : ForgeRegistries.BLOCKS.getKeys()) { @@ -200,16 +201,16 @@ private void setupRegistries(MinecraftServer server) { } } // Tags - for (ResourceLocation name : BlockTags.getAllTags().getAvailableTags()) { + Registry.BLOCK.getTagNames().map(TagKey::location).forEach(name -> { if (BlockCategory.REGISTRY.get(name.toString()) == null) { BlockCategory.REGISTRY.register(name.toString(), new BlockCategory(name.toString())); } - } - for (ResourceLocation name : ItemTags.getAllTags().getAvailableTags()) { + }); + Registry.ITEM.getTagNames().map(TagKey::location).forEach(name -> { if (ItemCategory.REGISTRY.get(name.toString()) == null) { ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(name.toString())); } - } + }); } @SubscribeEvent diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ExtendedChunk.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ExtendedChunk.java new file mode 100644 index 0000000000..7fb97026dc --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ExtendedChunk.java @@ -0,0 +1,43 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.forge.internal; + +import com.sk89q.worldedit.util.SideEffect; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; + +import javax.annotation.Nullable; + +public interface ExtendedChunk { + /** + * {@link ChunkAccess#setBlockState(BlockPos, BlockState, boolean)} with the extra + * {@link SideEffect#UPDATE} flag. + * + * @param pos the position to set + * @param state the state to set + * @param moved I honestly have no idea and can't be bothered to investigate, we pass {@code + * false} + * @param update the update flag, see side-effect for details + * @return the old block state, or {@code null} if unchanged + */ + @Nullable + BlockState setBlockState(BlockPos pos, BlockState state, boolean moved, boolean update); +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeTransmogrifier.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeTransmogrifier.java index b647237cb5..38224711fd 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeTransmogrifier.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeTransmogrifier.java @@ -19,6 +19,9 @@ package com.sk89q.worldedit.forge.internal; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.forge.ForgeAdapter; import com.sk89q.worldedit.registry.state.BooleanProperty; @@ -44,27 +47,34 @@ */ public class ForgeTransmogrifier { - public static Property transmogToWorldEditProperty(net.minecraft.world.level.block.state.properties.Property property) { - if (property instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { - return new BooleanProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.BooleanProperty) property).getPossibleValues())); - } - if (property instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { - return new IntegerProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.IntegerProperty) property).getPossibleValues())); - } - if (property instanceof DirectionProperty) { - return new DirectionalProperty(property.getName(), ((DirectionProperty) property).getPossibleValues().stream() - .map(ForgeAdapter::adaptEnumFacing) - .collect(Collectors.toList())); - } - if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - // Note: do not make x.getSerializedName a method reference. - // It will cause runtime bootstrap exceptions. - //noinspection Convert2MethodRef - return new EnumProperty(property.getName(), ((net.minecraft.world.level.block.state.properties.EnumProperty) property).getPossibleValues().stream() - .map(x -> x.getSerializedName()) - .collect(Collectors.toList())); + private static final LoadingCache, Property> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader<>() { + @Override + public Property load(net.minecraft.world.level.block.state.properties.Property property) throws Exception { + if (property instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { + return new BooleanProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.BooleanProperty) property).getPossibleValues())); + } + if (property instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { + return new IntegerProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.world.level.block.state.properties.IntegerProperty) property).getPossibleValues())); + } + if (property instanceof DirectionProperty) { + return new DirectionalProperty(property.getName(), ((DirectionProperty) property).getPossibleValues().stream() + .map(ForgeAdapter::adaptEnumFacing) + .collect(Collectors.toList())); + } + if (property instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + // Note: do not make x.getSerializedName a method reference. + // It will cause runtime bootstrap exceptions. + //noinspection Convert2MethodRef + return new EnumProperty(property.getName(), ((net.minecraft.world.level.block.state.properties.EnumProperty) property).getPossibleValues().stream() + .map(x -> x.getSerializedName()) + .collect(Collectors.toList())); + } + return new IPropertyAdapter<>(property); } - return new IPropertyAdapter<>(property); + }); + + public static Property transmogToWorldEditProperty(net.minecraft.world.level.block.state.properties.Property property) { + return PROPERTY_CACHE.getUnchecked(property); } public static Map, Object> transmogToWorldEditProperties(BlockType block, Map, Comparable> mcProps) { diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java index 975a645d8f..6e3d2694fb 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeWorldNativeAccess.java @@ -77,6 +77,11 @@ public BlockState getBlockState(LevelChunk chunk, BlockPos position) { @Nullable @Override public BlockState setBlockState(LevelChunk chunk, BlockPos position, BlockState state) { + if (chunk instanceof ExtendedChunk) { + return ((ExtendedChunk) chunk).setBlockState( + position, state, false, sideEffectSet.shouldApply(SideEffect.UPDATE) + ); + } return chunk.setBlockState(position, state, false); } @@ -140,6 +145,12 @@ public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newSta } } + @Override + public void updateBlock(BlockPos pos, BlockState oldState, BlockState newState) { + ServerLevel world = getWorld(); + newState.onPlace(world, pos, oldState, false); + } + @Override public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState, int recursionLimit) { ServerLevel world = getWorld(); diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/mixin/MixinLevelChunkSetBlockHook.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/mixin/MixinLevelChunkSetBlockHook.java new file mode 100644 index 0000000000..4bfa690b3b --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/mixin/MixinLevelChunkSetBlockHook.java @@ -0,0 +1,85 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.forge.mixin; + +import com.sk89q.worldedit.forge.internal.ExtendedChunk; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.levelgen.blending.BlendingData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Slice; + +import javax.annotation.Nullable; + +@Mixin(LevelChunk.class) +public abstract class MixinLevelChunkSetBlockHook extends ChunkAccess implements ExtendedChunk { + private boolean shouldUpdate = true; + + public MixinLevelChunkSetBlockHook(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, Registry registry, long l, @org.jetbrains.annotations.Nullable LevelChunkSection[] levelChunkSections, @org.jetbrains.annotations.Nullable BlendingData blendingData) { + super(chunkPos, upgradeData, levelHeightAccessor, registry, l, levelChunkSections, blendingData); + } + + @Nullable + @Override + public BlockState setBlockState(BlockPos pos, BlockState state, boolean moved, boolean update) { + // save the state for the hook + shouldUpdate = update; + try { + return setBlockState(pos, state, moved); + } finally { + // restore natural mode + shouldUpdate = true; + } + } + + @Redirect( + method = "setBlockState", + slice = @Slice( + from = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;is(Lnet/minecraft/world/level/block/Block;)Z") + ), + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;onPlace(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Z)V") + ) + public void setBlockStateHook(BlockState target, Level world, BlockPos pos, BlockState old, boolean move) { + boolean localShouldUpdate; + MinecraftServer server = world.getServer(); + if (server == null || Thread.currentThread() != server.getRunningThread()) { + // We're not on the server thread for some reason, WorldEdit will never be here + // so we'll just ignore our flag + localShouldUpdate = true; + } else { + localShouldUpdate = shouldUpdate; + } + if (localShouldUpdate) { + target.onPlace(world, pos, old, move); + } + } +} diff --git a/worldedit-forge/src/main/resources/worldedit-forge.mixins.json b/worldedit-forge/src/main/resources/worldedit-forge.mixins.json new file mode 100644 index 0000000000..930b41b6db --- /dev/null +++ b/worldedit-forge/src/main/resources/worldedit-forge.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "package": "com.sk89q.worldedit.forge.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "MixinLevelChunkSetBlockHook" + ], + "server": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "refmap": "worldedit-forge.mixins.refmap.json" +} diff --git a/worldedit-libs/sponge/build.gradle.kts b/worldedit-libs/sponge/build.gradle.kts index 47bd7e59c5..7f10cf0124 100644 --- a/worldedit-libs/sponge/build.gradle.kts +++ b/worldedit-libs/sponge/build.gradle.kts @@ -8,5 +8,4 @@ repositories { } } dependencies { - "shade"("net.kyori:text-adapter-spongeapi:${Versions.TEXT_EXTRAS}") } diff --git a/worldedit-mod/build.gradle.kts b/worldedit-mod/build.gradle.kts index 66fa8cd67b..ebed03bf56 100644 --- a/worldedit-mod/build.gradle.kts +++ b/worldedit-mod/build.gradle.kts @@ -13,9 +13,29 @@ tasks.register("jar") { project(":worldedit-forge").tasks.named("reobfShadowJar") ) from(zipTree({remapFabric.get().archiveFile})) - from(zipTree({project(":worldedit-forge").tasks.getByName("shadowJar").outputs.files.singleFile})) + from(zipTree({project(":worldedit-forge").tasks.getByName("shadowJar").outputs.files.singleFile})) { + // Duplicated first-party files + exclude("META-INF/services/org.enginehub.piston.CommandManagerService") + exclude("lang/") + // No-brainer library excludes + exclude("com/sk89q/jchronic/") + exclude("com/sk89q/jnbt/") + exclude("com/sk89q/minecraft/") + exclude("com/sk89q/util/") + exclude("com/thoughtworks/") + exclude("net/royawesome/") + exclude("org/enginehub/piston/") + // Exclude worldedit-core + exclude { + val pathString = it.relativePath.pathString + pathString.startsWith("com/sk89q/worldedit/") && !pathString.startsWith("com/sk89q/worldedit/forge/") + } + // Questionable excludes. So far the two files from each jar are the same. + exclude("defaults/worldedit.properties") + exclude("pack.mcmeta") + } - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + duplicatesStrategy = DuplicatesStrategy.FAIL archiveClassifier.set("dist") } diff --git a/worldedit-sponge/build.gradle.kts b/worldedit-sponge/build.gradle.kts index affa68b86a..c8493eac9f 100644 --- a/worldedit-sponge/build.gradle.kts +++ b/worldedit-sponge/build.gradle.kts @@ -1,54 +1,86 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.spongepowered.gradle.plugin.config.PluginLoaders +import org.spongepowered.plugin.metadata.model.PluginDependency plugins { id("org.spongepowered.gradle.plugin") + id("org.spongepowered.gradle.vanilla") } applyPlatformAndCoreConfiguration() applyShadowConfiguration() -// I can't believe sponge sets this in a base plugin with no opt-out -convention.getPlugin(JavaPluginConvention::class.java).apply { - setSourceCompatibility(null) - setTargetCompatibility(null) +repositories { + mavenCentral() } -repositories { - maven { url = uri("https://repo.codemc.org/repository/maven-public") } +minecraft { + version("1.16.5") +} + +val spongeApiVersion = "8.1.0"; + +sponge { + apiVersion(spongeApiVersion) + license("GPL-3.0-or-later") + plugin("worldedit") { + loader { + name(PluginLoaders.JAVA_PLAIN) + version("1.0") + } + displayName("WorldEdit") + version(project.ext["internalVersion"].toString()) + entrypoint("com.sk89q.worldedit.sponge.SpongeWorldEdit") + description("WorldEdit is an easy-to-use in-game world editor for Minecraft, supporting both single- and multi-player.") + links { + homepage("https://enginehub.org/worldedit/") + source("https://github.com/EngineHub/WorldEdit") + issues("https://github.com/EngineHub/WorldEdit/issues") + } + contributor("EngineHub") { + description("Various members of the EngineHub team") + } + dependency("spongeapi") { + loadOrder(PluginDependency.LoadOrder.AFTER) + optional(false) + } + } } dependencies { api(project(":worldedit-core")) api(project(":worldedit-libs:sponge")) - api("org.spongepowered:spongeapi:7.1.0") { - exclude(group = "org.slf4j", module = "slf4j-api") + // TODO remove after VG merges and releases my patch + // https://github.com/SpongePowered/VanillaGradle/pull/66 + minecraft(minecraft.platform().get().moduleName() + ":" + minecraft.version().get()) { + exclude("it.unimi.dsi", "fastutil") } implementation(platform("org.apache.logging.log4j:log4j-bom:${Versions.LOG4J}") { because("Sponge 8 (will?) provides Log4J") }) api("org.apache.logging.log4j:log4j-api") - api("org.bstats:bstats-sponge:1.7") - testImplementation("org.mockito:mockito-core:1.9.0-rc1") + implementation("org.bstats:bstats-sponge:3.0.0") + implementation("it.unimi.dsi:fastutil") + testImplementation("org.mockito:mockito-core:${Versions.MOCKITO}") +} + +configure { + archivesName.set("${project.name}-api$spongeApiVersion") } addJarManifest(WorldEditKind.Mod, includeClasspath = true) tasks.named("shadowJar") { dependencies { - relocate ("org.bstats", "com.sk89q.worldedit.sponge.bstats") { - include(dependency("org.bstats:bstats-sponge:1.7")) - } - } -} + include(dependency("org.bstats:")) + include(dependency("org.antlr:antlr4-runtime")) + include(dependency("it.unimi.dsi:fastutil")) -if (project.hasProperty("signing")) { - apply(plugin = "signing") - - configure { - sign("shadowJar") - } - - tasks.named("build").configure { - dependsOn("signShadowJar") + relocate("org.antlr.v4", "com.sk89q.worldedit.antlr4") + relocate("org.bstats", "com.sk89q.worldedit.sponge.bstats") + relocate("it.unimi.dsi.fastutil", "com.sk89q.worldedit.sponge.fastutil") } } +tasks.named("assemble").configure { + dependsOn("shadowJar") +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CUIChannelHandler.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CUIChannelHandler.java index 8e342f79ca..79f23a134c 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CUIChannelHandler.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CUIChannelHandler.java @@ -20,42 +20,48 @@ package com.sk89q.worldedit.sponge; import com.sk89q.worldedit.LocalSession; -import org.spongepowered.api.Platform; -import org.spongepowered.api.Sponge; -import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.network.ChannelBinding; -import org.spongepowered.api.network.ChannelBuf; -import org.spongepowered.api.network.PlayerConnection; -import org.spongepowered.api.network.RawDataListener; -import org.spongepowered.api.network.RemoteConnection; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.util.lifecycle.SimpleLifecycled; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.lifecycle.RegisterChannelEvent; +import org.spongepowered.api.network.ServerPlayerConnection; +import org.spongepowered.api.network.channel.ChannelBuf; +import org.spongepowered.api.network.channel.raw.RawDataChannel; +import org.spongepowered.api.network.channel.raw.play.RawPlayDataHandler; import java.nio.charset.StandardCharsets; -public class CUIChannelHandler implements RawDataListener { - public static final String CUI_PLUGIN_CHANNEL = "worldedit:cui"; +public class CUIChannelHandler implements RawPlayDataHandler { + public static final ResourceKey CUI_PLUGIN_CHANNEL = ResourceKey.of("worldedit", "cui"); + private static final SimpleLifecycled CHANNEL = SimpleLifecycled.invalid(); - private static ChannelBinding.RawDataChannel channel; - - public static void init() { - channel = Sponge.getChannelRegistrar().createRawChannel(SpongeWorldEdit.inst(), CUI_PLUGIN_CHANNEL); - channel.addListener(Platform.Type.SERVER, new CUIChannelHandler()); + public static final class RegistrationHandler { + @Listener + public void onChannelRegistration(RegisterChannelEvent event) { + RawDataChannel channel = event.register(CUI_PLUGIN_CHANNEL, RawDataChannel.class); + channel.play().addHandler(ServerPlayerConnection.class, new CUIChannelHandler()); + CHANNEL.newValue(channel); + } } - - public static ChannelBinding.RawDataChannel getActiveChannel() { - return channel; + public static RawDataChannel channel() { + return CHANNEL.valueOrThrow(); } @Override - public void handlePayload(ChannelBuf data, RemoteConnection connection, Platform.Type side) { - if (connection instanceof PlayerConnection) { - Player player = ((PlayerConnection) connection).getPlayer(); + public void handlePayload(ChannelBuf data, ServerPlayerConnection connection) { + ServerPlayer player = connection.player(); - LocalSession session = SpongeWorldEdit.inst().getSession(player); + SpongePlayer spongePlayer = SpongeAdapter.adapt(player); + LocalSession session = WorldEdit.getInstance().getSessionManager().get( + spongePlayer + ); - final SpongePlayer actor = SpongeWorldEdit.inst().wrapPlayer(player); - session.handleCUIInitializationMessage(new String(data.readBytes(data.available()), StandardCharsets.UTF_8), - actor); - } + session.handleCUIInitializationMessage( + new String(data.readBytes(data.available()), StandardCharsets.UTF_8), + spongePlayer + ); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CommandAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CommandAdapter.java index 261d811b8d..be08709a14 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CommandAdapter.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/CommandAdapter.java @@ -20,18 +20,17 @@ package com.sk89q.worldedit.sponge; import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.sponge.internal.LocaleResolver; +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.NonNull; import org.enginehub.piston.Command; -import org.spongepowered.api.command.CommandCallable; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.text.Text; +import org.spongepowered.api.command.CommandCause; import java.util.Collections; import java.util.Optional; import java.util.Set; -import static com.sk89q.worldedit.sponge.SpongeTextAdapter.convert; - -public abstract class CommandAdapter implements CommandCallable { +public abstract class CommandAdapter implements org.spongepowered.api.command.Command.Raw { private final Command command; protected CommandAdapter(Command command) { @@ -39,12 +38,18 @@ protected CommandAdapter(Command command) { } @Override - public boolean testPermission(CommandSource source) { + public boolean canExecute(CommandCause cause) { Set permissions = command.getCondition().as(PermissionCondition.class) .map(PermissionCondition::getPermissions) .orElseGet(Collections::emptySet); + + // Allow commands without permission nodes to always execute. + if (permissions.isEmpty()) { + return true; + } + for (String perm : permissions) { - if (source.hasPermission(perm)) { + if (cause.hasPermission(perm)) { return true; } } @@ -52,19 +57,25 @@ public boolean testPermission(CommandSource source) { } @Override - public Optional getShortDescription(CommandSource source) { + public Optional shortDescription(CommandCause cause) { return Optional.of(command.getDescription()) - .map(desc -> SpongeTextAdapter.convert(desc, source.getLocale())); + .map(desc -> SpongeTextAdapter.convert(desc, LocaleResolver.resolveLocale(cause.audience()))); + } + + @Override + public Optional extendedDescription(CommandCause cause) { + return command.getFooter() + .map(footer -> SpongeTextAdapter.convert(footer, LocaleResolver.resolveLocale(cause.audience()))); } @Override - public Optional getHelp(CommandSource source) { + public Optional help(@NonNull CommandCause cause) { return Optional.of(command.getFullHelp()) - .map(help -> SpongeTextAdapter.convert(help, source.getLocale())); + .map(help -> SpongeTextAdapter.convert(help, LocaleResolver.resolveLocale(cause.audience()))); } @Override - public Text getUsage(CommandSource source) { - return convert(command.getUsage(), source.getLocale()); + public Component usage(CommandCause cause) { + return SpongeTextAdapter.convert(command.getUsage(), LocaleResolver.resolveLocale(cause.audience())); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/AdapterLoadException.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/Constants.java similarity index 65% rename from worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/AdapterLoadException.java rename to worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/Constants.java index f2698fc729..7edfd16c6f 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/AdapterLoadException.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/Constants.java @@ -17,25 +17,23 @@ * along with this program. If not, see . */ -package com.sk89q.worldedit.sponge.adapter; +package com.sk89q.worldedit.sponge; + +import org.spongepowered.api.data.persistence.DataQuery; /** - * Thrown when no adapter can be found. + * Kinda mirrors Sponge Common's Constants class. + * + *

Internal. Do not use.

*/ -public class AdapterLoadException extends Exception { - - public AdapterLoadException() { - } - - public AdapterLoadException(String message) { - super(message); - } +public class Constants { + public static class Sponge { + public static final DataQuery UNSAFE_NBT = DataQuery.of("UnsafeData"); - public AdapterLoadException(String message, Throwable cause) { - super(message, cause); + private Sponge() { + } } - public AdapterLoadException(Throwable cause) { - super(cause); + private Constants() { } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java index 5f720b679c..034e988d12 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeAdapter.java @@ -19,15 +19,37 @@ package com.sk89q.worldedit.sponge; -import com.flowpowered.math.vector.Vector3d; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.sponge.internal.NbtAdapter; +import com.sk89q.worldedit.sponge.internal.SpongeTransmogrifier; +import com.sk89q.worldedit.util.Direction; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; -import com.sk89q.worldedit.world.biome.BiomeTypes; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.item.ItemTypes; +import net.minecraft.world.level.block.Block; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.data.persistence.DataView; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.registry.RegistryKey; +import org.spongepowered.api.registry.RegistryReference; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.world.biome.Biome; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3i; + +import java.util.Objects; import static com.google.common.base.Preconditions.checkNotNull; @@ -36,22 +58,21 @@ */ public class SpongeAdapter { - private SpongeAdapter() { + public static org.spongepowered.api.block.BlockState adapt(BlockState blockState) { + int blockStateId = BlockStateIdAccess.getBlockStateId(blockState); + if (!BlockStateIdAccess.isValidInternalId(blockStateId)) { + return SpongeTransmogrifier.transmogToMinecraft(blockState); + } + return (org.spongepowered.api.block.BlockState) Block.stateById(blockStateId); } - /** - * Create a WorldEdit world from a Sponge extent. - * - * @param world the Sponge extent - * @return a WorldEdit world - */ - public static World checkWorld(org.spongepowered.api.world.extent.Extent world) { - checkNotNull(world); - if (world instanceof org.spongepowered.api.world.World) { - return adapt((org.spongepowered.api.world.World) world); - } else { - throw new IllegalArgumentException("Extent type is not a world"); + public static BlockState adapt(org.spongepowered.api.block.BlockState blockState) { + int blockStateId = Block.getId((net.minecraft.world.level.block.state.BlockState) blockState); + BlockState worldEdit = BlockStateIdAccess.getBlockStateById(blockStateId); + if (worldEdit == null) { + return SpongeTransmogrifier.transmogToWorldEdit(blockState); } + return worldEdit; } /** @@ -60,9 +81,9 @@ public static World checkWorld(org.spongepowered.api.world.extent.Extent world) * @param world the Sponge world * @return a WorldEdit world */ - public static World adapt(org.spongepowered.api.world.World world) { + public static World adapt(ServerWorld world) { checkNotNull(world); - return SpongeWorldEdit.inst().getWorld(world); + return new SpongeWorld(world); } /** @@ -71,12 +92,13 @@ public static World adapt(org.spongepowered.api.world.World world) { * @param player The Sponge player * @return The WorldEdit player */ - public static SpongePlayer adapt(Player player) { - return SpongeWorldEdit.inst().wrapPlayer(player); + public static SpongePlayer adapt(ServerPlayer player) { + Objects.requireNonNull(player); + return new SpongePlayer(player); } /** - * Create a Bukkit Player from a WorldEdit Player. + * Create a Sponge Player from a WorldEdit Player. * * @param player The WorldEdit player * @return The Bukkit player @@ -91,12 +113,15 @@ public static Player adapt(com.sk89q.worldedit.entity.Player player) { * @param world the WorldEdit world * @return a Sponge world */ - public static org.spongepowered.api.world.World adapt(World world) { + public static ServerWorld adapt(World world) { checkNotNull(world); if (world instanceof SpongeWorld) { return ((SpongeWorld) world).getWorld(); } else { - org.spongepowered.api.world.World match = Sponge.getServer().getWorld(world.getName()).orElse(null); + // Currently this is 99% certain to fail, we don't have consistent world name/id mapping + ServerWorld match = Sponge.server().worldManager().world( + ResourceKey.resolve(world.getName()) + ).orElse(null); if (match != null) { return match; } else { @@ -105,12 +130,9 @@ public static org.spongepowered.api.world.World adapt(World world) { } } - public static BiomeType adapt(org.spongepowered.api.world.biome.BiomeType biomeType) { - return BiomeTypes.get(biomeType.getId()); - } - - public static org.spongepowered.api.world.biome.BiomeType adapt(BiomeType biomeType) { - return Sponge.getRegistry().getType(org.spongepowered.api.world.biome.BiomeType.class, biomeType.getId()).orElse(null); + public static RegistryReference adapt(BiomeType biomeType) { + return RegistryKey.of(RegistryTypes.BIOME, ResourceKey.resolve(biomeType.getId())) + .asReference(); } /** @@ -119,14 +141,15 @@ public static org.spongepowered.api.world.biome.BiomeType adapt(BiomeType biomeT * @param location the Sponge location * @return a WorldEdit location */ - public static Location adapt(org.spongepowered.api.world.Location location, Vector3d rotation) { + public static Location adapt(ServerLocation location, Vector3d rotation) { checkNotNull(location); Vector3 position = asVector(location); return new Location( - adapt(location.getExtent()), - position, - (float) rotation.getX(), - (float) rotation.getY()); + adapt(location.world()), + position, + (float) rotation.x(), + (float) rotation.y() + ); } /** @@ -135,12 +158,13 @@ public static Location adapt(org.spongepowered.api.world.Location adapt(Location location) { + public static ServerLocation adapt(Location location) { checkNotNull(location); Vector3 position = location.toVector(); - return new org.spongepowered.api.world.Location<>( - adapt((World) location.getExtent()), - position.getX(), position.getY(), position.getZ()); + return ServerLocation.of( + adapt((World) location.getExtent()), + position.getX(), position.getY(), position.getZ() + ); } /** @@ -155,24 +179,66 @@ public static Vector3d adaptRotation(Location location) { } /** - * Create a WorldEdit Vector from a Bukkit location. + * Create a WorldEdit Vector from a Sponge location. * - * @param location The Bukkit location + * @param location The Sponge location * @return a WorldEdit vector */ - public static Vector3 asVector(org.spongepowered.api.world.Location location) { + public static Vector3 asVector(ServerLocation location) { checkNotNull(location); - return Vector3.at(location.getX(), location.getY(), location.getZ()); + return Vector3.at(location.x(), location.y(), location.z()); } /** - * Create a WorldEdit BlockVector from a Bukkit location. + * Create a WorldEdit BlockVector from a Sponge location. * - * @param location The Bukkit location + * @param location The Sponge location * @return a WorldEdit vector */ - public static BlockVector3 asBlockVector(org.spongepowered.api.world.Location location) { + public static BlockVector3 asBlockVector(ServerLocation location) { checkNotNull(location); - return BlockVector3.at(location.getX(), location.getY(), location.getZ()); + return BlockVector3.at(location.x(), location.y(), location.z()); } + + public static BaseItemStack adapt(ItemStack itemStack) { + CompoundTag tag = itemStack.toContainer().getView(Constants.Sponge.UNSAFE_NBT) + .map(NbtAdapter::adaptToWorldEdit) + .orElse(null); + return new BaseItemStack( + ItemTypes.get(itemStack.type().key(RegistryTypes.ITEM_TYPE).asString()), + tag, + itemStack.quantity() + ); + } + + public static ItemStack adapt(BaseItemStack itemStack) { + ItemStack stack = ItemStack.builder() + .itemType(() -> Sponge.game().registry(RegistryTypes.ITEM_TYPE) + .value(ResourceKey.resolve(itemStack.getType().getId()))) + .quantity(itemStack.getAmount()) + .build(); + if (itemStack.getNbtData() != null) { + stack.setRawData( + DataContainer.createNew(DataView.SafetyMode.NO_DATA_CLONED) + .set(Constants.Sponge.UNSAFE_NBT, NbtAdapter.adaptFromWorldEdit(itemStack.getNbtData())) + ); + } + return stack; + } + + public static Direction adapt(org.spongepowered.api.util.Direction direction) { + return Direction.valueOf(direction.name()); + } + + public static Vector3i adaptVector3i(BlockVector3 bv3) { + return new Vector3i(bv3.getX(), bv3.getY(), bv3.getZ()); + } + + public static BlockVector3 adaptVector3i(Vector3i vec3i) { + return BlockVector3.at(vec3i.x(), vec3i.y(), vec3i.z()); + } + + private SpongeAdapter() { + } + } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBiomeRegistry.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBiomeRegistry.java index fd012d2941..48dafb6a2e 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBiomeRegistry.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBiomeRegistry.java @@ -24,7 +24,8 @@ import com.sk89q.worldedit.util.translation.TranslationManager; import com.sk89q.worldedit.world.biome.BiomeData; import com.sk89q.worldedit.world.registry.BiomeRegistry; -import org.spongepowered.api.world.biome.BiomeType; +import org.spongepowered.api.registry.RegistryReference; +import org.spongepowered.api.world.biome.Biome; import javax.annotation.Nullable; @@ -49,21 +50,21 @@ public BiomeData getData(com.sk89q.worldedit.world.biome.BiomeType biome) { @Deprecated private static class SpongeBiomeData implements BiomeData { - private final BiomeType biome; + private final RegistryReference biome; /** * Create a new instance. * * @param biome the base biome */ - private SpongeBiomeData(BiomeType biome) { + private SpongeBiomeData(RegistryReference biome) { this.biome = biome; } @SuppressWarnings("deprecation") @Override public String getName() { - return biome.getName(); + return biome.location().asString(); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockCategoryRegistry.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockCategoryRegistry.java new file mode 100644 index 0000000000..7d28858ca0 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockCategoryRegistry.java @@ -0,0 +1,43 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge; + +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.registry.BlockCategoryRegistry; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.RegistryTypes; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + +public class SpongeBlockCategoryRegistry implements BlockCategoryRegistry { + @Override + public Set getCategorisedByName(String category) { + return Sponge.game().registry(RegistryTypes.BLOCK_TYPE_TAGS) + .findValue(ResourceKey.resolve(category)) + .map(org.spongepowered.api.tag.Tag::values) + .orElse(Collections.emptyList()) + .stream() + .map(b -> BlockType.REGISTRY.get(Sponge.game().registry(RegistryTypes.BLOCK_TYPE).valueKey(b).formatted())) + .collect(Collectors.toSet()); + } +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java new file mode 100644 index 0000000000..142b53a9ef --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockMaterial.java @@ -0,0 +1,96 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge; + +import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.world.registry.PassthroughBlockMaterial; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.material.PushReaction; + +import javax.annotation.Nullable; + +/** + * Sponge block material that pulls as much info as possible from the Minecraft + * Material, and passes the rest to another implementation, typically the + * bundled block info. + */ +public class SpongeBlockMaterial extends PassthroughBlockMaterial { + + private final Material delegate; + private final BlockState block; + + public SpongeBlockMaterial(Material delegate, BlockState block, @Nullable BlockMaterial secondary) { + super(secondary); + this.delegate = delegate; + this.block = block; + } + + @Override + public boolean isAir() { + return delegate == Material.AIR || super.isAir(); + } + + @Override + public boolean isOpaque() { + return delegate.isSolidBlocking(); + } + + @Override + public boolean isLiquid() { + return delegate.isLiquid(); + } + + @Override + public boolean isSolid() { + return delegate.isSolid(); + } + + @Override + public boolean isFragileWhenPushed() { + return delegate.getPushReaction() == PushReaction.DESTROY; + } + + @Override + public boolean isUnpushable() { + return delegate.getPushReaction() == PushReaction.BLOCK; + } + + @Override + public boolean isMovementBlocker() { + return delegate.blocksMotion(); + } + + @Override + public boolean isBurnable() { + return delegate.isFlammable(); + } + + @Override + public boolean isToolRequired() { + return block.requiresCorrectToolForDrops(); + } + + @Override + public boolean isReplacedDuringPlacement() { + return delegate.isReplaceable(); + } + +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockRegistry.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockRegistry.java new file mode 100644 index 0000000000..89515a5b94 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeBlockRegistry.java @@ -0,0 +1,92 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge; + +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.sponge.internal.SpongeTransmogrifier; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.world.registry.BundledBlockRegistry; +import net.minecraft.world.level.block.Block; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.state.StateProperty; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalInt; +import java.util.TreeMap; + +public class SpongeBlockRegistry extends BundledBlockRegistry { + + private final Map materialMap = + new HashMap<>(); + + @Override + public Component getRichName(BlockType blockType) { + return SpongeTextAdapter.convert(Sponge.game().registry(RegistryTypes.BLOCK_TYPE) + .value(ResourceKey.resolve(blockType.getId())).asComponent()); + } + + @Override + public BlockMaterial getMaterial(BlockType blockType) { + org.spongepowered.api.block.BlockType spongeBlockType = + Sponge.game().registry(RegistryTypes.BLOCK_TYPE) + .value(ResourceKey.resolve(blockType.getId())); + return materialMap.computeIfAbsent( + spongeBlockType.defaultState(), + m -> { + net.minecraft.world.level.block.state.BlockState blockState = + (net.minecraft.world.level.block.state.BlockState) m; + return new SpongeBlockMaterial( + blockState.getMaterial(), + blockState, + super.getMaterial(blockType) + ); + } + ); + } + + @Override + public Map> getProperties(BlockType blockType) { + org.spongepowered.api.block.BlockType spongeBlockType = + Sponge.game().registry(RegistryTypes.BLOCK_TYPE) + .value(ResourceKey.resolve(blockType.getId())); + Map> map = new TreeMap<>(); + Collection> propertyKeys = spongeBlockType + .defaultState().stateProperties(); + for (StateProperty key : propertyKeys) { + map.put(key.name(), SpongeTransmogrifier.transmogToWorldEditProperty(key)); + } + return map; + } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + org.spongepowered.api.block.BlockState equivalent = SpongeAdapter.adapt(state); + return OptionalInt.of(Block.getId( + (net.minecraft.world.level.block.state.BlockState) equivalent + )); + } +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java index 39b8cfd2c8..a75ee8fc6c 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeCommandSender.java @@ -23,21 +23,15 @@ import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.internal.cui.CUIEvent; import com.sk89q.worldedit.session.SessionKey; -import com.sk89q.worldedit.util.auth.AuthorizationException; -import com.sk89q.worldedit.util.formatting.WorldEditText; import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.formatting.text.adapter.spongeapi.TextAdapter; -import org.spongepowered.api.command.CommandSource; +import com.sk89q.worldedit.util.formatting.text.TextComponent; +import com.sk89q.worldedit.util.formatting.text.format.TextColor; +import net.kyori.adventure.audience.Audience; import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.format.TextColor; -import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.text.serializer.TextSerializers; import java.io.File; import java.util.Locale; import java.util.UUID; -import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; @@ -49,15 +43,15 @@ public class SpongeCommandSender implements Actor { */ private static final UUID DEFAULT_ID = UUID.fromString("a233eb4b-4cab-42cd-9fd9-7e7b9a3f74be"); - private final CommandSource sender; - private final SpongeWorldEdit plugin; + private final Audience sender; - public SpongeCommandSender(SpongeWorldEdit plugin, CommandSource sender) { - checkNotNull(plugin); + public SpongeCommandSender(Audience sender) { checkNotNull(sender); - checkArgument(!(sender instanceof Player), "Cannot wrap a player"); + checkArgument( + !(sender instanceof Player), + "Players should be wrapped using the specialized class" + ); - this.plugin = plugin; this.sender = sender; } @@ -68,42 +62,44 @@ public UUID getUniqueId() { @Override public String getName() { - return sender.getName(); + return "Console"; } - @SuppressWarnings("deprecation") @Override + @Deprecated public void printRaw(String msg) { for (String part : msg.split("\n")) { - sender.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part)); + sender.sendMessage(net.kyori.adventure.text.Component.text(part)); } } @Override + @Deprecated public void print(String msg) { - sendColorized(msg, TextColors.LIGHT_PURPLE); + for (String part : msg.split("\n")) { + print(TextComponent.of(part, TextColor.LIGHT_PURPLE)); + } } @Override + @Deprecated public void printDebug(String msg) { - sendColorized(msg, TextColors.GRAY); + for (String part : msg.split("\n")) { + print(TextComponent.of(part, TextColor.GRAY)); + } } @Override + @Deprecated public void printError(String msg) { - sendColorized(msg, TextColors.RED); + for (String part : msg.split("\n")) { + print(TextComponent.of(part, TextColor.RED)); + } } @Override public void print(Component component) { - TextAdapter.sendMessage(sender, WorldEditText.format(component, getLocale())); - } - - @SuppressWarnings("deprecation") - private void sendColorized(String msg, TextColor formatting) { - for (String part : msg.split("\n")) { - sender.sendMessage(Text.of(formatting, TextSerializers.LEGACY_FORMATTING_CODE.deserialize(part))); - } + sender.sendMessage(SpongeTextAdapter.convert(component, getLocale())); } @Override @@ -122,7 +118,7 @@ public boolean hasPermission(String perm) { } @Override - public void checkPermission(String permission) throws AuthorizationException { + public void checkPermission(String permission) { } @Override @@ -152,20 +148,19 @@ public Locale getLocale() { @Override public SessionKey getSessionKey() { return new SessionKey() { - @Nullable @Override public String getName() { - return null; + return "Console"; } @Override public boolean isActive() { - return false; + return true; } @Override public boolean isPersistent() { - return false; + return true; } @Override diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntity.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntity.java index 532a366082..48ecd794fb 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntity.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntity.java @@ -19,14 +19,17 @@ package com.sk89q.worldedit.sponge; -import com.flowpowered.math.vector.Vector3d; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.entity.metadata.EntityProperties; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.sponge.internal.NbtAdapter; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.world.NullWorld; -import org.spongepowered.api.world.World; +import com.sk89q.worldedit.world.entity.EntityType; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.math.vector.Vector3d; import java.lang.ref.WeakReference; import javax.annotation.Nullable; @@ -45,21 +48,28 @@ class SpongeEntity implements Entity { @Override public BaseEntity getState() { org.spongepowered.api.entity.Entity entity = entityRef.get(); - if (entity != null) { - return SpongeWorldEdit.inst().getAdapter().createBaseEntity(entity); - } else { + if (entity == null || entity.vehicle().isPresent()) { + return null; + } + EntityType entityType = EntityType.REGISTRY.get(entity.type().key(RegistryTypes.ENTITY_TYPE).asString()); + if (entityType == null) { return null; } + return new BaseEntity(entityType, + entity.toContainer().getView(Constants.Sponge.UNSAFE_NBT) + .map(NbtAdapter::adaptToWorldEdit) + .orElse(null) + ); } @Override public Location getLocation() { org.spongepowered.api.entity.Entity entity = entityRef.get(); if (entity != null) { - org.spongepowered.api.world.Location entityLoc = entity.getLocation(); - Vector3d entityRot = entity.getRotation(); + ServerLocation entityLoc = entity.serverLocation(); + Vector3d entityRot = entity.rotation(); - return SpongeWorldEdit.inst().getAdapter().adapt(entityLoc, entityRot); + return SpongeAdapter.adapt(entityLoc, entityRot); } else { return new Location(NullWorld.getInstance()); } @@ -79,7 +89,7 @@ public boolean setLocation(Location location) { public Extent getExtent() { org.spongepowered.api.entity.Entity entity = entityRef.get(); if (entity != null) { - return SpongeWorldEdit.inst().getAdapter().getWorld(entity.getWorld()); + return SpongeAdapter.adapt(entity.serverLocation().world()); } else { return NullWorld.getInstance(); } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntityProperties.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntityProperties.java index 7373ae642f..f7b0269a7f 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntityProperties.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeEntityProperties.java @@ -20,29 +20,27 @@ package com.sk89q.worldedit.sponge; import com.sk89q.worldedit.entity.metadata.EntityProperties; -import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.data.Keys; import org.spongepowered.api.entity.Entity; import org.spongepowered.api.entity.ExperienceOrb; import org.spongepowered.api.entity.FallingBlock; import org.spongepowered.api.entity.Item; -import org.spongepowered.api.entity.explosive.PrimedTNT; +import org.spongepowered.api.entity.explosive.fused.PrimedTNT; import org.spongepowered.api.entity.hanging.ItemFrame; import org.spongepowered.api.entity.hanging.Painting; import org.spongepowered.api.entity.living.Ambient; import org.spongepowered.api.entity.living.ArmorStand; +import org.spongepowered.api.entity.living.ComplexLivingPart; import org.spongepowered.api.entity.living.Humanoid; import org.spongepowered.api.entity.living.Living; -import org.spongepowered.api.entity.living.Villager; import org.spongepowered.api.entity.living.animal.Animal; -import org.spongepowered.api.entity.living.complex.ComplexLivingPart; +import org.spongepowered.api.entity.living.aquatic.Aquatic; import org.spongepowered.api.entity.living.golem.Golem; import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.trader.Trader; import org.spongepowered.api.entity.projectile.Projectile; import org.spongepowered.api.entity.vehicle.Boat; import org.spongepowered.api.entity.vehicle.minecart.Minecart; -import org.spongepowered.api.text.Text; - -import java.util.Optional; import static com.google.common.base.Preconditions.checkNotNull; @@ -122,7 +120,7 @@ public boolean isAmbient() { @Override public boolean isNPC() { - return entity instanceof Villager; + return entity instanceof Trader; } @Override @@ -132,12 +130,12 @@ public boolean isGolem() { @Override public boolean isTamed() { - return entity.get(Keys.TAMED_OWNER).orElse(Optional.empty()).isPresent(); + return entity.get(Keys.IS_TAMED).orElse(false); } @Override public boolean isTagged() { - return !entity.get(Keys.DISPLAY_NAME).orElse(Text.EMPTY).isEmpty(); + return entity.get(Keys.CUSTOM_NAME).isPresent(); } @Override @@ -152,6 +150,6 @@ public boolean isPasteable() { @Override public boolean isWaterCreature() { - return false; // TODO api8 + return entity instanceof Aquatic; } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeItemCategoryRegistry.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeItemCategoryRegistry.java new file mode 100644 index 0000000000..22029ea8d7 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeItemCategoryRegistry.java @@ -0,0 +1,43 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge; + +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.registry.ItemCategoryRegistry; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.RegistryTypes; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; + +public class SpongeItemCategoryRegistry implements ItemCategoryRegistry { + @Override + public Set getCategorisedByName(String category) { + return Sponge.game().registry(RegistryTypes.ITEM_TYPE_TAGS) + .findValue(ResourceKey.resolve(category)) + .map(org.spongepowered.api.tag.Tag::values) + .orElse(Collections.emptyList()) + .stream() + .map(b -> ItemType.REGISTRY.get(Sponge.game().registry(RegistryTypes.ITEM_TYPE).valueKey(b).formatted())) + .collect(Collectors.toSet()); + } +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeItemRegistry.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeItemRegistry.java new file mode 100644 index 0000000000..61dd1222d9 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeItemRegistry.java @@ -0,0 +1,47 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge; + +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.registry.BundledItemRegistry; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.RegistryTypes; + +public class SpongeItemRegistry extends BundledItemRegistry { + + @Override + public Component getRichName(ItemType itemType) { + return SpongeTextAdapter.convert(Sponge.game().registry(RegistryTypes.ITEM_TYPE) + .value(ResourceKey.resolve(itemType.getId())).asComponent()); + } + + @Override + public Component getRichName(BaseItemStack itemStack) { + return TranslatableComponent.of( + ((ItemStack) (Object) SpongeAdapter.adapt(itemStack)).getDescriptionId() + ); + } + +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePermissionsProvider.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePermissionsProvider.java index 0e8fc89c94..da99c0eca6 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePermissionsProvider.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePermissionsProvider.java @@ -20,28 +20,28 @@ package com.sk89q.worldedit.sponge; import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.CommandCallable; -import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.service.permission.PermissionDescription; import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.SubjectReference; public class SpongePermissionsProvider { - public boolean hasPermission(Player player, String permission) { + public boolean hasPermission(ServerPlayer player, String permission) { return player.hasPermission(permission); } - public void registerPermission(CommandCallable command, String permission) { - Sponge.getGame().getServiceManager().getRegistration(PermissionService.class).ifPresent((permissionService -> { - PermissionDescription.Builder permissionBuilder = permissionService.getProvider().newDescriptionBuilder(SpongeWorldEdit.inst()); + public void registerPermission(String permission) { + Sponge.game().serviceProvider().registration(PermissionService.class).ifPresent((permissionService -> { + PermissionDescription.Builder permissionBuilder = permissionService.service() + .newDescriptionBuilder(SpongeWorldEdit.inst().getPluginContainer()); permissionBuilder.id(permission).register(); })); } - public String[] getGroups(Player player) { - return player.getParents().stream() - .map(SubjectReference::getSubjectIdentifier) - .toArray(String[]::new); + public String[] getGroups(ServerPlayer player) { + return player.parents().stream() + .map(SubjectReference::subjectIdentifier) + .toArray(String[]::new); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java index a55109b9b9..11c47d75d9 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlatform.java @@ -19,31 +19,25 @@ package com.sk89q.worldedit.sponge; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.entity.Player; -import com.sk89q.worldedit.event.platform.CommandEvent; -import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.extension.platform.AbstractPlatform; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.MultiUserPlatform; import com.sk89q.worldedit.extension.platform.Preference; -import com.sk89q.worldedit.internal.command.CommandUtil; import com.sk89q.worldedit.sponge.config.SpongeConfiguration; import com.sk89q.worldedit.util.SideEffect; import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.registry.Registries; -import org.enginehub.piston.Command; import org.enginehub.piston.CommandManager; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; -import org.spongepowered.api.command.CommandException; -import org.spongepowered.api.command.CommandResult; -import org.spongepowered.api.command.CommandSource; -import org.spongepowered.api.entity.EntityType; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.scheduler.Task; -import org.spongepowered.api.world.Location; +import org.spongepowered.api.util.Ticks; +import org.spongepowered.api.world.server.ServerWorld; import java.util.ArrayList; import java.util.Collection; @@ -76,13 +70,13 @@ public Registries getRegistries() { @Override public int getDataVersion() { - // TODO add to adapter - org.spongepowered.common.data.util.DataUtil#MINECRAFT_DATA_VERSION - return 1631; + return Sponge.platform().minecraftVersion().dataVersion().orElse(-1); } @Override public boolean isValidMobType(String type) { - return Sponge.getRegistry().getType(EntityType.class, type).isPresent(); + return Sponge.game().registry(RegistryTypes.ENTITY_TYPE) + .findValue(ResourceKey.resolve(type)).isPresent(); } @Override @@ -93,16 +87,21 @@ public void reload() { @Override public int schedule(long delay, long period, Runnable task) { - Task.builder().delayTicks(delay).intervalTicks(period).execute(task).submit(SpongeWorldEdit.inst()); - return 0; // TODO This isn't right, but we only check for -1 values + Task t = Task.builder() + .delay(Ticks.of(delay)) + .interval(Ticks.of(period)) + .execute(task) + .plugin(SpongeWorldEdit.inst().getPluginContainer()) + .build(); + return Math.abs(t.hashCode()); // TODO This isn't right, but we only check for -1 values } @Override public List getWorlds() { - Collection worlds = Sponge.getServer().getWorlds(); + Collection worlds = Sponge.server().worldManager().worlds(); List ret = new ArrayList<>(worlds.size()); - for (org.spongepowered.api.world.World world : worlds) { - ret.add(SpongeWorldEdit.inst().getAdapter().getWorld(world)); + for (ServerWorld world : worlds) { + ret.add(SpongeAdapter.adapt(world)); } return ret; } @@ -113,8 +112,8 @@ public Player matchPlayer(Player player) { if (player instanceof SpongePlayer) { return player; } else { - Optional optPlayer = Sponge.getServer().getPlayer(player.getUniqueId()); - return optPlayer.map(player1 -> new SpongePlayer(this, player1)).orElse(null); + Optional optPlayer = Sponge.server().player(player.getUniqueId()); + return optPlayer.map(SpongePlayer::new).orElse(null); } } @@ -124,9 +123,10 @@ public World matchWorld(World world) { if (world instanceof SpongeWorld) { return world; } else { - for (org.spongepowered.api.world.World ws : Sponge.getServer().getWorlds()) { - if (ws.getName().equals(world.getName())) { - return SpongeWorldEdit.inst().getAdapter().getWorld(ws); + // TODO this needs fixing for world name shenanigans + for (ServerWorld spongeWorld : Sponge.server().worldManager().worlds()) { + if (spongeWorld.key().toString().equals(world.getName())) { + return SpongeAdapter.adapt(spongeWorld); } } @@ -136,26 +136,6 @@ public World matchWorld(World world) { @Override public void registerCommands(CommandManager manager) { - for (Command command : manager.getAllCommands().collect(toList())) { - CommandAdapter adapter = new CommandAdapter(command) { - @Override - public CommandResult process(CommandSource source, String arguments) throws org.spongepowered.api.command.CommandException { - CommandEvent weEvent = new CommandEvent(SpongeWorldEdit.inst().wrapCommandSource(source), command.getName() + " " + arguments); - WorldEdit.getInstance().getEventBus().post(weEvent); - return weEvent.isCancelled() ? CommandResult.success() : CommandResult.empty(); - } - - @Override - public List getSuggestions(CommandSource source, String arguments, @Nullable Location targetPosition) throws CommandException { - CommandSuggestionEvent weEvent = new CommandSuggestionEvent(SpongeWorldEdit.inst().wrapCommandSource(source), command.getName() + " " + arguments); - WorldEdit.getInstance().getEventBus().post(weEvent); - return CommandUtil.fixSuggestions(arguments, weEvent.getSuggestions()); - } - }; - ImmutableList.Builder aliases = ImmutableList.builder(); - aliases.add(command.getName()).addAll(command.getAliases()); - Sponge.getCommandManager().register(SpongeWorldEdit.inst(), adapter, aliases.build()); - } } @Override @@ -202,15 +182,13 @@ public Map getCapabilities() { @Override public Set getSupportedSideEffects() { - return ImmutableSet.of(); + return ImmutableSet.of( + SideEffect.UPDATE, SideEffect.ENTITY_AI, SideEffect.LIGHTING, SideEffect.NEIGHBORS + ); } @Override public Collection getConnectedUsers() { - List users = new ArrayList<>(); - for (org.spongepowered.api.entity.living.player.Player player : Sponge.getServer().getOnlinePlayers()) { - users.add(new SpongePlayer(this, player)); - } - return users; + return Sponge.server().onlinePlayers().stream().map(SpongePlayer::new).collect(toList()); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java index f0f814d74f..9d32d6ba4b 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongePlayer.java @@ -19,7 +19,7 @@ package com.sk89q.worldedit.sponge; -import com.flowpowered.math.vector.Vector3d; +import com.sk89q.jnbt.CompoundTag; import com.sk89q.util.StringUtil; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; @@ -29,63 +29,64 @@ import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.session.SessionKey; +import com.sk89q.worldedit.sponge.internal.NbtAdapter; import com.sk89q.worldedit.util.HandSide; import com.sk89q.worldedit.util.Location; -import com.sk89q.worldedit.util.formatting.WorldEditText; import com.sk89q.worldedit.util.formatting.text.Component; -import com.sk89q.worldedit.util.formatting.text.adapter.spongeapi.TextAdapter; +import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.gamemode.GameMode; import com.sk89q.worldedit.world.gamemode.GameModes; -import com.sk89q.worldedit.world.item.ItemTypes; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.spongepowered.api.ResourceKey; import org.spongepowered.api.Sponge; -import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.data.Keys; import org.spongepowered.api.data.type.HandTypes; import org.spongepowered.api.entity.living.player.Player; -import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.format.TextColor; -import org.spongepowered.api.text.format.TextColors; -import org.spongepowered.api.text.serializer.TextSerializers; -import org.spongepowered.api.world.World; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.math.vector.Vector3d; import java.nio.charset.StandardCharsets; import java.util.Locale; -import java.util.Optional; import java.util.UUID; import javax.annotation.Nullable; public class SpongePlayer extends AbstractPlayerActor { + private static final int STRUCTURE_BLOCK_PACKET_ID = 7; - private final Player player; + private final ServerPlayer player; - protected SpongePlayer(SpongePlatform platform, Player player) { + protected SpongePlayer(ServerPlayer player) { this.player = player; ThreadSafeCache.getInstance().getOnlineIds().add(getUniqueId()); } @Override public UUID getUniqueId() { - return player.getUniqueId(); + return player.uniqueId(); } @Override public BaseItemStack getItemInHand(HandSide handSide) { - Optional is = this.player.getItemInHand(handSide == HandSide.MAIN_HAND - ? HandTypes.MAIN_HAND : HandTypes.OFF_HAND); - return is.map(itemStack -> new BaseItemStack(ItemTypes.get(itemStack.getType().getId()))).orElse(null); + ItemStack is = this.player.itemInHand( + handSide == HandSide.MAIN_HAND ? HandTypes.MAIN_HAND : HandTypes.OFF_HAND + ); + return SpongeAdapter.adapt(is); } @Override public String getName() { - return this.player.getName(); + return this.player.name(); } - @SuppressWarnings("deprecation") @Override public String getDisplayName() { - return player.getDisplayNameData().displayName().getDirect().map(TextSerializers.LEGACY_FORMATTING_CODE::serialize).orElse(getName()); + return LegacyComponentSerializer.legacySection().serialize(player.displayName().get()); } @Override @@ -95,10 +96,10 @@ public BaseEntity getState() { @Override public Location getLocation() { - org.spongepowered.api.world.Location entityLoc = this.player.getLocation(); - Vector3d entityRot = this.player.getRotation(); + ServerLocation entityLoc = this.player.serverLocation(); + Vector3d entityRot = this.player.rotation(); - return SpongeWorldEdit.inst().getAdapter().adapt(entityLoc, entityRot); + return SpongeAdapter.adapt(entityLoc, entityRot); } @Override @@ -108,15 +109,12 @@ public boolean setLocation(Location location) { @Override public com.sk89q.worldedit.world.World getWorld() { - return SpongeWorldEdit.inst().getAdapter().getWorld(player.getWorld()); + return SpongeAdapter.adapt(player.serverLocation().world()); } @Override public void giveItem(BaseItemStack itemStack) { - this.player.getInventory().offer( - ItemStack.of(Sponge.getGame().getRegistry().getType(ItemType.class, itemStack.getType().getId()).get(), - itemStack.getAmount()) - ); + this.player.inventory().offer(SpongeAdapter.adapt(itemStack)); } @Override @@ -128,46 +126,55 @@ public void dispatchCUIEvent(CUIEvent event) { } String finalData = send; - CUIChannelHandler.getActiveChannel().sendTo(player, buffer -> buffer.writeBytes(finalData.getBytes(StandardCharsets.UTF_8))); + CUIChannelHandler.channel().play().sendTo( + player, + buffer -> buffer.writeBytes(finalData.getBytes(StandardCharsets.UTF_8)) + ); } @Override + @Deprecated public void printRaw(String msg) { for (String part : msg.split("\n")) { - this.player.sendMessage(TextSerializers.FORMATTING_CODE.deserialize(part)); + this.player.sendMessage(LegacyComponentSerializer.legacySection().deserialize(part)); } } @Override + @Deprecated public void printDebug(String msg) { - sendColorized(msg, TextColors.GRAY); + sendColorized(msg, NamedTextColor.GRAY); } @Override + @Deprecated public void print(String msg) { - sendColorized(msg, TextColors.LIGHT_PURPLE); + sendColorized(msg, NamedTextColor.LIGHT_PURPLE); } @Override + @Deprecated public void printError(String msg) { - sendColorized(msg, TextColors.RED); + sendColorized(msg, NamedTextColor.RED); } @Override public void print(Component component) { - TextAdapter.sendMessage(player, WorldEditText.format(component, getLocale())); + player.sendMessage(SpongeTextAdapter.convert(component, getLocale())); } private void sendColorized(String msg, TextColor formatting) { for (String part : msg.split("\n")) { - this.player.sendMessage(Text.of(formatting, TextSerializers.FORMATTING_CODE.deserialize(part))); + this.player.sendMessage( + LegacyComponentSerializer.legacySection().deserialize(part).color(formatting) + ); } } @Override public boolean trySetPosition(Vector3 pos, float pitch, float yaw) { - org.spongepowered.api.world.Location loc = new org.spongepowered.api.world.Location<>( - this.player.getWorld(), pos.getX(), pos.getY(), pos.getZ() + ServerLocation loc = ServerLocation.of( + this.player.world(), pos.getX(), pos.getY(), pos.getZ() ); return this.player.setLocationAndRotation(loc, new Vector3d(pitch, yaw, 0)); @@ -196,13 +203,16 @@ public T getFacet(Class cls) { @Override public GameMode getGameMode() { - return GameModes.get(player.getGameModeData().type().get().getId()); + return GameModes.get(player.gameMode().get().key(RegistryTypes.GAME_MODE).asString()); } @Override public void setGameMode(GameMode gameMode) { - player.getGameModeData().type().set(Sponge.getRegistry().getType(org.spongepowered.api.entity.living.player.gamemode.GameMode.class, - gameMode.getId()).get()); + player.gameMode().set( + Sponge.game().registry(RegistryTypes.GAME_MODE).value( + ResourceKey.resolve(gameMode.getId()) + ) + ); } @Override @@ -217,24 +227,29 @@ public void setFlying(boolean flying) { @Override public > void sendFakeBlock(BlockVector3 pos, B block) { - org.spongepowered.api.world.Location loc = player.getWorld().getLocation(pos.getX(), pos.getY(), pos.getZ()); if (block == null) { - player.sendBlockChange(loc.getBlockPosition(), loc.getBlock()); + player.resetBlockChange(pos.getX(), pos.getY(), pos.getZ()); } else { - // TODO - // player.sendBlockChange(loc, BukkitAdapter.adapt(block)); - // if (block instanceof BaseBlock && ((BaseBlock) block).hasNbtData()) { - // BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter(); - // if (adapter != null) { - // adapter.sendFakeNBT(player, pos, ((BaseBlock) block).getNbtData()); - // } - // } + player.sendBlockChange(pos.getX(), pos.getY(), pos.getZ(), SpongeAdapter.adapt(block.toImmutableState())); + if (block instanceof BaseBlock && block.getBlockType().equals(com.sk89q.worldedit.world.block.BlockTypes.STRUCTURE_BLOCK)) { + final BaseBlock baseBlock = (BaseBlock) block; + final CompoundTag nbtData = baseBlock.getNbtData(); + if (nbtData != null) { + net.minecraft.server.level.ServerPlayer mcPlayer = + ((net.minecraft.server.level.ServerPlayer) player); + mcPlayer.connection.send(new net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket( + new net.minecraft.core.BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()), + STRUCTURE_BLOCK_PACKET_ID, + NbtAdapter.adaptNMSToWorldEdit(nbtData)) + ); + } + } } } @Override public Locale getLocale() { - return player.getLocale(); + return player.locale(); } @Override @@ -249,8 +264,8 @@ static class SessionKeyImpl implements SessionKey { private final String name; SessionKeyImpl(Player player) { - this.uuid = player.getUniqueId(); - this.name = player.getName(); + this.uuid = player.uniqueId(); + this.name = player.name(); } @Override diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeRegistries.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeRegistries.java index 47b83250e2..04baa39365 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeRegistries.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeRegistries.java @@ -20,7 +20,11 @@ package com.sk89q.worldedit.sponge; import com.sk89q.worldedit.world.registry.BiomeRegistry; +import com.sk89q.worldedit.world.registry.BlockCategoryRegistry; +import com.sk89q.worldedit.world.registry.BlockRegistry; import com.sk89q.worldedit.world.registry.BundledRegistries; +import com.sk89q.worldedit.world.registry.ItemCategoryRegistry; +import com.sk89q.worldedit.world.registry.ItemRegistry; /** * World data for the Sponge platform. @@ -28,20 +32,39 @@ class SpongeRegistries extends BundledRegistries { private static final SpongeRegistries INSTANCE = new SpongeRegistries(); + + public static SpongeRegistries getInstance() { + return INSTANCE; + } + private final BiomeRegistry biomeRegistry = new SpongeBiomeRegistry(); + private final BlockRegistry blockRegistry = new SpongeBlockRegistry(); + private final BlockCategoryRegistry blockCategoryRegistry = new SpongeBlockCategoryRegistry(); + private final ItemRegistry itemRegistry = new SpongeItemRegistry(); + private final ItemCategoryRegistry itemCategoryRegistry = new SpongeItemCategoryRegistry(); @Override public BiomeRegistry getBiomeRegistry() { return biomeRegistry; } - /** - * Get a static instance. - * - * @return an instance - */ - public static SpongeRegistries getInstance() { - return INSTANCE; + @Override + public BlockRegistry getBlockRegistry() { + return blockRegistry; } + @Override + public BlockCategoryRegistry getBlockCategoryRegistry() { + return blockCategoryRegistry; + } + + @Override + public ItemRegistry getItemRegistry() { + return itemRegistry; + } + + @Override + public ItemCategoryRegistry getItemCategoryRegistry() { + return itemCategoryRegistry; + } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeTextAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeTextAdapter.java index 1731f98d13..7709737e43 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeTextAdapter.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeTextAdapter.java @@ -22,16 +22,22 @@ import com.sk89q.worldedit.util.formatting.WorldEditText; import com.sk89q.worldedit.util.formatting.text.Component; import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer; -import org.spongepowered.api.text.Text; -import org.spongepowered.api.text.serializer.TextSerializers; import java.util.Locale; public class SpongeTextAdapter { - public static Text convert(Component component, Locale locale) { + public static net.kyori.adventure.text.Component convert(Component component, Locale locale) { component = WorldEditText.format(component, locale); - return TextSerializers.JSON.deserialize(GsonComponentSerializer.INSTANCE.serialize(component)); + return net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson() + .deserialize(GsonComponentSerializer.INSTANCE.serialize(component)); + } + + public static Component convert(net.kyori.adventure.text.Component component) { + return GsonComponentSerializer.INSTANCE.deserialize( + net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson() + .serialize(component) + ); } private SpongeTextAdapter() { diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java index 8aead8e81e..0509d8e40c 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorld.java @@ -19,49 +19,74 @@ package com.sk89q.worldedit.sponge; -import com.flowpowered.math.vector.Vector3d; -import com.flowpowered.math.vector.Vector3i; +import com.google.common.collect.Sets; +import com.sk89q.jnbt.AdventureNBTConverter; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.internal.util.LogManagerCompat; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.regions.CuboidRegion; import com.sk89q.worldedit.regions.Region; -import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.sponge.internal.NbtAdapter; +import com.sk89q.worldedit.sponge.internal.SpongeWorldNativeAccess; import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.TreeGenerator; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.RegenOptions; -import com.sk89q.worldedit.world.WorldUnloadedException; +import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; +import net.minecraft.core.BlockPos; +import net.minecraft.data.worldgen.Features; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; +import org.apache.logging.log4j.Logger; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; -import org.spongepowered.api.block.BlockSnapshot; -import org.spongepowered.api.block.BlockState; -import org.spongepowered.api.block.BlockType; -import org.spongepowered.api.block.BlockTypes; -import org.spongepowered.api.block.tileentity.TileEntity; -import org.spongepowered.api.data.key.Keys; -import org.spongepowered.api.data.property.block.GroundLuminanceProperty; -import org.spongepowered.api.data.property.block.SkyLuminanceProperty; +import org.spongepowered.api.block.entity.BlockEntityArchetype; +import org.spongepowered.api.block.entity.BlockEntityType; +import org.spongepowered.api.entity.EntityArchetype; import org.spongepowered.api.entity.EntityType; import org.spongepowered.api.entity.EntityTypes; +import org.spongepowered.api.entity.Item; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.util.Ticks; import org.spongepowered.api.world.BlockChangeFlags; -import org.spongepowered.api.world.World; -import org.spongepowered.api.world.weather.Weather; +import org.spongepowered.api.world.LightTypes; +import org.spongepowered.api.world.SerializationBehavior; +import org.spongepowered.api.world.generation.config.WorldGenerationConfig; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.api.world.server.WorldTemplate; +import org.spongepowered.api.world.volume.stream.StreamOptions; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.math.vector.Vector3i; import java.lang.ref.WeakReference; import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; -import java.util.Locale; -import java.util.Map; import java.util.Optional; +import java.util.Random; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; import javax.annotation.Nullable; import static com.google.common.base.Preconditions.checkNotNull; @@ -69,43 +94,34 @@ /** * An adapter to Minecraft worlds for WorldEdit. */ -public abstract class SpongeWorld extends AbstractWorld { +public final class SpongeWorld extends AbstractWorld { - private final WeakReference worldRef; + private static final Random random = new Random(); + private static final Logger LOGGER = LogManagerCompat.getLogger(); + + private final WeakReference worldRef; + private final SpongeWorldNativeAccess worldNativeAccess; /** * Construct a new world. * * @param world the world */ - protected SpongeWorld(World world) { + protected SpongeWorld(ServerWorld world) { checkNotNull(world); this.worldRef = new WeakReference<>(world); + this.worldNativeAccess = new SpongeWorldNativeAccess(new WeakReference<>((ServerLevel) world)); } /** * Get the underlying handle to the world. * * @return the world - * @throws WorldEditException thrown if a reference to the world was lost (i.e. world was unloaded) - */ - public World getWorldChecked() throws WorldEditException { - World world = worldRef.get(); - if (world != null) { - return world; - } else { - throw new WorldUnloadedException(); - } - } - - /** - * Get the underlying handle to the world. - * - * @return the world - * @throws RuntimeException thrown if a reference to the world was lost (i.e. world was unloaded) + * @throws RuntimeException thrown if a reference to the world was lost (i.e. world was + * unloaded) */ - public World getWorld() { - World world = worldRef.get(); + ServerWorld getWorld() { + ServerWorld world = worldRef.get(); if (world != null) { return world; } else { @@ -113,104 +129,237 @@ public World getWorld() { } } + // This is sus but leaving it for later world name/id reworks @Override public String getName() { - return getWorld().getName(); + return getWorld().key().asString(); } @Override public String getId() { - return getName().replace(" ", "_").toLowerCase(Locale.ROOT) - + getWorld().getDimension().getType().getName().toLowerCase(Locale.ROOT); + return getWorld().key().asString(); } @Override public Path getStoragePath() { - return getWorld().getDirectory(); + return getWorld().directory(); + } + + @Override + public BlockState getBlock(BlockVector3 position) { + return SpongeAdapter.adapt(getWorld().block( + position.getX(), position.getY(), position.getZ() + )); + } + + @Override + public BaseBlock getFullBlock(BlockVector3 position) { + CompoundTag entity = getWorld() + .blockEntity(position.getX(), position.getY(), position.getZ()) + .map(e -> NbtAdapter.adaptToWorldEdit(e.createArchetype().blockEntityData())) + .orElse(null); + return getBlock(position).toBaseBlock(entity); } - @SuppressWarnings("WeakerAccess") - protected BlockState getBlockState(BlockStateHolder block) { - if (block instanceof com.sk89q.worldedit.world.block.BlockState) { - BlockState state = Sponge.getRegistry().getType(BlockType.class, block.getBlockType().getId()).orElse(BlockTypes.AIR).getDefaultState(); - for (Map.Entry, Object> entry : block.getStates().entrySet()) { - // TODO Convert across states + @Override + public > boolean setBlock(BlockVector3 position, B block, SideEffectSet sideEffects) throws WorldEditException { + checkNotNull(position); + checkNotNull(block); + + ServerWorld world = getWorld(); + + org.spongepowered.api.block.BlockState newState = SpongeAdapter.adapt(block.toImmutableState()); + + boolean didSet = world.setBlock( + position.getX(), position.getY(), position.getZ(), + newState, + BlockChangeFlags.NONE + .withUpdateNeighbors(sideEffects.shouldApply(SideEffect.NEIGHBORS)) + .withNotifyClients(true) + .withPhysics(sideEffects.shouldApply(SideEffect.UPDATE)) + .withNotifyObservers(sideEffects.shouldApply(SideEffect.UPDATE)) + .withLightingUpdates(sideEffects.shouldApply(SideEffect.LIGHTING)) + .withPathfindingUpdates(sideEffects.shouldApply(SideEffect.ENTITY_AI)) + .withNeighborDropsAllowed(false) + .withBlocksMoving(false) + .withForcedReRender(false) + .withIgnoreRender(false) + ); + if (!didSet) { + // still update NBT if the block is the same + if (world.block(position.getX(), position.getY(), position.getZ()) == newState) { + didSet = block.toBaseBlock().hasNbtData(); } - return state; - } else { - throw new UnsupportedOperationException("Missing Sponge adapter for WorldEdit!"); } - } - @SuppressWarnings("WeakerAccess") - protected abstract void applyTileEntityData(TileEntity entity, BaseBlock block); + // Create the TileEntity + if (didSet && block instanceof BaseBlock && ((BaseBlock) block).getNbtReference() != null) { + BaseBlock baseBlock = (BaseBlock) block; + BlockEntityArchetype.builder() + .blockEntity((BlockEntityType) + world.engine().registry(RegistryTypes.BLOCK_ENTITY_TYPE) + .value(ResourceKey.resolve(baseBlock.getNbtId())) + ) + .blockEntityData(NbtAdapter.adaptFromWorldEdit((CompoundTag) AdventureNBTConverter.fromAdventure(baseBlock.getNbt()))) + .state(newState) + .build() + .apply(ServerLocation.of(world, position.getX(), position.getY(), position.getZ())); + } - private static final BlockSnapshot.Builder builder = BlockSnapshot.builder(); + return true; + } @Override - public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { + public Set applySideEffects(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType, SideEffectSet sideEffectSet) throws WorldEditException { checkNotNull(position); - checkNotNull(block); - World world = getWorldChecked(); + worldNativeAccess.applySideEffects(position, previousType, sideEffectSet); - // First set the block - Vector3i pos = new Vector3i(position.getX(), position.getY(), position.getZ()); - BlockState newState = getBlockState(block); + return Sets.intersection( + SpongeWorldEdit.inst().getInternalPlatform().getSupportedSideEffects(), + sideEffectSet.getSideEffectsToApply() + ); + } + + @Override + public boolean clearContainerBlockContents(BlockVector3 position) { + getWorld().removeBlockEntity(position.getX(), position.getY(), position.getZ()); + return true; + } - BlockSnapshot snapshot = builder.reset() - .blockState(newState) - .position(pos) - .world(world.getProperties()) - .build(); + @Override + public boolean regenerate(Region region, Extent extent, RegenOptions options) { + Server server = Sponge.server(); + + final String id = "worldedittemp_" + getWorld().key().value(); + + WorldGenerationConfig baseConfig = getWorld().asTemplate().generationConfig(); + + WorldTemplate tempWorldProperties = getWorld().asTemplate().asBuilder() + .key(ResourceKey.of("worldedit", id)) + .loadOnStartup(false) + .serializationBehavior(SerializationBehavior.NONE) + .generationConfig(options.getSeed().isPresent() + ? WorldGenerationConfig.Mutable.builder() + .generateBonusChest(baseConfig.generateBonusChest()) + .generateFeatures(baseConfig.generateFeatures()) + .seed(options.getSeed().getAsLong()) + .build() + : baseConfig) + .build(); + + ServerWorld tempWorld; + try { + tempWorld = server.worldManager().loadWorld(tempWorldProperties).get(); + } catch (InterruptedException | ExecutionException e) { + LOGGER.error("Failed to load temp world", e); + return false; + } - snapshot.restore(true, notifyAndLight ? BlockChangeFlags.ALL : BlockChangeFlags.NONE); + try { + // Pre-gen all the chunks + // We need to also pull one more chunk in every direction + CuboidRegion expandedPreGen = new CuboidRegion(region.getMinimumPoint().subtract(16, 16, 16), region.getMaximumPoint().add(16, 16, 16)); + for (BlockVector3 chunk : expandedPreGen.getChunkCubes()) { + tempWorld.loadChunk(chunk.getBlockX(), chunk.getBlockY(), chunk.getBlockZ(), true); + } - // Create the TileEntity - if (block instanceof BaseBlock) { - BaseBlock baseBlock = (BaseBlock) block; - if (baseBlock.getNbtReference() != null) { - // Kill the old TileEntity - world.getTileEntity(pos).ifPresent(tileEntity -> applyTileEntityData(tileEntity, baseBlock)); + World from = SpongeAdapter.adapt(tempWorld); + for (BlockVector3 vec : region) { + extent.setBlock(vec, from.getFullBlock(vec)); + if (options.shouldRegenBiomes()) { + extent.setBiome(vec, from.getBiome(vec)); + } } + } catch (WorldEditException e) { + throw new RuntimeException(e); + } finally { + // Remove temp world + server.worldManager().unloadWorld(tempWorldProperties.key()).thenRun(() -> server.worldManager().deleteWorld(tempWorldProperties.key())); } return true; } - @Override - public boolean notifyAndLightBlock(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType) throws WorldEditException { - // TODO Move this to adapter - return false; + + @Nullable + private static ConfiguredFeature createTreeFeatureGenerator(TreeGenerator.TreeType type) { + switch (type) { + // Based off of the SaplingGenerator class, as well as uses of DefaultBiomeFeatures fields + case TREE: + return Features.OAK; + case BIG_TREE: + return Features.FANCY_OAK; + case REDWOOD: + return Features.SPRUCE; + case TALL_REDWOOD: + return Features.MEGA_SPRUCE; + case MEGA_REDWOOD: + return Features.MEGA_PINE; + case BIRCH: + return Features.BIRCH; + case JUNGLE: + return Features.MEGA_JUNGLE_TREE; + case SMALL_JUNGLE: + return Features.JUNGLE_TREE; + case SHORT_JUNGLE: + return Features.JUNGLE_TREE_NO_VINE; + case JUNGLE_BUSH: + return Features.JUNGLE_BUSH; + case SWAMP: + return Features.SWAMP_TREE; + case ACACIA: + return Features.ACACIA; + case DARK_OAK: + return Features.DARK_OAK; + case TALL_BIRCH: + return Features.BIRCH_TALL; + case RED_MUSHROOM: + return Features.HUGE_RED_MUSHROOM; + case BROWN_MUSHROOM: + return Features.HUGE_BROWN_MUSHROOM; + case WARPED_FUNGUS: + return Features.WARPED_FUNGI; + case CRIMSON_FUNGUS: + return Features.CRIMSON_FUNGI; + case CHORUS_PLANT: + return Features.CHORUS_PLANT; + case RANDOM: + return createTreeFeatureGenerator(TreeGenerator.TreeType.values()[ThreadLocalRandom.current().nextInt(TreeGenerator.TreeType.values().length)]); + default: + return null; + } } @Override - public boolean regenerate(Region region, Extent extent, RegenOptions options) { - return false; + public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) { + ConfiguredFeature generator = createTreeFeatureGenerator(type); + ServerLevel world = (ServerLevel) getWorld(); + return generator != null && generator.place( + world, world.getChunkSource().getGenerator(), random, + new BlockPos(position.getX(), position.getY(), position.getZ()) + ); } @Override public int getBlockLightLevel(BlockVector3 position) { checkNotNull(position); - BlockState state = getWorld().getBlock(new Vector3i(position.getX(), position.getY(), position.getZ())); + int skyLight = getWorld().light(LightTypes.SKY, position.getX(), position.getY(), position.getZ()); + int groundLight = getWorld().light(LightTypes.BLOCK, position.getX(), position.getY(), position.getZ()); - Optional groundLuminanceProperty = state.getProperty(GroundLuminanceProperty.class); - Optional skyLuminanceProperty = state.getProperty(SkyLuminanceProperty.class); - - if (!groundLuminanceProperty.isPresent() || !skyLuminanceProperty.isPresent()) { - return 0; - } - - //noinspection ConstantConditions - return (int) Math.max(groundLuminanceProperty.get().getValue(), skyLuminanceProperty.get().getValue()); + return Math.max(skyLight, groundLight); } @Override public BiomeType getBiome(BlockVector3 position) { checkNotNull(position); - return SpongeAdapter.adapt(getWorld().getBiome(position.getBlockX(), position.getBlockY(), position.getBlockZ())); + return BiomeType.REGISTRY.get( + getWorld().registry(RegistryTypes.BIOME) + .valueKey(getWorld().biome(position.getBlockX(), position.getBlockY(), position.getBlockZ())) + .asString() + ); } @Override @@ -218,7 +367,12 @@ public boolean setBiome(BlockVector3 position, BiomeType biome) { checkNotNull(position); checkNotNull(biome); - getWorld().setBiome(position.getBlockX(), position.getY(), position.getBlockZ(), SpongeAdapter.adapt(biome)); + getWorld().setBiome( + position.getBlockX(), position.getY(), position.getBlockZ(), + getWorld().registry(RegistryTypes.BIOME).value( + ResourceKey.resolve(biome.getId()) + ) + ); return true; } @@ -231,24 +385,32 @@ public void dropItem(Vector3 position, BaseItemStack item) { return; } - org.spongepowered.api.entity.Entity entity = getWorld().createEntity( - EntityTypes.ITEM, - new Vector3d(position.getX(), position.getY(), position.getZ()) + Item itemEntity = getWorld().createEntity( + EntityTypes.ITEM, + new Vector3d(position.getX(), position.getY(), position.getZ()) ); - entity.offer(Keys.REPRESENTED_ITEM, SpongeWorldEdit.toSpongeItemStack(item).createSnapshot()); - getWorld().spawnEntity(entity); + itemEntity.item().set( + SpongeAdapter.adapt(item).createSnapshot() + ); + getWorld().spawnEntity(itemEntity); } @Override public void simulateBlockMine(BlockVector3 position) { - // TODO + getWorld().destroyBlock( + new Vector3i(position.getX(), position.getY(), position.getZ()), + true + ); } @Override public boolean canPlaceAt(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState blockState) { - // TODO - return true; + return ((net.minecraft.world.level.block.state.BlockState) SpongeAdapter.adapt(blockState)) + .canSurvive( + ((LevelReader) getWorld()), + new BlockPos(position.getX(), position.getY(), position.getZ()) + ); } @Override @@ -262,89 +424,89 @@ public boolean equals(Object o) { return false; } else if ((o instanceof SpongeWorld)) { SpongeWorld other = ((SpongeWorld) o); - World otherWorld = other.worldRef.get(); - World thisWorld = worldRef.get(); + ServerWorld otherWorld = other.worldRef.get(); + ServerWorld thisWorld = worldRef.get(); return otherWorld != null && otherWorld.equals(thisWorld); } else { return o instanceof com.sk89q.worldedit.world.World - && ((com.sk89q.worldedit.world.World) o).getName().equals(getName()); + && ((com.sk89q.worldedit.world.World) o).getName().equals(getName()); } } @Override public List getEntities(Region region) { - List entities = new ArrayList<>(); - for (org.spongepowered.api.entity.Entity entity : getWorld().getEntities()) { - org.spongepowered.api.world.Location loc = entity.getLocation(); - if (region.contains(BlockVector3.at(loc.getX(), loc.getY(), loc.getZ()))) { - entities.add(new SpongeEntity(entity)); - } - } - return entities; + return getWorld() + .entityStream( + SpongeAdapter.adaptVector3i(region.getMinimumPoint()), + SpongeAdapter.adaptVector3i(region.getMaximumPoint()), + // We don't need to force load or clone to copy entities + StreamOptions.builder() + .setCarbonCopy(false) + .setLoadingStyle(StreamOptions.LoadingStyle.NONE) + .build() + ) + .toStream() + .map(ve -> new SpongeEntity(ve.type())) + .collect(Collectors.toList()); } @Override public List getEntities() { - List entities = new ArrayList<>(); - for (org.spongepowered.api.entity.Entity entity : getWorld().getEntities()) { - entities.add(new SpongeEntity(entity)); - } - return entities; + return getWorld().entities().stream() + .map(SpongeEntity::new) + .collect(Collectors.toList()); } - protected abstract void applyEntityData(org.spongepowered.api.entity.Entity entity, BaseEntity data); - @Nullable @Override public Entity createEntity(Location location, BaseEntity entity) { - World world = getWorld(); - - EntityType entityType = Sponge.getRegistry().getType(EntityType.class, entity.getType().getId()).get(); - Vector3d pos = new Vector3d(location.getX(), location.getY(), location.getZ()); - - org.spongepowered.api.entity.Entity newEnt = world.createEntity(entityType, pos); - if (entity.getNbtReference() != null) { - applyEntityData(newEnt, entity); + Optional> entityType = Sponge.game().registry(RegistryTypes.ENTITY_TYPE) + .findValue(ResourceKey.resolve(entity.getType().getId())); + if (!entityType.isPresent()) { + return null; } - - // Overwrite any data set by the NBT application - Vector3 dir = location.getDirection(); - - newEnt.setLocationAndRotation( - new org.spongepowered.api.world.Location<>(getWorld(), pos), - new Vector3d(dir.getX(), dir.getY(), dir.getZ()) - ); - - if (world.spawnEntity(newEnt)) { - return new SpongeEntity(newEnt); + EntityArchetype.Builder builder = EntityArchetype.builder().type(entityType.get()); + CompoundBinaryTag nativeTag = entity.getNbt(); + if (nativeTag != null) { + builder.entityData(NbtAdapter.adaptFromWorldEdit((CompoundTag) AdventureNBTConverter.fromAdventure(nativeTag))); } - - return null; + return builder.build().apply(SpongeAdapter.adapt(location)).map(SpongeEntity::new).orElse(null); } @Override public WeatherType getWeather() { - return WeatherTypes.get(getWorld().getWeather().getId()); + return WeatherTypes.get( + getWorld().weather().type().key(RegistryTypes.WEATHER_TYPE).asString() + ); } @Override public long getRemainingWeatherDuration() { - return getWorld().getRemainingDuration(); + return getWorld().weather().remainingDuration().ticks(); } @Override public void setWeather(WeatherType weatherType) { - getWorld().setWeather(Sponge.getRegistry().getType(Weather.class, weatherType.getId()).get()); + getWorld().setWeather( + Sponge.game().registry(RegistryTypes.WEATHER_TYPE).value( + ResourceKey.resolve(weatherType.getId()) + ) + ); } @Override public void setWeather(WeatherType weatherType, long duration) { - getWorld().setWeather(Sponge.getRegistry().getType(Weather.class, weatherType.getId()).get(), duration); + getWorld().setWeather( + Sponge.game().registry(RegistryTypes.WEATHER_TYPE).value( + ResourceKey.resolve(weatherType.getId()) + ), + Ticks.of(duration) + ); } @Override public BlockVector3 getSpawnPosition() { - return SpongeAdapter.asBlockVector(getWorld().getSpawnLocation()); + return SpongeAdapter.adaptVector3i(getWorld().properties().spawnPosition()); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldEdit.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldEdit.java index c9b218a4d6..519b2b4244 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldEdit.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/SpongeWorldEdit.java @@ -19,227 +19,283 @@ package com.sk89q.worldedit.sponge; +import com.google.common.base.Joiner; import com.google.inject.Inject; -import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.event.platform.CommandEvent; +import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; import com.sk89q.worldedit.event.platform.PlatformReadyEvent; +import com.sk89q.worldedit.event.platform.PlatformUnreadyEvent; +import com.sk89q.worldedit.event.platform.PlatformsRegisteredEvent; import com.sk89q.worldedit.event.platform.SessionIdleEvent; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.extension.platform.PlatformManager; import com.sk89q.worldedit.internal.anvil.ChunkDeleter; -import com.sk89q.worldedit.sponge.adapter.AdapterLoadException; -import com.sk89q.worldedit.sponge.adapter.SpongeImplAdapter; -import com.sk89q.worldedit.sponge.adapter.SpongeImplLoader; +import com.sk89q.worldedit.internal.command.CommandUtil; import com.sk89q.worldedit.sponge.config.SpongeConfiguration; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockCategory; +import com.sk89q.worldedit.world.item.ItemCategory; +import net.kyori.adventure.audience.Audience; import org.apache.logging.log4j.Logger; -import org.bstats.sponge.Metrics2; +import org.bstats.sponge.Metrics; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; import org.spongepowered.api.block.BlockSnapshot; import org.spongepowered.api.block.BlockType; import org.spongepowered.api.block.BlockTypes; -import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.command.Command; +import org.spongepowered.api.command.CommandCause; +import org.spongepowered.api.command.CommandCompletion; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.parameter.ArgumentReader; import org.spongepowered.api.config.ConfigDir; -import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.block.InteractBlockEvent; import org.spongepowered.api.event.filter.cause.Root; -import org.spongepowered.api.event.game.state.GameAboutToStartServerEvent; -import org.spongepowered.api.event.game.state.GameInitializationEvent; -import org.spongepowered.api.event.game.state.GamePostInitializationEvent; -import org.spongepowered.api.event.game.state.GamePreInitializationEvent; -import org.spongepowered.api.event.game.state.GameStartedServerEvent; -import org.spongepowered.api.event.game.state.GameStoppingServerEvent; import org.spongepowered.api.event.item.inventory.InteractItemEvent; -import org.spongepowered.api.event.network.ClientConnectionEvent; -import org.spongepowered.api.item.ItemType; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.plugin.Plugin; -import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.event.lifecycle.ConstructPluginEvent; +import org.spongepowered.api.event.lifecycle.RegisterCommandEvent; +import org.spongepowered.api.event.lifecycle.StartedEngineEvent; +import org.spongepowered.api.event.lifecycle.StartingEngineEvent; +import org.spongepowered.api.event.lifecycle.StoppingEngineEvent; +import org.spongepowered.api.event.network.ServerSideConnectionEvent; +import org.spongepowered.api.registry.RegistryTypes; import org.spongepowered.api.scheduler.Task; -import org.spongepowered.api.world.Location; -import org.spongepowered.api.world.World; +import org.spongepowered.api.world.server.ServerLocation; +import org.spongepowered.api.world.server.ServerWorld; +import org.spongepowered.math.vector.Vector3d; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; -import java.io.File; -import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; +import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.TimeUnit; -import static com.google.common.base.Preconditions.checkNotNull; import static com.sk89q.worldedit.internal.anvil.ChunkDeleter.DELCHUNKS_FILE_NAME; +import static java.util.stream.Collectors.toList; /** * The Sponge implementation of WorldEdit. */ -@Plugin(id = SpongeWorldEdit.MOD_ID, name = "WorldEdit", - description = "WorldEdit is an easy-to-use in-game world editor for Minecraft", - url = "https://enginehub.org/worldedit/") +@Plugin(SpongeWorldEdit.MOD_ID) public class SpongeWorldEdit { - // I think this breaks right now b/c SpongeAPI 7 injects an slf4j logger - // But it should be fine in 8, where they're also Log4j - @Inject - private Logger logger; - - private final Metrics2 metrics; - public static final String MOD_ID = "worldedit"; private static final int BSTATS_PLUGIN_ID = 3329; - private SpongePermissionsProvider provider; - - @Inject - private PluginContainer container; - private static SpongeWorldEdit inst; - public static PluginContainer container() { - return inst.container; - } - public static SpongeWorldEdit inst() { return inst; } - private SpongePlatform platform; - private SpongeImplAdapter spongeAdapter; - - @Inject - private SpongeConfiguration config; + private final Logger logger; + private final PluginContainer container; + private final SpongeConfiguration config; + private final Path workingDir; - @Inject @ConfigDir(sharedRoot = false) - private File workingDir; + private SpongePermissionsProvider provider; + private SpongePlatform platform; @Inject - public SpongeWorldEdit(Metrics2.Factory metricsFactory) { + public SpongeWorldEdit(Logger logger, + PluginContainer container, + SpongeConfiguration config, + Metrics.Factory metricsFactory, + @ConfigDir(sharedRoot = false) + Path workingDir) { + this.logger = logger; + this.container = container; + this.config = config; + this.workingDir = workingDir; + metricsFactory.make(BSTATS_PLUGIN_ID); inst = this; - metrics = metricsFactory.make(BSTATS_PLUGIN_ID); } @Listener - public void preInit(GamePreInitializationEvent event) { - // Load configuration - config.load(); + public void onPluginConstruction(ConstructPluginEvent event) { + this.platform = new SpongePlatform(this); + WorldEdit.getInstance().getPlatformManager().register(platform); - Task.builder().interval(30, TimeUnit.SECONDS).execute(ThreadSafeCache.getInstance()).submit(this); - } + this.provider = new SpongePermissionsProvider(); - @Listener - public void init(GameInitializationEvent event) { - CUIChannelHandler.init(); - } + Task.builder() + .plugin(container) + .interval(30, TimeUnit.SECONDS) + .execute(ThreadSafeCache.getInstance()) + .build(); - @Listener - public void postInit(GamePostInitializationEvent event) { + event.game().eventManager().registerListeners( + container, + new CUIChannelHandler.RegistrationHandler() + ); logger.info("WorldEdit for Sponge (version " + getInternalVersion() + ") is loaded"); } @Listener - public void serverAboutToStart(GameAboutToStartServerEvent event) { - if (this.platform != null) { - logger.warn("GameAboutToStartServerEvent occurred when GameStoppingServerEvent hasn't"); - WorldEdit.getInstance().getPlatformManager().unregister(platform); - } - - final Path delChunks = workingDir.toPath().resolve(DELCHUNKS_FILE_NAME); + public void serverStarting(StartingEngineEvent event) { + final Path delChunks = workingDir.resolve(DELCHUNKS_FILE_NAME); if (Files.exists(delChunks)) { ChunkDeleter.runFromFile(delChunks, true); } + } - this.platform = new SpongePlatform(this); - this.provider = new SpongePermissionsProvider(); - - for (BlockType blockType : Sponge.getRegistry().getAllOf(BlockType.class)) { - // TODO Handle blockstate stuff - String id = blockType.getId(); + @Listener + public void serverStarted(StartedEngineEvent event) { + event.game().registry(RegistryTypes.BLOCK_TYPE).streamEntries().forEach(blockType -> { + String id = blockType.key().asString(); if (!com.sk89q.worldedit.world.block.BlockType.REGISTRY.keySet().contains(id)) { - com.sk89q.worldedit.world.block.BlockType.REGISTRY.register(id, new com.sk89q.worldedit.world.block.BlockType(id)); + com.sk89q.worldedit.world.block.BlockType.REGISTRY.register(id, new com.sk89q.worldedit.world.block.BlockType( + id, + input -> { + BlockType spongeBlockType = Sponge.game().registry(RegistryTypes.BLOCK_TYPE).value( + ResourceKey.resolve(input.getBlockType().getId()) + ); + return SpongeAdapter.adapt(spongeBlockType.defaultState()); + } + )); } - } + }); - for (ItemType itemType : Sponge.getRegistry().getAllOf(ItemType.class)) { - String id = itemType.getId(); + event.game().registry(RegistryTypes.ITEM_TYPE).streamEntries().forEach(itemType -> { + String id = itemType.key().asString(); if (!com.sk89q.worldedit.world.item.ItemType.REGISTRY.keySet().contains(id)) { com.sk89q.worldedit.world.item.ItemType.REGISTRY.register(id, new com.sk89q.worldedit.world.item.ItemType(id)); } + }); + + event.game().registry(RegistryTypes.ENTITY_TYPE).streamEntries().forEach(entityType -> { + String id = entityType.key().asString(); + if (!com.sk89q.worldedit.world.entity.EntityType.REGISTRY.keySet().contains(id)) { + com.sk89q.worldedit.world.entity.EntityType.REGISTRY.register(id, new com.sk89q.worldedit.world.entity.EntityType(id)); + } + }); + + for (ServerWorld world : event.engine().worldManager().worlds()) { + world.registry(RegistryTypes.BIOME).streamEntries().forEach(biomeType -> { + String id = biomeType.key().asString(); + if (!BiomeType.REGISTRY.keySet().contains(id)) { + BiomeType.REGISTRY.register(id, new BiomeType(id)); + } + }); } - WorldEdit.getInstance().getPlatformManager().register(platform); + event.game().registry(RegistryTypes.BLOCK_TYPE_TAGS).streamEntries().forEach(blockTypeTag -> { + String id = blockTypeTag.key().asString(); + if (!BlockCategory.REGISTRY.keySet().contains(id)) { + BlockCategory.REGISTRY.register(id, new BlockCategory(id)); + } + }); + event.game().registry(RegistryTypes.ITEM_TYPE_TAGS).streamEntries().forEach(itemTypeTag -> { + String id = itemTypeTag.key().asString(); + if (!ItemCategory.REGISTRY.keySet().contains(id)) { + ItemCategory.REGISTRY.register(id, new ItemCategory(id)); + } + }); + + config.load(); + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent(platform)); } @Listener - public void serverStopping(GameStoppingServerEvent event) { + public void serverStopping(StoppingEngineEvent event) { WorldEdit worldEdit = WorldEdit.getInstance(); worldEdit.getSessionManager().unload(); - worldEdit.getPlatformManager().unregister(platform); + WorldEdit.getInstance().getEventBus().post(new PlatformUnreadyEvent(platform)); } @Listener - public void serverStarted(GameStartedServerEvent event) { - WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent(platform)); + public void registerCommand(RegisterCommandEvent event) { + WorldEdit.getInstance().getEventBus().post(new PlatformsRegisteredEvent()); + PlatformManager manager = WorldEdit.getInstance().getPlatformManager(); + Platform commandsPlatform = manager.queryCapability(Capability.USER_COMMANDS); + if (commandsPlatform != platform || !platform.isHookingEvents()) { + // We're not in control of commands/events -- do not register. + return; + } - loadAdapter(); + List commands = manager.getPlatformCommandManager().getCommandManager() + .getAllCommands().collect(toList()); + for (org.enginehub.piston.Command command : commands) { + registerAdaptedCommand(event, command); + + Set perms = command.getCondition().as(PermissionCondition.class) + .map(PermissionCondition::getPermissions) + .orElseGet(Collections::emptySet); + if (!perms.isEmpty()) { + perms.forEach(getPermissionsProvider()::registerPermission); + } + } } - private void loadAdapter() { - WorldEdit worldEdit = WorldEdit.getInstance(); + private String rebuildArguments(String commandLabel, String args) { + int plSep = commandLabel.indexOf(":"); + if (plSep >= 0 && plSep < commandLabel.length() + 1) { + commandLabel = commandLabel.substring(plSep + 1); + } - // Attempt to load a Sponge adapter - SpongeImplLoader adapterLoader = new SpongeImplLoader(); + StringBuilder sb = new StringBuilder("/").append(commandLabel); - try { - adapterLoader.addFromPath(getClass().getClassLoader()); - } catch (IOException e) { - logger.warn("Failed to search path for Sponge adapters"); + String[] split = args.split(" ", -1); + if (split.length > 0) { + sb.append(" "); } + return Joiner.on(" ").appendTo(sb, split).toString(); + } - try { - adapterLoader.addFromJar(container.getSource().get().toFile()); - } catch (IOException e) { - logger.warn("Failed to search " + container.getSource().get().toFile() + " for Sponge adapters", e); - } - try { - spongeAdapter = adapterLoader.loadAdapter(); - logger.info("Using " + spongeAdapter.getClass().getCanonicalName() + " as the Sponge adapter"); - } catch (AdapterLoadException e) { - Platform platform = worldEdit.getPlatformManager().queryCapability(Capability.WORLD_EDITING); - if (platform instanceof SpongePlatform) { - logger.warn(e.getMessage()); - } else { - logger.info("WorldEdit could not find a Sponge adapter for this MC version, " - + "but it seems that you have another implementation of WorldEdit installed (" + platform.getPlatformName() + ") " - + "that handles the world editing."); + private void registerAdaptedCommand(RegisterCommandEvent event, org.enginehub.piston.Command command) { + CommandAdapter adapter = new CommandAdapter(command) { + @Override + public CommandResult process(CommandCause cause, ArgumentReader.Mutable arguments) { + CommandEvent weEvent = new CommandEvent(SpongeWorldEdit.inst().wrapCommandCause(cause), rebuildArguments(command.getName(), arguments.remaining()).trim()); + WorldEdit.getInstance().getEventBus().post(weEvent); + return weEvent.isCancelled() ? CommandResult.success() : CommandResult.builder().build(); } - } + + @Override + public List complete(CommandCause cause, ArgumentReader.Mutable arguments) { + String args = rebuildArguments(command.getName(), arguments.remaining()); + CommandSuggestionEvent weEvent = new CommandSuggestionEvent(SpongeWorldEdit.inst().wrapCommandCause(cause), args); + WorldEdit.getInstance().getEventBus().post(weEvent); + return CommandUtil.fixSuggestions(args, weEvent.getSuggestions()) + .stream().map(CommandCompletion::of).collect(toList()); + } + }; + event.register( + container, adapter, command.getName(), command.getAliases().toArray(new String[0]) + ); } - public SpongeImplAdapter getAdapter() { - return this.spongeAdapter; + private boolean isHookingEvents() { + return platform != null && platform.isHookingEvents(); } @Listener - public void onPlayerItemInteract(InteractItemEvent.Secondary event, @Root Player spongePlayer) { - if (platform == null) { + public void onPlayerItemInteract(InteractItemEvent.Secondary event, @Root ServerPlayer spongePlayer) { + if (!isHookingEvents()) { return; } - if (!platform.isHookingEvents()) { - return; // We have to be told to catch these events - } - WorldEdit we = WorldEdit.getInstance(); - SpongePlayer player = wrapPlayer(spongePlayer); + SpongePlayer player = SpongeAdapter.adapt(spongePlayer); if (we.handleRightClick(player)) { event.setCancelled(true); } } @Listener - public void onPlayerInteract(InteractBlockEvent event, @Root Player spongePlayer) { + public void onPlayerInteract(InteractBlockEvent event, @Root ServerPlayer spongePlayer) { if (platform == null) { return; } @@ -250,62 +306,60 @@ public void onPlayerInteract(InteractBlockEvent event, @Root Player spongePlayer WorldEdit we = WorldEdit.getInstance(); - SpongePlayer player = wrapPlayer(spongePlayer); - com.sk89q.worldedit.world.World world = player.getWorld(); + SpongePlayer player = SpongeAdapter.adapt(spongePlayer); - BlockSnapshot targetBlock = event.getTargetBlock(); - Optional> optLoc = targetBlock.getLocation(); + BlockSnapshot targetBlock = event.block(); + Optional optLoc = targetBlock.location(); - BlockType interactedType = targetBlock.getState().getType(); - if (event instanceof InteractBlockEvent.Primary) { - if (interactedType != BlockTypes.AIR) { + BlockType interactedType = targetBlock.state().type(); + if (event instanceof InteractBlockEvent.Primary.Start) { + InteractBlockEvent.Primary.Start eventCast = ((InteractBlockEvent.Primary.Start) event); + if (interactedType != BlockTypes.AIR.get()) { if (!optLoc.isPresent()) { return; } - Location loc = optLoc.get(); - com.sk89q.worldedit.util.Location pos = new com.sk89q.worldedit.util.Location( - world, loc.getX(), loc.getY(), loc.getZ()); - - if (we.handleBlockLeftClick(player, pos)) { - event.setCancelled(true); - } + ServerLocation loc = optLoc.get(); + com.sk89q.worldedit.util.Location pos = SpongeAdapter.adapt( + loc, Vector3d.ZERO + ); - if (we.handleArmSwing(player)) { - event.setCancelled(true); - } - } else { - if (we.handleArmSwing(player)) { - event.setCancelled(true); + if (we.handleBlockLeftClick(player, pos, SpongeAdapter.adapt(eventCast.targetSide()))) { + eventCast.setCancelled(true); } } + if (we.handleArmSwing(player)) { + eventCast.setCancelled(true); + } } else if (event instanceof InteractBlockEvent.Secondary) { if (!optLoc.isPresent()) { return; } + InteractBlockEvent.Secondary eventCast = ((InteractBlockEvent.Secondary) event); - Location loc = optLoc.get(); - com.sk89q.worldedit.util.Location pos = new com.sk89q.worldedit.util.Location( - world, loc.getX(), loc.getY(), loc.getZ()); + ServerLocation loc = optLoc.get(); + com.sk89q.worldedit.util.Location pos = SpongeAdapter.adapt( + loc, Vector3d.ZERO + ); - if (we.handleBlockRightClick(player, pos)) { - event.setCancelled(true); + if (we.handleBlockRightClick(player, pos, SpongeAdapter.adapt(eventCast.targetSide()))) { + eventCast.setCancelled(true); } if (we.handleRightClick(player)) { - event.setCancelled(true); + eventCast.setCancelled(true); } } } @Listener - public void onPlayerQuit(ClientConnectionEvent.Disconnect event) { + public void onPlayerQuit(ServerSideConnectionEvent.Disconnect event) { WorldEdit.getInstance().getEventBus() - .post(new SessionIdleEvent(new SpongePlayer.SessionKeyImpl(event.getTargetEntity()))); + .post(new SessionIdleEvent(new SpongePlayer.SessionKeyImpl(event.player()))); } - public static ItemStack toSpongeItemStack(BaseItemStack item) { - return inst().getAdapter().makeSpongeStack(item); + public PluginContainer getPluginContainer() { + return container; } /** @@ -317,46 +371,18 @@ SpongeConfiguration getConfig() { return this.config; } - /** - * Get the WorldEdit proxy for the given player. - * - * @param player the player - * @return the WorldEdit player - */ - public SpongePlayer wrapPlayer(Player player) { - checkNotNull(player); - return new SpongePlayer(platform, player); - } - - public Actor wrapCommandSource(CommandSource sender) { - if (sender instanceof Player) { - return wrapPlayer((Player) sender); + public Actor wrapCommandCause(CommandCause cause) { + Object rootCause = cause.root(); + if (rootCause instanceof ServerPlayer) { + return SpongeAdapter.adapt((ServerPlayer) rootCause); + } + if (rootCause instanceof Audience) { + return new SpongeCommandSender((Audience) rootCause); } - return new SpongeCommandSender(this, sender); - } - - /** - * Get the session for a player. - * - * @param player the player - * @return the session - */ - public LocalSession getSession(Player player) { - checkNotNull(player); - return WorldEdit.getInstance().getSessionManager().get(wrapPlayer(player)); + throw new UnsupportedOperationException("Cannot wrap " + rootCause.getClass()); } - /** - * Get the WorldEdit proxy for the given world. - * - * @param world the world - * @return the WorldEdit world - */ - public SpongeWorld getWorld(World world) { - checkNotNull(world); - return getAdapter().getWorld(world); - } /** * Get the WorldEdit proxy for the platform. @@ -367,13 +393,17 @@ public Platform getPlatform() { return this.platform; } + SpongePlatform getInternalPlatform() { + return this.platform; + } + /** * Get the working directory where WorldEdit's files are stored. * * @return the working directory */ public Path getWorkingDir() { - return this.workingDir.toPath(); + return this.workingDir; } /** @@ -382,7 +412,7 @@ public Path getWorkingDir() { * @return a version string */ String getInternalVersion() { - return container.getVersion().orElse("Unknown"); + return container.metadata().version().toString(); } public void setPermissionsProvider(SpongePermissionsProvider provider) { diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/ThreadSafeCache.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/ThreadSafeCache.java index fea9026657..b6f131444f 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/ThreadSafeCache.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/ThreadSafeCache.java @@ -20,7 +20,7 @@ package com.sk89q.worldedit.sponge; import org.spongepowered.api.Sponge; -import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import java.util.ArrayList; import java.util.List; @@ -49,8 +49,8 @@ public Set getOnlineIds() { public void run() { List onlineIds = new ArrayList<>(); - for (Player player : Sponge.getServer().getOnlinePlayers()) { - onlineIds.add(player.getUniqueId()); + for (ServerPlayer player : Sponge.server().onlinePlayers()) { + onlineIds.add(player.uniqueId()); } this.onlineIds = new CopyOnWriteArraySet<>(onlineIds); diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/SpongeImplAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/SpongeImplAdapter.java deleted file mode 100644 index fcb2dc333a..0000000000 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/SpongeImplAdapter.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.sponge.adapter; - -import com.flowpowered.math.vector.Vector3d; -import com.sk89q.worldedit.blocks.BaseItemStack; -import com.sk89q.worldedit.entity.BaseEntity; -import com.sk89q.worldedit.math.Vector3; -import com.sk89q.worldedit.sponge.SpongeWorld; -import com.sk89q.worldedit.util.Location; -import org.spongepowered.api.entity.Entity; -import org.spongepowered.api.item.inventory.ItemStack; -import org.spongepowered.api.world.World; - -/** - * An interface for various things that can't be done through the Sponge API. - */ -public interface SpongeImplAdapter { - - BaseEntity createBaseEntity(Entity entity); - - ItemStack makeSpongeStack(BaseItemStack itemStack); - - SpongeWorld getWorld(World world); - - default boolean isBest() { - return true; - } - - default Location adapt(org.spongepowered.api.world.Location loc, Vector3d rot) { - Vector3 position = Vector3.at(loc.getX(), loc.getY(), loc.getZ()); - - return new Location(getWorld(loc.getExtent()), position, (float) rot.getY(), (float) rot.getX()); - } -} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/SpongeImplLoader.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/SpongeImplLoader.java deleted file mode 100644 index 47303b9d1f..0000000000 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/adapter/SpongeImplLoader.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.sponge.adapter; - -import com.google.common.collect.Lists; -import com.sk89q.worldedit.internal.util.LogManagerCompat; -import com.sk89q.worldedit.util.io.Closer; -import org.apache.logging.log4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -/** - * Loads Sponge implementation adapters. - */ -public class SpongeImplLoader { - - private static final Logger LOGGER = LogManagerCompat.getLogger(); - private final List adapterCandidates = new ArrayList<>(); - private String customCandidate; - - private static final String SEARCH_PACKAGE = "com.sk89q.worldedit.sponge.adapter.impl"; - private static final String SEARCH_PACKAGE_DOT = SEARCH_PACKAGE + "."; - private static final String SEARCH_PATH = SEARCH_PACKAGE.replace(".", "/"); - private static final String CLASS_SUFFIX = ".class"; - - private static final String LOAD_ERROR_MESSAGE = - "\n**********************************************\n" - + "** This WorldEdit version does not support your version of Sponge.\n" - + "** WorldEdit will not function! \n" - + "** \n" - + "** Please ensure you are running the latest version\n" - + "**********************************************\n"; - - /** - * Create a new instance. - */ - public SpongeImplLoader() { - addDefaults(); - } - - /** - * Add default candidates, such as any defined with - * {@code -Dworldedit.sponge.adapter}. - */ - private void addDefaults() { - String className = System.getProperty("worldedit.sponge.adapter"); - if (className != null) { - customCandidate = className; - adapterCandidates.add(className); - LOGGER.info("-Dworldedit.sponge.adapter used to add " + className + " to the list of available Sponge adapters"); - } - } - - /** - * Search the given JAR for candidate implementations. - * - * @param file the file - * @throws IOException thrown on I/O error - */ - public void addFromJar(File file) throws IOException { - Closer closer = Closer.create(); - JarFile jar = closer.register(new JarFile(file)); - try { - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry jarEntry = (JarEntry) entries.nextElement(); - - String className = jarEntry.getName().replaceAll("[/\\\\]+", "."); - - if (!className.startsWith(SEARCH_PACKAGE_DOT) || jarEntry.isDirectory() || className.contains("$")) { - continue; - } - - int beginIndex = 0; - int endIndex = className.length() - CLASS_SUFFIX.length(); - className = className.substring(beginIndex, endIndex); - adapterCandidates.add(className); - } - } finally { - closer.close(); - } - } - - /** - * Search for classes stored as separate files available via the given - * class loader. - * - * @param classLoader the class loader - * @throws IOException thrown on error - */ - public void addFromPath(ClassLoader classLoader) throws IOException { - Enumeration resources = classLoader.getResources(SEARCH_PATH); - while (resources.hasMoreElements()) { - File file = new File(resources.nextElement().getFile()); - addFromPath(file); - } - } - - /** - * Search for classes stored as separate files available via the given - * path. - * - * @param file the path - */ - private void addFromPath(File file) { - String resource = SEARCH_PACKAGE_DOT + file.getName(); - if (file.isDirectory()) { - File[] files = file.listFiles(); - if (files != null) { - for (File child : files) { - addFromPath(child); - } - } - } else if (resource.endsWith(CLASS_SUFFIX)) { - int beginIndex = 0; - int endIndex = resource.length() - CLASS_SUFFIX.length(); - String className = resource.substring(beginIndex, endIndex); - if (!className.contains("$")) { - adapterCandidates.add(className); - } - } - } - - /** - * Iterate through the list of candidates and load an adapter. - * - * @return an adapter - * @throws AdapterLoadException thrown if no adapter could be found - */ - public SpongeImplAdapter loadAdapter() throws AdapterLoadException { - List suitableAdapters = Lists.newArrayList(); - for (String className : adapterCandidates) { - try { - Class cls = Class.forName(className); - if (SpongeImplAdapter.class.isAssignableFrom(cls)) { - suitableAdapters.add((SpongeImplAdapter) cls.newInstance()); - } else { - LOGGER.warn("Failed to load the Sponge adapter class '" + className - + "' because it does not implement " + SpongeImplAdapter.class.getCanonicalName()); - } - } catch (ClassNotFoundException e) { - LOGGER.warn("Failed to load the Sponge adapter class '" + className - + "' that is not supposed to be missing", e); - } catch (IllegalAccessException e) { - LOGGER.warn("Failed to load the Sponge adapter class '" + className - + "' that is not supposed to be raising this error", e); - } catch (Throwable e) { - if (className.equals(customCandidate)) { - LOGGER.warn("Failed to load the Sponge adapter class '" + className + "'", e); - } - } - } - - if (suitableAdapters.isEmpty()) { - throw new AdapterLoadException(LOAD_ERROR_MESSAGE); - } else { - if (suitableAdapters.size() == 1) { - return suitableAdapters.get(0); - } else { - return suitableAdapters.stream().sorted((o1, o2) -> { - if (o1.isBest() && !o2.isBest()) { - return -1; - } else if (!o1.isBest() && o2.isBest()) { - return 1; - } - return 0; - }).findFirst().orElse(suitableAdapters.get(0)); - } - } - } -} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java index 7435d69887..6c88509858 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/ConfigurateConfiguration.java @@ -19,17 +19,17 @@ package com.sk89q.worldedit.sponge.config; -import com.google.common.reflect.TypeToken; +import com.google.common.collect.ImmutableList; import com.sk89q.worldedit.LocalConfiguration; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.session.SessionManager; import com.sk89q.worldedit.util.report.Unreported; import com.sk89q.worldedit.world.registry.LegacyMapper; -import ninja.leaping.configurate.ConfigurationOptions; -import ninja.leaping.configurate.commented.CommentedConfigurationNode; -import ninja.leaping.configurate.loader.ConfigurationLoader; -import ninja.leaping.configurate.objectmapping.ObjectMappingException; import org.apache.logging.log4j.Logger; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.ConfigurationOptions; +import org.spongepowered.configurate.loader.ConfigurationLoader; +import org.spongepowered.configurate.serialize.SerializationException; import java.io.IOException; import java.util.HashSet; @@ -37,10 +37,13 @@ public class ConfigurateConfiguration extends LocalConfiguration { - @Unreported protected final ConfigurationLoader config; - @Unreported protected final Logger logger; + @Unreported + protected final ConfigurationLoader config; + @Unreported + protected final Logger logger; - @Unreported protected CommentedConfigurationNode node; + @Unreported + protected CommentedConfigurationNode node; public ConfigurateConfiguration(ConfigurationLoader config, Logger logger) { this.config = config; @@ -51,87 +54,94 @@ public ConfigurateConfiguration(ConfigurationLoader public void load() { try { ConfigurationOptions options = ConfigurationOptions.defaults(); - options = options.setShouldCopyDefaults(true); + options = options.shouldCopyDefaults(true); node = config.load(options); } catch (IOException e) { logger.warn("Error loading WorldEdit configuration", e); } - profile = node.getNode("debug").getBoolean(profile); - traceUnflushedSessions = node.getNode("debugging", "trace-unflushed-sessions").getBoolean(traceUnflushedSessions); - wandItem = node.getNode("wand-item").getString(wandItem).toLowerCase(Locale.ROOT); + profile = node.node("debug").getBoolean(profile); + traceUnflushedSessions = node.node("debugging", "trace-unflushed-sessions").getBoolean(traceUnflushedSessions); + wandItem = node.node("wand-item").getString(wandItem).toLowerCase(Locale.ROOT); try { wandItem = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(wandItem)).getId(); } catch (Throwable ignored) { } - defaultChangeLimit = Math.max(-1, node.getNode("limits", "max-blocks-changed", "default").getInt(defaultChangeLimit)); - maxChangeLimit = Math.max(-1, node.getNode("limits", "max-blocks-changed", "maximum").getInt(maxChangeLimit)); + defaultChangeLimit = Math.max(-1, node.node("limits", "max-blocks-changed", "default").getInt(defaultChangeLimit)); + maxChangeLimit = Math.max(-1, node.node("limits", "max-blocks-changed", "maximum").getInt(maxChangeLimit)); - defaultVerticalHeight = Math.max(1, node.getNode("limits", "vertical-height", "default").getInt(defaultVerticalHeight)); + defaultVerticalHeight = Math.max(1, node.node("limits", "vertical-height", "default").getInt(defaultVerticalHeight)); - defaultMaxPolygonalPoints = Math.max(-1, node.getNode("limits", "max-polygonal-points", "default").getInt(defaultMaxPolygonalPoints)); - maxPolygonalPoints = Math.max(-1, node.getNode("limits", "max-polygonal-points", "maximum").getInt(maxPolygonalPoints)); + defaultMaxPolygonalPoints = Math.max(-1, node.node("limits", "max-polygonal-points", "default").getInt(defaultMaxPolygonalPoints)); + maxPolygonalPoints = Math.max(-1, node.node("limits", "max-polygonal-points", "maximum").getInt(maxPolygonalPoints)); - maxRadius = Math.max(-1, node.getNode("limits", "max-radius").getInt(maxRadius)); - maxBrushRadius = node.getNode("limits", "max-brush-radius").getInt(maxBrushRadius); - maxSuperPickaxeSize = Math.max(1, node.getNode("limits", "max-super-pickaxe-size").getInt(maxSuperPickaxeSize)); + maxRadius = Math.max(-1, node.node("limits", "max-radius").getInt(maxRadius)); + maxBrushRadius = node.node("limits", "max-brush-radius").getInt(maxBrushRadius); + maxSuperPickaxeSize = Math.max(1, node.node("limits", "max-super-pickaxe-size").getInt(maxSuperPickaxeSize)); - butcherDefaultRadius = Math.max(-1, node.getNode("limits", "butcher-radius", "default").getInt(butcherDefaultRadius)); - butcherMaxRadius = Math.max(-1, node.getNode("limits", "butcher-radius", "maximum").getInt(butcherMaxRadius)); + butcherDefaultRadius = Math.max(-1, node.node("limits", "butcher-radius", "default").getInt(butcherDefaultRadius)); + butcherMaxRadius = Math.max(-1, node.node("limits", "butcher-radius", "maximum").getInt(butcherMaxRadius)); try { - disallowedBlocks = new HashSet<>(node.getNode("limits", "disallowed-blocks").getList(TypeToken.of(String.class))); - } catch (ObjectMappingException e) { + disallowedBlocks = new HashSet<>( + node.node("limits", "disallowed-blocks").getList( + String.class, + ImmutableList.copyOf(getDefaultDisallowedBlocks()) + ) + ); + } catch (SerializationException e) { logger.warn("Error loading WorldEdit configuration", e); } try { - allowedDataCycleBlocks = new HashSet<>(node.getNode("limits", "allowed-data-cycle-blocks").getList(TypeToken.of(String.class))); - } catch (ObjectMappingException e) { + allowedDataCycleBlocks = new HashSet<>( + node.node("limits", "allowed-data-cycle-blocks").getList(String.class, ImmutableList.of()) + ); + } catch (SerializationException e) { logger.warn("Error loading WorldEdit configuration", e); } - registerHelp = node.getNode("register-help").getBoolean(true); - logCommands = node.getNode("logging", "log-commands").getBoolean(logCommands); - logFile = node.getNode("logging", "file").getString(logFile); - logFormat = node.getNode("logging", "format").getString(logFormat); + registerHelp = node.node("register-help").getBoolean(true); + logCommands = node.node("logging", "log-commands").getBoolean(logCommands); + logFile = node.node("logging", "file").getString(logFile); + logFormat = node.node("logging", "format").getString(logFormat); - superPickaxeDrop = node.getNode("super-pickaxe", "drop-items").getBoolean(superPickaxeDrop); - superPickaxeManyDrop = node.getNode("super-pickaxe", "many-drop-items").getBoolean(superPickaxeManyDrop); + superPickaxeDrop = node.node("super-pickaxe", "drop-items").getBoolean(superPickaxeDrop); + superPickaxeManyDrop = node.node("super-pickaxe", "many-drop-items").getBoolean(superPickaxeManyDrop); - useInventory = node.getNode("use-inventory", "enable").getBoolean(useInventory); - useInventoryOverride = node.getNode("use-inventory", "allow-override").getBoolean(useInventoryOverride); - useInventoryCreativeOverride = node.getNode("use-inventory", "creative-mode-overrides").getBoolean(useInventoryCreativeOverride); + useInventory = node.node("use-inventory", "enable").getBoolean(useInventory); + useInventoryOverride = node.node("use-inventory", "allow-override").getBoolean(useInventoryOverride); + useInventoryCreativeOverride = node.node("use-inventory", "creative-mode-overrides").getBoolean(useInventoryCreativeOverride); - navigationWand = node.getNode("navigation-wand", "item").getString(navigationWand).toLowerCase(Locale.ROOT); + navigationWand = node.node("navigation-wand", "item").getString(navigationWand).toLowerCase(Locale.ROOT); try { navigationWand = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(navigationWand)).getId(); } catch (Throwable ignored) { } - navigationWandMaxDistance = node.getNode("navigation-wand", "max-distance").getInt(navigationWandMaxDistance); - navigationUseGlass = node.getNode("navigation", "use-glass").getBoolean(navigationUseGlass); + navigationWandMaxDistance = node.node("navigation-wand", "max-distance").getInt(navigationWandMaxDistance); + navigationUseGlass = node.node("navigation", "use-glass").getBoolean(navigationUseGlass); - scriptTimeout = node.getNode("scripting", "timeout").getInt(scriptTimeout); - scriptsDir = node.getNode("scripting", "dir").getString(scriptsDir); + scriptTimeout = node.node("scripting", "timeout").getInt(scriptTimeout); + scriptsDir = node.node("scripting", "dir").getString(scriptsDir); - saveDir = node.getNode("saving", "dir").getString(saveDir); + saveDir = node.node("saving", "dir").getString(saveDir); - allowSymlinks = node.getNode("files", "allow-symbolic-links").getBoolean(false); - LocalSession.MAX_HISTORY_SIZE = Math.max(0, node.getNode("history", "size").getInt(15)); - SessionManager.EXPIRATION_GRACE = node.getNode("history", "expiration").getInt(10) * 60 * 1000; + allowSymlinks = node.node("files", "allow-symbolic-links").getBoolean(false); + LocalSession.MAX_HISTORY_SIZE = Math.max(0, node.node("history", "size").getInt(15)); + SessionManager.EXPIRATION_GRACE = node.node("history", "expiration").getInt(10) * 60 * 1000; - showHelpInfo = node.getNode("show-help-on-first-use").getBoolean(true); - serverSideCUI = node.getNode("server-side-cui").getBoolean(true); + showHelpInfo = node.node("show-help-on-first-use").getBoolean(true); + serverSideCUI = node.node("server-side-cui").getBoolean(true); - String snapshotsDir = node.getNode("snapshots", "directory").getString(""); - boolean experimentalSnapshots = node.getNode("snapshots", "experimental").getBoolean(false); + String snapshotsDir = node.node("snapshots", "directory").getString(""); + boolean experimentalSnapshots = node.node("snapshots", "experimental").getBoolean(false); initializeSnapshotConfiguration(snapshotsDir, experimentalSnapshots); - String type = node.getNode("shell-save-type").getString("").trim(); + String type = node.node("shell-save-type").getString("").trim(); shellSaveType = type.equals("") ? null : type; - extendedYLimit = node.getNode("compat", "extended-y-limit").getBoolean(false); - setDefaultLocaleName(node.getNode("default-locale").getString(defaultLocaleName)); + extendedYLimit = node.node("compat", "extended-y-limit").getBoolean(false); + setDefaultLocaleName(node.node("default-locale").getString(defaultLocaleName)); } } diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/SpongeConfiguration.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/SpongeConfiguration.java index 05e9cd2e17..bb58a5d8c4 100644 --- a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/SpongeConfiguration.java +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/config/SpongeConfiguration.java @@ -21,10 +21,10 @@ import com.google.inject.Inject; import com.sk89q.worldedit.sponge.SpongeWorldEdit; -import ninja.leaping.configurate.commented.CommentedConfigurationNode; -import ninja.leaping.configurate.loader.ConfigurationLoader; import org.apache.logging.log4j.Logger; import org.spongepowered.api.config.DefaultConfig; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.loader.ConfigurationLoader; import java.io.IOException; import java.nio.file.Path; @@ -35,7 +35,9 @@ public class SpongeConfiguration extends ConfigurateConfiguration { public boolean cheatMode = false; @Inject - public SpongeConfiguration(@DefaultConfig(sharedRoot = false) ConfigurationLoader config, Logger logger) { + public SpongeConfiguration(@DefaultConfig(sharedRoot = false) + ConfigurationLoader config, + Logger logger) { super(config, logger); } @@ -43,8 +45,8 @@ public SpongeConfiguration(@DefaultConfig(sharedRoot = false) ConfigurationLoade public void load() { super.load(); - creativeEnable = node.getNode("use-in-creative").getBoolean(false); - cheatMode = node.getNode("cheat-mode").getBoolean(false); + creativeEnable = node.node("use-in-creative").getBoolean(false); + cheatMode = node.node("cheat-mode").getBoolean(false); try { config.save(node); diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/ExtendedChunk.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/ExtendedChunk.java new file mode 100644 index 0000000000..47e0451e27 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/ExtendedChunk.java @@ -0,0 +1,43 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge.internal; + +import com.sk89q.worldedit.util.SideEffect; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; + +import javax.annotation.Nullable; + +public interface ExtendedChunk { + /** + * {@link LevelChunk#setBlockState(BlockPos, BlockState, boolean)} with the extra + * {@link SideEffect#UPDATE} flag. + * + * @param pos the position to set + * @param state the state to set + * @param moved I honestly have no idea and can't be bothered to investigate, we pass {@code + * false} + * @param update the update flag, see side-effect for details + * @return the old block state, or {@code null} if unchanged + */ + @Nullable + BlockState setBlockState(BlockPos pos, BlockState state, boolean moved, boolean update); +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/LocaleResolver.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/LocaleResolver.java new file mode 100644 index 0000000000..4f7f4c8a54 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/LocaleResolver.java @@ -0,0 +1,35 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge.internal; + +import com.sk89q.worldedit.WorldEdit; +import net.kyori.adventure.audience.Audience; +import org.spongepowered.api.util.locale.LocaleSource; + +import java.util.Locale; + +public class LocaleResolver { + public static Locale resolveLocale(Audience audience) { + if (audience instanceof LocaleSource) { + return ((LocaleSource) audience).locale(); + } + return WorldEdit.getInstance().getConfiguration().defaultLocale; + } +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/NbtAdapter.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/NbtAdapter.java new file mode 100644 index 0000000000..5cf4229111 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/NbtAdapter.java @@ -0,0 +1,279 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge.internal; + +import com.sk89q.jnbt.ByteArrayTag; +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.CompoundTagBuilder; +import com.sk89q.jnbt.DoubleTag; +import com.sk89q.jnbt.EndTag; +import com.sk89q.jnbt.FloatTag; +import com.sk89q.jnbt.IntArrayTag; +import com.sk89q.jnbt.IntTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.ListTagBuilder; +import com.sk89q.jnbt.LongArrayTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import org.spongepowered.api.data.persistence.DataContainer; +import org.spongepowered.api.data.persistence.DataQuery; +import org.spongepowered.api.data.persistence.DataSerializable; +import org.spongepowered.api.data.persistence.DataView; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class NbtAdapter { + /** + * A separator to introduce errors if there is something to be separated. We should only see + * single-part keys. + */ + private static final String BREAKING_SEPARATOR = "if you see this, something is wrong"; + + public static CompoundTag adaptToWorldEdit(DataView view) { + CompoundTagBuilder builder = CompoundTagBuilder.create(); + for (Map.Entry entry : view.values(false).entrySet()) { + builder.put( + entry.getKey().asString(BREAKING_SEPARATOR), + adaptUnknownToWorldEdit(entry.getValue()) + ); + } + return builder.build(); + } + + private static Tag adaptUnknownToWorldEdit(Object object) { + if (object instanceof DataView) { + return adaptToWorldEdit((DataView) object); + } + if (object instanceof Boolean) { + return new ByteTag((byte) ((Boolean) object ? 1 : 0)); + } + if (object instanceof Byte) { + return new ByteTag((Byte) object); + } + if (object instanceof Short) { + return new ShortTag(((Short) object)); + } + if (object instanceof Integer) { + return new IntTag(((Integer) object)); + } + if (object instanceof Long) { + return new LongTag(((Long) object)); + } + if (object instanceof Float) { + return new FloatTag(((Float) object)); + } + if (object instanceof Double) { + return new DoubleTag(((Double) object)); + } + if (object instanceof String) { + return new StringTag((String) object); + } + if (object instanceof byte[]) { + return new ByteArrayTag(((byte[]) object)); + } + if (object instanceof Byte[]) { + Byte[] array = (Byte[]) object; + byte[] copy = new byte[array.length]; + for (int i = 0; i < copy.length; i++) { + copy[i] = array[i]; + } + return new ByteArrayTag(copy); + } + if (object instanceof int[]) { + return new IntArrayTag(((int[]) object)); + } + if (object instanceof Integer[]) { + Integer[] array = (Integer[]) object; + int[] copy = new int[array.length]; + for (int i = 0; i < copy.length; i++) { + copy[i] = array[i]; + } + return new IntArrayTag(copy); + } + if (object instanceof long[]) { + return new LongArrayTag(((long[]) object)); + } + if (object instanceof Long[]) { + Long[] array = (Long[]) object; + long[] copy = new long[array.length]; + for (int i = 0; i < copy.length; i++) { + copy[i] = array[i]; + } + return new LongArrayTag(copy); + } + if (object instanceof List) { + List objects = (List) object; + if (objects.isEmpty()) { + return new ListTag(EndTag.class, Collections.emptyList()); + } + Tag[] entries = new Tag[objects.size()]; + for (int i = 0; i < objects.size(); i++) { + Object value = objects.get(i); + entries[i] = adaptUnknownToWorldEdit(value); + } + return ListTagBuilder.createWith(entries).build(); + } + if (object instanceof Map) { + CompoundTagBuilder builder = CompoundTagBuilder.create(); + for (Map.Entry entry : ((Map) object).entrySet()) { + String key = entry.getKey() instanceof DataQuery + ? ((DataQuery) entry.getKey()).asString(BREAKING_SEPARATOR) + : entry.getKey().toString(); + builder.put(key, adaptUnknownToWorldEdit(entry.getValue())); + } + return builder.build(); + } + if (object instanceof DataSerializable) { + return adaptToWorldEdit(((DataSerializable) object).toContainer()); + } + throw new UnsupportedOperationException("Unable to translate into NBT: " + object.getClass()); + } + + public static DataContainer adaptFromWorldEdit(CompoundTag tag) { + // copy to container, no cloning used because it's unlikely to leak + // and it's cheaper this way + DataContainer container = DataContainer.createNew(DataView.SafetyMode.NO_DATA_CLONED); + for (Map.Entry entry : tag.getValue().entrySet()) { + container.set(DataQuery.of(entry.getKey()), adaptTagFromWorldEdit(entry.getValue())); + } + return container; + } + + private static Object adaptTagFromWorldEdit(Tag value) { + if (value instanceof ListTag) { + return ((ListTag) value).getValue().stream() + .map(NbtAdapter::adaptTagFromWorldEdit) + .collect(Collectors.toList()); + } + if (value instanceof CompoundTag) { + return adaptFromWorldEdit(((CompoundTag) value)); + } + // everything else is raw JDK types, so we can use it directly + return value.getValue(); + } + + public static net.minecraft.nbt.Tag adaptNMSToWorldEdit(Tag tag) { + if (tag instanceof IntArrayTag) { + return adaptNMSToWorldEdit((IntArrayTag) tag); + + } else if (tag instanceof ListTag) { + return adaptNMSToWorldEdit((ListTag) tag); + + } else if (tag instanceof LongTag) { + return adaptNMSToWorldEdit((LongTag) tag); + + } else if (tag instanceof LongArrayTag) { + return adaptNMSToWorldEdit((LongArrayTag) tag); + + } else if (tag instanceof StringTag) { + return adaptNMSToWorldEdit((StringTag) tag); + + } else if (tag instanceof IntTag) { + return adaptNMSToWorldEdit((IntTag) tag); + + } else if (tag instanceof ByteTag) { + return adaptNMSToWorldEdit((ByteTag) tag); + + } else if (tag instanceof ByteArrayTag) { + return adaptNMSToWorldEdit((ByteArrayTag) tag); + + } else if (tag instanceof CompoundTag) { + return adaptNMSToWorldEdit((CompoundTag) tag); + + } else if (tag instanceof FloatTag) { + return adaptNMSToWorldEdit((FloatTag) tag); + + } else if (tag instanceof ShortTag) { + return adaptNMSToWorldEdit((ShortTag) tag); + + } else if (tag instanceof DoubleTag) { + return adaptNMSToWorldEdit((DoubleTag) tag); + } else { + throw new IllegalArgumentException("Can't convert tag of type " + tag.getClass().getCanonicalName()); + } + } + + public static net.minecraft.nbt.IntArrayTag adaptNMSToWorldEdit(IntArrayTag tag) { + int[] value = tag.getValue(); + return new net.minecraft.nbt.IntArrayTag(Arrays.copyOf(value, value.length)); + } + + public static net.minecraft.nbt.ListTag adaptNMSToWorldEdit(ListTag tag) { + net.minecraft.nbt.ListTag list = new net.minecraft.nbt.ListTag(); + for (Tag child : tag.getValue()) { + if (child instanceof EndTag) { + continue; + } + list.add(adaptNMSToWorldEdit(child)); + } + return list; + } + + public static net.minecraft.nbt.LongTag adaptNMSToWorldEdit(LongTag tag) { + return net.minecraft.nbt.LongTag.valueOf(tag.getValue()); + } + + public static net.minecraft.nbt.LongArrayTag adaptNMSToWorldEdit(LongArrayTag tag) { + return new net.minecraft.nbt.LongArrayTag(tag.getValue().clone()); + } + + public static net.minecraft.nbt.StringTag adaptNMSToWorldEdit(StringTag tag) { + return net.minecraft.nbt.StringTag.valueOf(tag.getValue()); + } + + public static net.minecraft.nbt.IntTag adaptNMSToWorldEdit(IntTag tag) { + return net.minecraft.nbt.IntTag.valueOf(tag.getValue()); + } + + public static net.minecraft.nbt.ByteTag adaptNMSToWorldEdit(ByteTag tag) { + return net.minecraft.nbt.ByteTag.valueOf(tag.getValue()); + } + + public static net.minecraft.nbt.ByteArrayTag adaptNMSToWorldEdit(ByteArrayTag tag) { + return new net.minecraft.nbt.ByteArrayTag(tag.getValue().clone()); + } + + public static net.minecraft.nbt.CompoundTag adaptNMSToWorldEdit(CompoundTag tag) { + net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag(); + for (Map.Entry child : tag.getValue().entrySet()) { + compound.put(child.getKey(), adaptNMSToWorldEdit(child.getValue())); + } + return compound; + } + + public static net.minecraft.nbt.FloatTag adaptNMSToWorldEdit(FloatTag tag) { + return net.minecraft.nbt.FloatTag.valueOf(tag.getValue()); + } + + public static net.minecraft.nbt.ShortTag adaptNMSToWorldEdit(ShortTag tag) { + return net.minecraft.nbt.ShortTag.valueOf(tag.getValue()); + } + + public static net.minecraft.nbt.DoubleTag adaptNMSToWorldEdit(DoubleTag tag) { + return net.minecraft.nbt.DoubleTag.valueOf(tag.getValue()); + } +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeTransmogrifier.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeTransmogrifier.java new file mode 100644 index 0000000000..a557d40e96 --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeTransmogrifier.java @@ -0,0 +1,189 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge.internal; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; +import com.sk89q.worldedit.registry.state.BooleanProperty; +import com.sk89q.worldedit.registry.state.DirectionalProperty; +import com.sk89q.worldedit.registry.state.EnumProperty; +import com.sk89q.worldedit.registry.state.IntegerProperty; +import com.sk89q.worldedit.registry.state.Property; +import com.sk89q.worldedit.util.Direction; +import com.sk89q.worldedit.world.block.BlockState; +import com.sk89q.worldedit.world.block.BlockType; +import net.minecraft.util.StringRepresentable; +import org.spongepowered.api.ResourceKey; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.api.state.BooleanStateProperty; +import org.spongepowered.api.state.EnumStateProperty; +import org.spongepowered.api.state.IntegerStateProperty; +import org.spongepowered.api.state.StateProperty; + +import java.util.Comparator; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; +import java.util.stream.Collectors; + +/** + * Raw, un-cached transformations. + */ +public class SpongeTransmogrifier { + + private static final LoadingCache, Property> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader, Property>() { + @Override + public Property load(StateProperty property) throws Exception { + if (property instanceof BooleanStateProperty) { + return new BooleanProperty(property.name(), ImmutableList.copyOf(((BooleanStateProperty) property).possibleValues())); + } + if (property instanceof IntegerStateProperty) { + return new IntegerProperty(property.name(), ImmutableList.copyOf(((IntegerStateProperty) property).possibleValues())); + } + if (isDirectionProperty(property)) { + return new DirectionalProperty(property.name(), + ((EnumStateProperty) property).possibleValues().stream() + .map(x -> adaptDirection((net.minecraft.core.Direction) x)) + .collect(Collectors.toList()) + ); + } + if (property instanceof EnumStateProperty) { + return new EnumProperty(property.name(), ((EnumStateProperty) property).possibleValues().stream() + .map(x -> ((StringRepresentable) x).getSerializedName()) + .collect(Collectors.toList())); + } + throw new IllegalStateException("Unknown property type"); + } + }); + + public static Property transmogToWorldEditProperty(StateProperty property) { + return PROPERTY_CACHE.getUnchecked(property); + } + + private static Map, Object> transmogToWorldEditProperties(BlockType block, Map, ?> mcProps) { + Map, Object> props = new TreeMap<>(Comparator.comparing(Property::getName)); + for (Map.Entry, ?> prop : mcProps.entrySet()) { + Object value = prop.getValue(); + if (isDirectionProperty(prop.getKey())) { + value = adaptDirection((net.minecraft.core.Direction) value); + } else if (prop.getKey() instanceof EnumStateProperty) { + value = ((StringRepresentable) value).getSerializedName(); + } + props.put(block.getProperty(prop.getKey().name()), value); + } + return props; + } + + private static boolean isDirectionProperty(StateProperty property) { + return property instanceof EnumStateProperty + && property.valueClass().isAssignableFrom(net.minecraft.core.Direction.class); + } + + private static Direction adaptDirection(net.minecraft.core.Direction direction) { + switch (direction) { + case UP: + return Direction.UP; + case DOWN: + return Direction.DOWN; + case EAST: + return Direction.EAST; + case WEST: + return Direction.WEST; + case NORTH: + return Direction.NORTH; + case SOUTH: + return Direction.SOUTH; + default: + throw new AssertionError("New direction added: " + direction); + } + } + + private static net.minecraft.core.Direction adaptDirection(Direction direction) { + switch (direction) { + case UP: + return net.minecraft.core.Direction.UP; + case DOWN: + return net.minecraft.core.Direction.DOWN; + case EAST: + return net.minecraft.core.Direction.EAST; + case WEST: + return net.minecraft.core.Direction.WEST; + case NORTH: + return net.minecraft.core.Direction.NORTH; + case SOUTH: + return net.minecraft.core.Direction.SOUTH; + default: + throw new AssertionError("New direction added: " + direction); + } + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static org.spongepowered.api.block.BlockState transmogToMinecraftProperties( + org.spongepowered.api.block.BlockState newState, + Map, Object> states + ) { + org.spongepowered.api.block.BlockType type = newState.type(); + for (Map.Entry, Object> state : states.entrySet()) { + StateProperty property = type.findStateProperty(state.getKey().getName()) + .orElseThrow(() -> new IllegalStateException( + "Missing property in " + type + ": " + state.getKey().getName()) + ); + Comparable value = (Comparable) state.getValue(); + // we may need to adapt this value, depending on the source prop + if (state.getKey() instanceof DirectionalProperty) { + Direction dir = (Direction) value; + value = adaptDirection(dir); + } else if (state.getKey() instanceof EnumProperty) { + String enumName = (String) value; + value = property.parseValue(enumName).orElseThrow(() -> new IllegalStateException( + "Failed to parse '" + enumName + "' into " + state.getKey().getName() + )); + } + + Optional optional = + newState.withStateProperty((StateProperty) property, value); + newState = optional.orElseThrow(() -> new IllegalStateException( + "Failed to change state property " + property.name() + )); + } + return newState; + } + + public static org.spongepowered.api.block.BlockState transmogToMinecraft(BlockState blockState) { + org.spongepowered.api.block.BlockType mcBlock = Sponge.game().registry(RegistryTypes.BLOCK_TYPE) + .value(ResourceKey.resolve(blockState.getBlockType().getId())); + org.spongepowered.api.block.BlockState newState = mcBlock.defaultState(); + Map, Object> states = blockState.getStates(); + return transmogToMinecraftProperties(newState, states); + } + + public static BlockState transmogToWorldEdit(org.spongepowered.api.block.BlockState blockState) { + BlockType blockType = BlockType.REGISTRY.get( + blockState.type().key(RegistryTypes.BLOCK_TYPE).asString() + ); + return blockType.getState(transmogToWorldEditProperties(blockType, blockState.statePropertyMap())); + } + + private SpongeTransmogrifier() { + } +} diff --git a/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeWorldNativeAccess.java b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeWorldNativeAccess.java new file mode 100644 index 0000000000..0bd6436bfc --- /dev/null +++ b/worldedit-sponge/src/main/java/com/sk89q/worldedit/sponge/internal/SpongeWorldNativeAccess.java @@ -0,0 +1,160 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.sponge.internal; + +import com.sk89q.jnbt.AdventureNBTConverter; +import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.sponge.SpongeAdapter; +import com.sk89q.worldedit.util.SideEffect; +import com.sk89q.worldedit.util.SideEffectSet; +import com.sk89q.worldedit.util.nbt.CompoundBinaryTag; +import com.sk89q.worldedit.world.storage.ChunkStore; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; + +import java.lang.ref.WeakReference; +import java.util.Objects; +import javax.annotation.Nullable; + +public class SpongeWorldNativeAccess implements WorldNativeAccess { + private static final int UPDATE = 1; + private static final int NOTIFY = 2; + + private final WeakReference world; + private SideEffectSet sideEffectSet; + + public SpongeWorldNativeAccess(WeakReference world) { + this.world = world; + } + + private ServerLevel getWorld() { + return Objects.requireNonNull(world.get(), "The reference to the world was lost"); + } + + @Override + public void setCurrentSideEffectSet(SideEffectSet sideEffectSet) { + this.sideEffectSet = sideEffectSet; + } + + @Override + public LevelChunk getChunk(int x, int z) { + return getWorld().getChunk(x, z); + } + + @Override + public BlockState toNative(com.sk89q.worldedit.world.block.BlockState state) { + return (BlockState) SpongeAdapter.adapt(state); + } + + @Override + public BlockState getBlockState(LevelChunk chunk, BlockPos position) { + return chunk.getBlockState(position); + } + + @Nullable + @Override + public BlockState setBlockState(LevelChunk chunk, BlockPos position, BlockState state) { + if (chunk instanceof ExtendedChunk) { + return ((ExtendedChunk) chunk).setBlockState( + position, state, false, sideEffectSet.shouldApply(SideEffect.UPDATE) + ); + } + return chunk.setBlockState(position, state, false); + } + + @Override + public BlockState getValidBlockForPosition(BlockState block, BlockPos position) { + return Block.updateFromNeighbourShapes(block, getWorld(), position); + } + + @Override + public BlockPos getPosition(int x, int y, int z) { + return new BlockPos(x, y, z); + } + + @Override + public void updateLightingForBlock(BlockPos position) { + getWorld().getChunkSource().getLightEngine().checkBlock(position); + } + + @Override + public boolean updateTileEntity(BlockPos position, CompoundBinaryTag tag) { + CompoundTag nativeTag = (CompoundTag) NbtAdapter.adaptNMSToWorldEdit(AdventureNBTConverter.fromAdventure(tag)); + BlockEntity tileEntity = getWorld().getChunk(position).getBlockEntity(position); + if (tileEntity == null) { + return false; + } + tileEntity.setLevelAndPosition(getWorld(), position); + tileEntity.load(getWorld().getBlockState(position), nativeTag); + return true; + } + + @Override + public void notifyBlockUpdate(LevelChunk chunk, BlockPos position, BlockState oldState, BlockState newState) { + if (chunk.getSections()[position.getY() >> ChunkStore.CHUNK_SHIFTS] != null) { // TODO 1.17 - world.get().getSectionIndex(position.getY()) + getWorld().sendBlockUpdated(position, oldState, newState, UPDATE | NOTIFY); + } + } + + @Override + public boolean isChunkTicking(LevelChunk chunk) { + return chunk.getFullStatus().isOrAfter(ChunkHolder.FullChunkStatus.TICKING); + } + + @Override + public void markBlockChanged(LevelChunk chunk, BlockPos position) { + if (chunk.getSections()[position.getY() >> ChunkStore.CHUNK_SHIFTS] != null) { // TODO 1.17 - world.getSectionIndex(position.getY()) + getWorld().getChunkSource().blockChanged(position); + } + } + + @Override + public void notifyNeighbors(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().updateNeighborsAt(pos, oldState.getBlock()); + if (newState.hasAnalogOutputSignal()) { + getWorld().updateNeighbourForOutputSignal(pos, newState.getBlock()); + } + } + + @Override + public void updateBlock(BlockPos pos, BlockState oldState, BlockState newState) { + ServerLevel world = getWorld(); + newState.onPlace(world, pos, oldState, false); + } + + @Override + public void updateNeighbors(BlockPos pos, BlockState oldState, BlockState newState, int recursionLimit) { + ServerLevel world = getWorld(); + oldState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); + newState.updateIndirectNeighbourShapes(world, pos, NOTIFY, recursionLimit); + newState.updateNeighbourShapes(world, pos, NOTIFY, recursionLimit); + } + + @Override + public void onBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) { + getWorld().onBlockStateChange(pos, oldState, newState); + } +} diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$1.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$1.class deleted file mode 100644 index 0194fc40b5..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$1.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$SpongeNMSWorld.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$SpongeNMSWorld.class deleted file mode 100644 index 3b24d6c4df..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$SpongeNMSWorld.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$TileEntityBaseBlock.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$TileEntityBaseBlock.class deleted file mode 100644 index 247fa0d3a6..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl$TileEntityBaseBlock.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl.class deleted file mode 100644 index 456c101a41..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_10_Impl.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$1.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$1.class deleted file mode 100644 index 954d9f03a6..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$1.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$SpongeNMSWorld.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$SpongeNMSWorld.class deleted file mode 100644 index ab33bdfa78..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$SpongeNMSWorld.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$TileEntityBaseBlock.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$TileEntityBaseBlock.class deleted file mode 100644 index 05f63946b3..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl$TileEntityBaseBlock.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl.class deleted file mode 100644 index f26ed1ec62..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_2_Impl.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$1.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$1.class deleted file mode 100644 index 5b71777263..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$1.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$SpongeNMSWorld.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$SpongeNMSWorld.class deleted file mode 100644 index 4da53c21fa..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$SpongeNMSWorld.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$TileEntityBaseBlock.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$TileEntityBaseBlock.class deleted file mode 100644 index 0f0a4f1e8d..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl$TileEntityBaseBlock.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl.class deleted file mode 100644 index 61273203f1..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_11_Impl.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$1.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$1.class deleted file mode 100644 index 256a056619..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$1.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$SpongeNMSWorld.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$SpongeNMSWorld.class deleted file mode 100644 index 61ce0a7dbb..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$SpongeNMSWorld.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$TileEntityBaseBlock.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$TileEntityBaseBlock.class deleted file mode 100644 index 1c5b4c31ce..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl$TileEntityBaseBlock.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl.class deleted file mode 100644 index cdc162c16a..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_1_Impl.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$1.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$1.class deleted file mode 100644 index cb18d7835a..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$1.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$SpongeNMSWorld.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$SpongeNMSWorld.class deleted file mode 100644 index a684ee39b6..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$SpongeNMSWorld.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$TileEntityBaseBlock.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$TileEntityBaseBlock.class deleted file mode 100644 index 52178975f2..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl$TileEntityBaseBlock.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl.class deleted file mode 100644 index 6ac56f5fb7..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_2_Impl.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$1.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$1.class deleted file mode 100644 index d3e8da1147..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$1.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$SpongeNMSWorld.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$SpongeNMSWorld.class deleted file mode 100644 index 69e7875f20..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$SpongeNMSWorld.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$TileEntityBaseBlock.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$TileEntityBaseBlock.class deleted file mode 100644 index 2a5b4e62a6..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl$TileEntityBaseBlock.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl.class deleted file mode 100644 index 45e6a76bf5..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_1_12_Impl.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$1.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$1.class deleted file mode 100644 index dcd0e9bb1d..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$1.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$SpongeNMSWorld.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$SpongeNMSWorld.class deleted file mode 100644 index 00fad03ee5..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$SpongeNMSWorld.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$TileEntityBaseBlock.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$TileEntityBaseBlock.class deleted file mode 100644 index 8f7f5118a8..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl$TileEntityBaseBlock.class and /dev/null differ diff --git a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl.class b/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl.class deleted file mode 100644 index ad74081357..0000000000 Binary files a/worldedit-sponge/src/main/resources/com/sk89q/worldedit/sponge/adapter/impl/Sponge_Dev_Impl.class and /dev/null differ