diff --git a/build.gradle b/build.gradle index 80c9e31f2..dddd5470e 100644 --- a/build.gradle +++ b/build.gradle @@ -30,7 +30,7 @@ println """ 1) Read COMPILING.md if you haven't yet 2) Try running 'build' in a separate Gradle run 3) Use gradlew and not gradle - 4) If you still need help, ask on IRC! irc.esper.net #sk89q + 4) If you still need help, ask on Discord! https://discord.gg/enginehub Output files will be in [subproject]/build/libs ******************************************* @@ -85,7 +85,7 @@ subprojects { } } -configure(['core', 'bukkit', 'forge', 'sponge'].collect { project(":worldedit-$it") }) { +configure(['core', 'bukkit', 'forge', 'sponge', 'fabric'].collect { project(":worldedit-$it") }) { apply plugin: 'java' apply plugin: 'maven' apply plugin: 'checkstyle' @@ -118,7 +118,7 @@ configure(['core', 'bukkit', 'forge', 'sponge'].collect { project(":worldedit-$i archives javadocJar } - if (!(name.equals('worldedit-forge') || name.equals('worldedit-sponge'))) { + if (name == "worldedit-core" || name == "worldedit-bukkit") { task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' from sourceSets.main.allSource @@ -144,7 +144,7 @@ configure(['core', 'bukkit', 'forge', 'sponge'].collect { project(":worldedit-$i } } -configure(['bukkit', 'forge', 'sponge'].collect { project(":worldedit-$it") }) { +configure(['bukkit', 'forge', 'sponge', 'fabric'].collect { project(":worldedit-$it") }) { shadowJar { classifier 'dist' dependencies { diff --git a/config/checkstyle/import-control.xml b/config/checkstyle/import-control.xml index a291b5ad5..27b054a03 100644 --- a/config/checkstyle/import-control.xml +++ b/config/checkstyle/import-control.xml @@ -60,6 +60,18 @@ + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index dec0467a5..a7c14e8ee 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,7 +2,7 @@ rootProject.name = 'worldedit' include 'worldedit-libs' -['bukkit', 'core', 'forge', 'sponge'].forEach { +['bukkit', 'core', 'forge', 'sponge', 'fabric'].forEach { include "worldedit-libs:$it" include "worldedit-$it" } diff --git a/worldedit-fabric/build.gradle b/worldedit-fabric/build.gradle new file mode 100644 index 000000000..6045e3679 --- /dev/null +++ b/worldedit-fabric/build.gradle @@ -0,0 +1,105 @@ +import net.fabricmc.loom.task.RemapJarTask + +buildscript { + repositories { + jcenter() + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + maven { + name = 'sponge' + url = 'https://repo.spongepowered.org/maven' + } + } + + dependencies { + classpath 'net.fabricmc:fabric-loom:0.2.4-SNAPSHOT' + classpath 'org.spongepowered:mixin:0.7.11-SNAPSHOT' + } +} + +apply plugin: 'eclipse' +apply plugin: 'fabric-loom' + +def minecraftVersion = "1.14.2" +def fabricVersion = "0.3.0+build.185" +def yarnMappings = "1.14.2+build.7" +def loaderVersion = "0.4.8+build.155" + +configurations.all { Configuration it -> + it.resolutionStrategy { ResolutionStrategy rs -> + rs.force("com.google.guava:guava:21.0") + } +} + +dependencies { + compile project(':worldedit-core') + compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.8.1' + + minecraft "com.mojang:minecraft:${minecraftVersion}" + mappings "net.fabricmc:yarn:${yarnMappings}" + modCompile "net.fabricmc:fabric-loader:${loaderVersion}" + + modCompile "net.fabricmc.fabric-api:fabric-api:${fabricVersion}" + + testCompile group: 'org.mockito', name: 'mockito-core', version: '1.9.0-rc1' +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +minecraft { +} + +project.archivesBaseName = "${project.archivesBaseName}-mc${minecraftVersion}" + +processResources { + // this will ensure that this task is redone when the versions change. + inputs.property 'version', project.internalVersion + + from(sourceSets.main.resources.srcDirs) { + include "fabric.mod.json" + expand "version": project.internalVersion + } + + // copy everything else except the mod json + from(sourceSets.main.resources.srcDirs) { + exclude "fabric.mod.json" + } +} + +jar { + manifest { + attributes("Class-Path": "truezip.jar WorldEdit/truezip.jar js.jar WorldEdit/js.jar", + "WorldEdit-Version": version) + } +} + +shadowJar { + classifier = 'dist-dev' + dependencies { + relocate "org.slf4j", "com.sk89q.worldedit.slf4j" + relocate "org.apache.logging.slf4j", "com.sk89q.worldedit.log4jbridge" + + include(dependency('org.slf4j:slf4j-api')) + include(dependency("org.apache.logging.log4j:log4j-slf4j-impl")) + } +} + +task deobfJar(type: Jar) { + from sourceSets.main.output + classifier = 'dev' +} + +artifacts { + archives deobfJar +} + +task shadowJarRemap(type: RemapJarTask) { + input shadowJar.archivePath + output new File(shadowJar.archivePath.getAbsolutePath().replaceFirst('-dev\\.jar$', ".jar")) +} + +shadowJarRemap.dependsOn(shadowJar) +build.dependsOn(shadowJarRemap) \ No newline at end of file diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java new file mode 100644 index 000000000..8efa5ce97 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/CommandWrapper.java @@ -0,0 +1,128 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.sk89q.worldedit.fabric.FabricAdapter.adaptPlayer; +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +import com.google.common.collect.ImmutableList; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.context.StringRange; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestion; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.event.platform.CommandSuggestionEvent; +import com.sk89q.worldedit.extension.platform.Actor; +import com.sk89q.worldedit.internal.util.Substring; +import net.minecraft.entity.Entity; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import org.enginehub.piston.inject.InjectedValueStore; +import org.enginehub.piston.inject.Key; +import org.enginehub.piston.inject.MapBackedValueStore; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + + +public final class CommandWrapper { + + private CommandWrapper() { + } + + public static void register(CommandDispatcher dispatcher, org.enginehub.piston.Command command) { + ImmutableList.Builder aliases = ImmutableList.builder(); + aliases.add(command.getName()).addAll(command.getAliases()); + + Command commandRunner = + ctx -> { + WorldEdit.getInstance().getEventBus().post(new com.sk89q.worldedit.event.platform.CommandEvent( + adaptPlayer(ctx.getSource().getPlayer()), + ctx.getInput() + )); + return 0; + }; + + for (String alias : aliases.build()) { + LiteralArgumentBuilder base = literal(alias).executes(commandRunner) + .then(argument("args", StringArgumentType.greedyString()) + .suggests(CommandWrapper::suggest) + .executes(commandRunner)); + if (command.getCondition() != org.enginehub.piston.Command.Condition.TRUE) { + base.requires(requirementsFor(command)); + } + dispatcher.register(base); + } + } + + private static Predicate requirementsFor(org.enginehub.piston.Command mapping) { + return ctx -> { + final Entity entity = ctx.getEntity(); + if (!(entity instanceof ServerPlayerEntity)) { + return true; + } + final Actor actor = FabricAdapter.adaptPlayer(((ServerPlayerEntity) entity)); + InjectedValueStore store = MapBackedValueStore.create(); + store.injectValue(Key.of(Actor.class), context -> Optional.of(actor)); + return mapping.getCondition().satisfied(store); + }; + } + + private static CompletableFuture suggest(CommandContext context, + SuggestionsBuilder builder) throws CommandSyntaxException { + CommandSuggestionEvent event = new CommandSuggestionEvent( + FabricAdapter.adaptPlayer(context.getSource().getPlayer()), + builder.getInput() + ); + WorldEdit.getInstance().getEventBus().post(event); + List suggestions = event.getSuggestions(); + + ImmutableList.Builder result = ImmutableList.builder(); + + for (Substring suggestion : suggestions) { + String suggestionText = suggestion.getSubstring(); + // If at end, we are actually suggesting the next argument + // Ensure there is a space! + if (suggestion.getStart() == suggestion.getEnd() + && suggestion.getEnd() == builder.getInput().length() + && !builder.getInput().endsWith(" ")) { + suggestionText = " " + suggestionText; + } + result.add(new Suggestion( + StringRange.between(suggestion.getStart(), suggestion.getEnd()), + suggestionText + )); + } + + return CompletableFuture.completedFuture( + Suggestions.create(builder.getInput(), result.build()) + ); + } + +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java new file mode 100644 index 000000000..317274331 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java @@ -0,0 +1,241 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableList; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +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.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.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.item.ItemTypes; +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.state.StateFactory; +import net.minecraft.state.property.DirectionProperty; +import net.minecraft.util.Identifier; +import net.minecraft.util.StringIdentifiable; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.biome.Biome; + +import java.util.Comparator; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public final class FabricAdapter { + + private FabricAdapter() { + } + + public static World adapt(net.minecraft.world.World world) { + return new FabricWorld(world); + } + + public static Biome adapt(BiomeType biomeType) { + return Registry.BIOME.get(new Identifier(biomeType.getId())); + } + + public static BiomeType adapt(Biome biome) { + return BiomeTypes.get(Registry.BIOME.getId(biome).toString()); + } + + public static Vector3 adapt(Vec3d vector) { + return Vector3.at(vector.x, vector.y, vector.z); + } + + public static BlockVector3 adapt(BlockPos pos) { + return BlockVector3.at(pos.getX(), pos.getY(), pos.getZ()); + } + + public static Vec3d toVec3(BlockVector3 vector) { + return new Vec3d(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); + } + + public static net.minecraft.util.math.Direction adapt(Direction face) { + switch (face) { + case NORTH: return net.minecraft.util.math.Direction.NORTH; + case SOUTH: return net.minecraft.util.math.Direction.SOUTH; + case WEST: return net.minecraft.util.math.Direction.WEST; + case EAST: return net.minecraft.util.math.Direction.EAST; + case DOWN: return net.minecraft.util.math.Direction.DOWN; + case UP: + default: + return net.minecraft.util.math.Direction.UP; + } + } + + public static Direction adaptEnumFacing(net.minecraft.util.math.Direction face) { + switch (face) { + case NORTH: return Direction.NORTH; + case SOUTH: return Direction.SOUTH; + case WEST: return Direction.WEST; + case EAST: return Direction.EAST; + case DOWN: return Direction.DOWN; + case UP: + default: + return Direction.UP; + } + } + + public static BlockPos toBlockPos(BlockVector3 vector) { + return new BlockPos(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); + } + + public static Property adaptProperty(net.minecraft.state.property.Property property) { + if (property instanceof net.minecraft.state.property.BooleanProperty) { + return new BooleanProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.state.property.BooleanProperty) property).getValues())); + } + if (property instanceof net.minecraft.state.property.IntProperty) { + return new IntegerProperty(property.getName(), ImmutableList.copyOf(((net.minecraft.state.property.IntProperty) property).getValues())); + } + if (property instanceof DirectionProperty) { + return new DirectionalProperty(property.getName(), ((DirectionProperty) property).getValues().stream() + .map(FabricAdapter::adaptEnumFacing) + .collect(Collectors.toList())); + } + if (property instanceof net.minecraft.state.property.EnumProperty) { + // Note: do not make x.asString a method reference. + // It will cause runtime bootstrap exceptions. + return new EnumProperty(property.getName(), ((net.minecraft.state.property.EnumProperty) property).getValues().stream() + .map(x -> x.asString()) + .collect(Collectors.toList())); + } + return new PropertyAdapter<>(property); + } + + public static Map, Object> adaptProperties(BlockType block, Map, Comparable> mcProps) { + Map, Object> props = new TreeMap<>(Comparator.comparing(Property::getName)); + for (Map.Entry, Comparable> prop : mcProps.entrySet()) { + Object value = prop.getValue(); + if (prop.getKey() instanceof DirectionProperty) { + value = adaptEnumFacing((net.minecraft.util.math.Direction) value); + } else if (prop.getKey() instanceof net.minecraft.state.property.EnumProperty) { + value = ((StringIdentifiable) value).asString(); + } + props.put(block.getProperty(prop.getKey().getName()), value); + } + return props; + } + + private static net.minecraft.block.BlockState applyProperties(StateFactory stateContainer, + net.minecraft.block.BlockState newState, Map, Object> states) { + for (Map.Entry, Object> state : states.entrySet()) { + net.minecraft.state.property.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.state.property.EnumProperty) { + String enumName = (String) value; + value = ((net.minecraft.state.property.EnumProperty) property).getValue((String) value).orElseGet(() -> { + throw new IllegalStateException("Enum property " + property.getName() + " does not contain " + enumName); + }); + } + + newState = newState.with(property, value); + } + return newState; + } + + public static net.minecraft.block.BlockState adapt(BlockState blockState) { + Block mcBlock = adapt(blockState.getBlockType()); + net.minecraft.block.BlockState newState = mcBlock.getDefaultState(); + Map, Object> states = blockState.getStates(); + return applyProperties(mcBlock.getStateFactory(), newState, states); + } + + public static BlockState adapt(net.minecraft.block.BlockState blockState) { + BlockType blockType = adapt(blockState.getBlock()); + return blockType.getState(adaptProperties(blockType, blockState.getEntries())); + } + + public static Block adapt(BlockType blockType) { + return Registry.BLOCK.get(new Identifier(blockType.getId())); + } + + public static BlockType adapt(Block block) { + return BlockTypes.get(Registry.BLOCK.getId(block).toString()); + } + + public static Item adapt(ItemType itemType) { + return Registry.ITEM.get(new Identifier(itemType.getId())); + } + + public static ItemType adapt(Item item) { + return ItemTypes.get(Registry.ITEM.getId(item).toString()); + } + + public static ItemStack adapt(BaseItemStack baseItemStack) { + net.minecraft.nbt.CompoundTag fabricCompound = null; + if (baseItemStack.getNbtData() != null) { + fabricCompound = NBTConverter.toNative(baseItemStack.getNbtData()); + } + final ItemStack itemStack = new ItemStack(adapt(baseItemStack.getType()), baseItemStack.getAmount()); + itemStack.setTag(fabricCompound); + return itemStack; + } + + public static BaseItemStack adapt(ItemStack itemStack) { + CompoundTag tag = NBTConverter.fromNative(itemStack.toTag(new net.minecraft.nbt.CompoundTag())); + if (tag.getValue().isEmpty()) { + tag = null; + } else { + final Tag tagTag = tag.getValue().get("tag"); + if (tagTag instanceof CompoundTag) { + tag = ((CompoundTag) tagTag); + } else { + tag = null; + } + } + return new BaseItemStack(adapt(itemStack.getItem()), tag, itemStack.getCount()); + } + + /** + * Get the WorldEdit proxy for the given player. + * + * @param player the player + * @return the WorldEdit player + */ + public static FabricPlayer adaptPlayer(ServerPlayerEntity player) { + checkNotNull(player); + return new FabricPlayer(player); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBiomeRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBiomeRegistry.java new file mode 100644 index 000000000..fbf883101 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBiomeRegistry.java @@ -0,0 +1,58 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.world.biome.BiomeData; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.registry.BiomeRegistry; +import net.minecraft.world.biome.Biome; + +/** + * Provides access to biome data in Fabric. + */ +class FabricBiomeRegistry implements BiomeRegistry { + + @Override + public BiomeData getData(BiomeType biome) { + return new FabricBiomeData(FabricAdapter.adapt(biome)); + } + + /** + * Cached biome data information. + */ + private static class FabricBiomeData implements BiomeData { + private final Biome biome; + + /** + * Create a new instance. + * + * @param biome the base biome + */ + private FabricBiomeData(Biome biome) { + this.biome = biome; + } + + @Override + public String getName() { + return biome.getTextComponent().getString(); + } + } + +} \ No newline at end of file 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 new file mode 100644 index 000000000..bbf6ffbd3 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockCategoryRegistry.java @@ -0,0 +1,46 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.registry.Category; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.registry.BlockCategoryRegistry; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.Tag; +import net.minecraft.util.Identifier; + +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.getContainer().get(new Identifier(category))) + .map(Tag::values).orElse(Collections.emptySet()) + .stream().map(FabricAdapter::adapt).collect(Collectors.toSet()); + } + + @Override + public Set getAll(Category category) { + return getCategorisedByName(category.getId()); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockMaterial.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockMaterial.java new file mode 100644 index 000000000..c9fc86e02 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockMaterial.java @@ -0,0 +1,93 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.world.registry.BlockMaterial; +import com.sk89q.worldedit.world.registry.PassthroughBlockMaterial; +import net.minecraft.block.Material; +import net.minecraft.block.piston.PistonBehavior; + +import javax.annotation.Nullable; + +/** + * Fabric 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 FabricBlockMaterial extends PassthroughBlockMaterial { + + private final Material delegate; + + public FabricBlockMaterial(Material delegate, @Nullable BlockMaterial secondary) { + super(secondary); + this.delegate = delegate; + } + + @Override + public boolean isAir() { + return delegate == Material.AIR || super.isAir(); + } + + @Override + public boolean isOpaque() { + return delegate.blocksLight(); + } + + @Override + public boolean isLiquid() { + return delegate.isLiquid(); + } + + @Override + public boolean isSolid() { + return delegate.isSolid(); + } + + @Override + public boolean isFragileWhenPushed() { + return delegate.getPistonBehavior() == PistonBehavior.DESTROY; + } + + @Override + public boolean isUnpushable() { + return delegate.getPistonBehavior() == PistonBehavior.BLOCK; + } + + @Override + public boolean isMovementBlocker() { + return delegate.blocksMovement(); + } + + @Override + public boolean isBurnable() { + return delegate.isBurnable(); + } + + @Override + public boolean isToolRequired() { + return !delegate.canBreakByHand(); + } + + @Override + public boolean isReplacedDuringPlacement() { + return delegate.isReplaceable(); + } + +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockRegistry.java new file mode 100644 index 000000000..1d219c90c --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricBlockRegistry.java @@ -0,0 +1,80 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.registry.state.Property; +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.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.block.Block; +import net.minecraft.block.Material; + +import javax.annotation.Nullable; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.OptionalInt; +import java.util.TreeMap; + +public class FabricBlockRegistry extends BundledBlockRegistry { + + private Map materialMap = new HashMap<>(); + + @Nullable + @Override + public String getName(BlockType blockType) { + Block block = FabricAdapter.adapt(blockType); + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { + return block.getTextComponent().getFormattedText(); + } else { + return super.getName(blockType); + } + } + + @Override + public BlockMaterial getMaterial(BlockType blockType) { + Block block = FabricAdapter.adapt(blockType); + return materialMap.computeIfAbsent(block.getDefaultState().getMaterial(), + m -> new FabricBlockMaterial(m, super.getMaterial(blockType))); + } + + @Override + public Map> getProperties(BlockType blockType) { + Block block = FabricAdapter.adapt(blockType); + Map> map = new TreeMap<>(); + Collection> propertyKeys = block + .getDefaultState() + .getProperties(); + for (net.minecraft.state.property.Property key : propertyKeys) { + map.put(key.getName(), FabricAdapter.adaptProperty(key)); + } + return map; + } + + @Override + public OptionalInt getInternalBlockStateId(BlockState state) { + net.minecraft.block.BlockState equivalent = FabricAdapter.adapt(state); + return OptionalInt.of(Block.getRawIdFromState(equivalent)); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricConfiguration.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricConfiguration.java new file mode 100644 index 000000000..ab00d6f89 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricConfiguration.java @@ -0,0 +1,45 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.util.PropertiesConfiguration; + +import java.io.File; + +public class FabricConfiguration extends PropertiesConfiguration { + + public boolean creativeEnable = false; + public boolean cheatMode = false; + + public FabricConfiguration(FabricWorldEdit mod) { + super(new File(mod.getWorkingDir(), "worldedit.properties")); + } + + @Override + protected void loadExtra() { + creativeEnable = getBool("use-in-creative", false); + cheatMode = getBool("cheat-mode", false); + } + + @Override + public File getWorkingDirectory() { + return FabricWorldEdit.inst.getWorkingDir(); + } +} \ No newline at end of file diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java new file mode 100644 index 000000000..c04e06840 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricDataFixer.java @@ -0,0 +1,2728 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.google.common.base.Strings; +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.Dynamic; +import com.mojang.datafixers.schemas.Schema; +import com.sk89q.jnbt.CompoundTag; +import net.minecraft.datafixers.NbtOps; +import net.minecraft.datafixers.Schemas; +import net.minecraft.datafixers.TypeReferences; +import net.minecraft.nbt.FloatTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.util.DyeColor; +import net.minecraft.util.Identifier; +import net.minecraft.util.JsonHelper; +import net.minecraft.util.math.Direction; +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("UnnecessarilyQualifiedStaticUsage") +class FabricDataFixer 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 = NBTConverter.toNative(originalChunk); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.CHUNK, tag, srcVer); + return NBTConverter.fromNative(fixed); + } + + private CompoundTag fixBlockEntity(CompoundTag origTileEnt, int srcVer) { + net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(origTileEnt); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.BLOCK_ENTITY, tag, srcVer); + return NBTConverter.fromNative(fixed); + } + + private CompoundTag fixEntity(CompoundTag origEnt, int srcVer) { + net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(origEnt); + net.minecraft.nbt.CompoundTag fixed = convert(LegacyType.ENTITY, tag, srcVer); + return NBTConverter.fromNative(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(TypeReferences.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.containsKey("Properties", 10)) { + sb.append('['); + net.minecraft.nbt.CompoundTag props = tagCompound.getCompound("Properties"); + sb.append(props.getKeys().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, TypeReferences.BIOME); + } + + private String fixItemType(String key, int srcVer) { + return fixName(key, srcVer, TypeReferences.ITEM_NAME); + } + + private static String fixName(String key, int srcVer, TypeReference type) { + return INSTANCE.fixer.update(type, new Dynamic<>(OPS_NBT, new StringTag(key)), srcVer, DATA_VERSION) + .asString().orElse(key); + } + + private static final NbtOps OPS_NBT = NbtOps.INSTANCE; + private static final int LEGACY_VERSION = 1343; + private static int DATA_VERSION; + private static FabricDataFixer 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(TypeReferences.LEVEL), + PLAYER(TypeReferences.PLAYER), + CHUNK(TypeReferences.CHUNK), + BLOCK_ENTITY(TypeReferences.BLOCK_ENTITY), + ENTITY(TypeReferences.ENTITY), + ITEM_INSTANCE(TypeReferences.ITEM_STACK), + OPTIONS(TypeReferences.OPTIONS), + STRUCTURE(TypeReferences.STRUCTURE); + + private final TypeReference type; + + LegacyType(TypeReference type) { + this.type = type; + DFU_TO_LEGACY.put(type.typeName(), this); + } + + public TypeReference getDFUType() { + return type; + } + } + + FabricDataFixer(int dataVersion) { + super(dataVersion); + DATA_VERSION = dataVersion; + INSTANCE = this; + registerConverters(); + registerInspectors(); + this.fixer = new WrappedDataFixer(Schemas.getFixer()); + } + + @Override + public DataFixer build(final Executor executor) { + return fixer; + } + + 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 = FabricDataFixer.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 = FabricDataFixer.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.containsKey("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 Identifier("item")); + map.put("EntityExperienceOrb", new Identifier("xp_orb")); + map.put("EntityAreaEffectCloud", new Identifier("area_effect_cloud")); + map.put("EntityGuardianElder", new Identifier("elder_guardian")); + map.put("EntitySkeletonWither", new Identifier("wither_skeleton")); + map.put("EntitySkeletonStray", new Identifier("stray")); + map.put("EntityEgg", new Identifier("egg")); + map.put("EntityLeash", new Identifier("leash_knot")); + map.put("EntityPainting", new Identifier("painting")); + map.put("EntityTippedArrow", new Identifier("arrow")); + map.put("EntitySnowball", new Identifier("snowball")); + map.put("EntityLargeFireball", new Identifier("fireball")); + map.put("EntitySmallFireball", new Identifier("small_fireball")); + map.put("EntityEnderPearl", new Identifier("ender_pearl")); + map.put("EntityEnderSignal", new Identifier("eye_of_ender_signal")); + map.put("EntityPotion", new Identifier("potion")); + map.put("EntityThrownExpBottle", new Identifier("xp_bottle")); + map.put("EntityItemFrame", new Identifier("item_frame")); + map.put("EntityWitherSkull", new Identifier("wither_skull")); + map.put("EntityTNTPrimed", new Identifier("tnt")); + map.put("EntityFallingBlock", new Identifier("falling_block")); + map.put("EntityFireworks", new Identifier("fireworks_rocket")); + map.put("EntityZombieHusk", new Identifier("husk")); + map.put("EntitySpectralArrow", new Identifier("spectral_arrow")); + map.put("EntityShulkerBullet", new Identifier("shulker_bullet")); + map.put("EntityDragonFireball", new Identifier("dragon_fireball")); + map.put("EntityZombieVillager", new Identifier("zombie_villager")); + map.put("EntityHorseSkeleton", new Identifier("skeleton_horse")); + map.put("EntityHorseZombie", new Identifier("zombie_horse")); + map.put("EntityArmorStand", new Identifier("armor_stand")); + map.put("EntityHorseDonkey", new Identifier("donkey")); + map.put("EntityHorseMule", new Identifier("mule")); + map.put("EntityEvokerFangs", new Identifier("evocation_fangs")); + map.put("EntityEvoker", new Identifier("evocation_illager")); + map.put("EntityVex", new Identifier("vex")); + map.put("EntityVindicator", new Identifier("vindication_illager")); + map.put("EntityIllagerIllusioner", new Identifier("illusion_illager")); + map.put("EntityMinecartCommandBlock", new Identifier("commandblock_minecart")); + map.put("EntityBoat", new Identifier("boat")); + map.put("EntityMinecartRideable", new Identifier("minecart")); + map.put("EntityMinecartChest", new Identifier("chest_minecart")); + map.put("EntityMinecartFurnace", new Identifier("furnace_minecart")); + map.put("EntityMinecartTNT", new Identifier("tnt_minecart")); + map.put("EntityMinecartHopper", new Identifier("hopper_minecart")); + map.put("EntityMinecartMobSpawner", new Identifier("spawner_minecart")); + map.put("EntityCreeper", new Identifier("creeper")); + map.put("EntitySkeleton", new Identifier("skeleton")); + map.put("EntitySpider", new Identifier("spider")); + map.put("EntityGiantZombie", new Identifier("giant")); + map.put("EntityZombie", new Identifier("zombie")); + map.put("EntitySlime", new Identifier("slime")); + map.put("EntityGhast", new Identifier("ghast")); + map.put("EntityPigZombie", new Identifier("zombie_pigman")); + map.put("EntityEnderman", new Identifier("enderman")); + map.put("EntityCaveSpider", new Identifier("cave_spider")); + map.put("EntitySilverfish", new Identifier("silverfish")); + map.put("EntityBlaze", new Identifier("blaze")); + map.put("EntityMagmaCube", new Identifier("magma_cube")); + map.put("EntityEnderDragon", new Identifier("ender_dragon")); + map.put("EntityWither", new Identifier("wither")); + map.put("EntityBat", new Identifier("bat")); + map.put("EntityWitch", new Identifier("witch")); + map.put("EntityEndermite", new Identifier("endermite")); + map.put("EntityGuardian", new Identifier("guardian")); + map.put("EntityShulker", new Identifier("shulker")); + map.put("EntityPig", new Identifier("pig")); + map.put("EntitySheep", new Identifier("sheep")); + map.put("EntityCow", new Identifier("cow")); + map.put("EntityChicken", new Identifier("chicken")); + map.put("EntitySquid", new Identifier("squid")); + map.put("EntityWolf", new Identifier("wolf")); + map.put("EntityMushroomCow", new Identifier("mooshroom")); + map.put("EntitySnowman", new Identifier("snowman")); + map.put("EntityOcelot", new Identifier("ocelot")); + map.put("EntityIronGolem", new Identifier("villager_golem")); + map.put("EntityHorse", new Identifier("horse")); + map.put("EntityRabbit", new Identifier("rabbit")); + map.put("EntityPolarBear", new Identifier("polar_bear")); + map.put("EntityLlama", new Identifier("llama")); + map.put("EntityLlamaSpit", new Identifier("llama_spit")); + map.put("EntityParrot", new Identifier("parrot")); + map.put("EntityVillager", new Identifier("villager")); + map.put("EntityEnderCrystal", new Identifier("ender_crystal")); + map.put("TileEntityFurnace", new Identifier("furnace")); + map.put("TileEntityChest", new Identifier("chest")); + map.put("TileEntityEnderChest", new Identifier("ender_chest")); + map.put("TileEntityRecordPlayer", new Identifier("jukebox")); + map.put("TileEntityDispenser", new Identifier("dispenser")); + map.put("TileEntityDropper", new Identifier("dropper")); + map.put("TileEntitySign", new Identifier("sign")); + map.put("TileEntityMobSpawner", new Identifier("mob_spawner")); + map.put("TileEntityNote", new Identifier("noteblock")); + map.put("TileEntityPiston", new Identifier("piston")); + map.put("TileEntityBrewingStand", new Identifier("brewing_stand")); + map.put("TileEntityEnchantTable", new Identifier("enchanting_table")); + map.put("TileEntityEnderPortal", new Identifier("end_portal")); + map.put("TileEntityBeacon", new Identifier("beacon")); + map.put("TileEntitySkull", new Identifier("skull")); + map.put("TileEntityLightDetector", new Identifier("daylight_detector")); + map.put("TileEntityHopper", new Identifier("hopper")); + map.put("TileEntityComparator", new Identifier("comparator")); + map.put("TileEntityFlowerPot", new Identifier("flower_pot")); + map.put("TileEntityBanner", new Identifier("banner")); + map.put("TileEntityStructure", new Identifier("structure_block")); + map.put("TileEntityEndGateway", new Identifier("end_gateway")); + map.put("TileEntityCommand", new Identifier("command_block")); + map.put("TileEntityShulkerBox", new Identifier("shulker_box")); + map.put("TileEntityBed", new Identifier("bed")); + } + + private static Identifier getKey(String type) { + final Identifier 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.containsKey(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.containsKey(key, 9)) { + ListTag nbttaglist = nbttagcompound.getList(key, 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.add(j, convert(LegacyType.ITEM_INSTANCE, nbttaglist.getCompoundTag(j), sourceVer, targetVer)); + } + } + + } + + private static class DataConverterEquipment implements DataConverter { + + DataConverterEquipment() {} + + @Override + public int getDataVersion() { + return 100; + } + + @Override + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + ListTag nbttaglist = cmp.getList("Equipment", 10); + ListTag nbttaglist1; + + if (!nbttaglist.isEmpty() && !cmp.containsKey("HandItems", 10)) { + nbttaglist1 = new ListTag(); + nbttaglist1.add(nbttaglist.get(0)); + nbttaglist1.add(new net.minecraft.nbt.CompoundTag()); + cmp.put("HandItems", nbttaglist1); + } + + if (nbttaglist.size() > 1 && !cmp.containsKey("ArmorItem", 10)) { + nbttaglist1 = new 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.containsKey("DropChances", 9)) { + nbttaglist1 = cmp.getList("DropChances", 5); + ListTag nbttaglist2; + + if (!cmp.containsKey("HandDropChances", 10)) { + nbttaglist2 = new ListTag(); + nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(0))); + nbttaglist2.add(new FloatTag(0.0F)); + cmp.put("HandDropChances", nbttaglist2); + } + + if (!cmp.containsKey("ArmorDropChances", 10)) { + nbttaglist2 = new ListTag(); + nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(1))); + nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(2))); + nbttaglist2.add(new FloatTag(nbttaglist1.getFloat(3))); + nbttaglist2.add(new FloatTag(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 Identifier(s).toString(); + if (i < 515 && DataInspectorBlockEntity.b.containsKey(key)) { + return DataInspectorBlockEntity.b.get(key); + } else { + return DataInspectorBlockEntity.c.get(key); + } + } + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (!cmp.containsKey("tag", 10)) { + return cmp; + } else { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.containsKey("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.containsKey("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(FabricDataFixer.class); + + DataInspectorEntity() {} + + @Override + 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.containsKey("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; + + if (s1 == null) { + DataInspectorEntity.a.warn("Unable to resolve Entity for ItemInstance: {}", s); + flag = false; + } else { + flag = !nbttagcompound2.containsKey("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 Identifier 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 Identifier(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) { + FabricDataFixer.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) { + FabricDataFixer.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.containsKey("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"; + } + } + + 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.containsKey("tag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.containsKey("BlockEntityTag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttagcompound1.getCompound("BlockEntityTag"); + + if (nbttagcompound2.containsKey("Base", 99)) { + cmp.putShort("Damage", (short) (nbttagcompound2.getShort("Base") & 15)); + if (nbttagcompound1.containsKey("display", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound3 = nbttagcompound1.getCompound("display"); + + if (nbttagcompound3.containsKey("Lore", 9)) { + 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.containsKey("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.containsKey("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(new String[] { "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.containsKey("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.containsKey("SpawnPotentials", 9)) { + ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int i = 0; i < nbttaglist.size(); ++i) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompoundTag(i); + + if (nbttagcompound2.containsKey("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.containsKey("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.containsKey("HealF", 99)) { + f = cmp.getFloat("HealF"); + cmp.remove("HealF"); + } else { + if (!cmp.containsKey("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.containsKey("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.containsKey("Facing", 99)) { + Direction enumdirection; + + if (cmp.containsKey("Direction", 99)) { + enumdirection = Direction.fromHorizontal(cmp.getByte("Direction")); + cmp.putInt("TileX", cmp.getInt("TileX") + enumdirection.getOffsetX()); + cmp.putInt("TileY", cmp.getInt("TileY") + enumdirection.getOffsetY()); + cmp.putInt("TileZ", cmp.getInt("TileZ") + enumdirection.getOffsetZ()); + cmp.remove("Direction"); + if (flag1 && cmp.containsKey("ItemRotation", 99)) { + cmp.putByte("ItemRotation", (byte) (cmp.getByte("ItemRotation") * 2)); + } + } else { + enumdirection = Direction.fromHorizontal(cmp.getByte("Dir")); + cmp.remove("Dir"); + } + + cmp.putByte("Facing", (byte) enumdirection.getHorizontal()); + } + + 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) { + ListTag nbttaglist; + + if (cmp.containsKey("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.containsKey("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.containsKey("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) { + ListTag nbttaglist = new 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.containsKey("pages", 9)) { + ListTag nbttaglist = nbttagcompound1.getList("pages", 8); + + for (int i = 0; i < nbttaglist.size(); ++i) { + String s = nbttaglist.getString(i); + Object object = null; + + if (!"null".equals(s) && !Strings.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 = JsonHelper.deserialize(DataConverterSignText.a, s, Component.class, true); + if (object == null) { + object = new TextComponent(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = Component.Serializer.fromJsonString(s); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = Component.Serializer.fromLenientJsonString(s); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = new TextComponent(s); + } + } + } else { + object = new TextComponent(""); + } + + nbttaglist.set(i, new StringTag(Component.Serializer.toJsonString((Component) object))); + } + + nbttagcompound1.put("pages", nbttaglist); + } + } + + return cmp; + } + } + + private static class DataConverterCookedFish implements DataConverter { + + private static final Identifier a = new Identifier("cooked_fished"); + + DataConverterCookedFish() {} + + public int getDataVersion() { + return 502; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + if (cmp.containsKey("id", 8) && DataConverterCookedFish.a.equals(new Identifier(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.containsKey("ZombieType", 99)) { + int i = -1; + + if (cmp.containsKey("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 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"); + case 0: + default: + break; + } + + 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 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"); + break; + + case 0: + default: + cmp.putString("id", "Horse"); + break; + } + + 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.containsKey("Potion", 8)) { + nbttagcompound1.putString("Potion", "minecraft:water"); + } + + if (!cmp.containsKey("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.containsKey("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.containsKey("tag", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("tag"); + + if (nbttagcompound1.containsKey("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.containsKey("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(FabricDataFixer.class); + + DataConverterBedBlock() {} + + public int getDataVersion() { + return 1125; + } + + public net.minecraft.nbt.CompoundTag convert(net.minecraft.nbt.CompoundTag cmp) { + boolean flag = true; + + try { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); + int i = nbttagcompound1.getInt("xPos"); + int j = nbttagcompound1.getInt("zPos"); + ListTag nbttaglist = nbttagcompound1.getList("TileEntities", 10); + ListTag nbttaglist1 = nbttagcompound1.getList("Sections", 10); + + for (int k = 0; k < nbttaglist1.size(); ++k) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist1.getCompoundTag(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() { + Component 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(); + Component iTextComponent = null; + Iterator iterator = jsonarray.iterator(); + + while (iterator.hasNext()) { + JsonElement jsonelement1 = (JsonElement) iterator.next(); + Component iTextComponent1 = this.a(jsonelement1, jsonelement1.getClass(), jsondeserializationcontext); + + if (iTextComponent == null) { + iTextComponent = iTextComponent1; + } else { + iTextComponent.append(iTextComponent1); + } + } + + return iTextComponent; + } 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); + Object object = null; + + if (!"null".equals(s1) && !Strings.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 = JsonHelper.deserialize(DataConverterSignText.a, s1, Component.class, true); + if (object == null) { + object = new TextComponent(""); + } + } catch (JsonParseException jsonparseexception) { + ; + } + + if (object == null) { + try { + object = Component.Serializer.fromJsonString(s1); + } catch (JsonParseException jsonparseexception1) { + ; + } + } + + if (object == null) { + try { + object = Component.Serializer.fromLenientJsonString(s1); + } catch (JsonParseException jsonparseexception2) { + ; + } + } + + if (object == null) { + object = new TextComponent(s1); + } + } + } else { + object = new TextComponent(""); + } + + nbttagcompound.putString(s, Component.Serializer.toJsonString((Component) 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.containsKey("RootVehicle", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("RootVehicle"); + + if (nbttagcompound1.containsKey("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.containsKey("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) { + ListTag nbttaglist; + int j; + net.minecraft.nbt.CompoundTag nbttagcompound1; + + if (cmp.containsKey("entities", 9)) { + nbttaglist = cmp.getList("entities", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); + if (nbttagcompound1.containsKey("nbt", 10)) { + convertCompound(LegacyType.ENTITY, nbttagcompound1, "nbt", sourceVer, targetVer); + } + } + } + + if (cmp.containsKey("blocks", 9)) { + nbttaglist = cmp.getList("blocks", 10); + + for (j = 0; j < nbttaglist.size(); ++j) { + nbttagcompound1 = (net.minecraft.nbt.CompoundTag) nbttaglist.get(j); + if (nbttagcompound1.containsKey("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.containsKey("Level", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Level"); + ListTag nbttaglist; + int j; + + if (nbttagcompound1.containsKey("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.containsKey("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.containsKey("Passengers", 9)) { + ListTag nbttaglist = cmp.getList("Passengers", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.set(j, convert(LegacyType.ENTITY, nbttaglist.getCompoundTag(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.containsKey("ShoulderEntityLeft", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityLeft", sourceVer, targetVer); + } + + if (cmp.containsKey("ShoulderEntityRight", 10)) { + convertCompound(LegacyType.ENTITY, cmp, "ShoulderEntityRight", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorVillagers implements DataInspector { + Identifier entityVillager = getKey("EntityVillager"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (entityVillager.equals(new Identifier(cmp.getString("id"))) && cmp.containsKey("Offers", 10)) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = cmp.getCompound("Offers"); + + if (nbttagcompound1.containsKey("Recipes", 9)) { + ListTag nbttaglist = nbttagcompound1.getList("Recipes", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + net.minecraft.nbt.CompoundTag nbttagcompound2 = nbttaglist.getCompoundTag(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 { + Identifier entityMinecartMobSpawner = getKey("EntityMinecartMobSpawner"); + Identifier 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 Identifier(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 { + Identifier tileEntityMobSpawner = getKey("TileEntityMobSpawner"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (tileEntityMobSpawner.equals(new Identifier(cmp.getString("id")))) { + if (cmp.containsKey("SpawnPotentials", 9)) { + ListTag nbttaglist = cmp.getList("SpawnPotentials", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + net.minecraft.nbt.CompoundTag nbttagcompound1 = nbttaglist.getCompoundTag(j); + + convertCompound(LegacyType.ENTITY, nbttagcompound1, "Entity", sourceVer, targetVer); + } + } + + convertCompound(LegacyType.ENTITY, cmp, "SpawnData", sourceVer, targetVer); + } + + return cmp; + } + } + + private static class DataInspectorCommandBlock implements DataInspector { + Identifier tileEntityCommand = getKey("TileEntityCommand"); + + @Override + public net.minecraft.nbt.CompoundTag inspect(net.minecraft.nbt.CompoundTag cmp, int sourceVer, int targetVer) { + if (tileEntityCommand.equals(new Identifier(cmp.getString("id")))) { + cmp.putString("id", "Control"); + convert(LegacyType.BLOCK_ENTITY, cmp, sourceVer, targetVer); + cmp.putString("id", "MinecartCommandBlock"); + } + + return cmp; + } + } +} 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 new file mode 100644 index 000000000..d1fb75a04 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntity.java @@ -0,0 +1,116 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.google.common.base.Preconditions.checkNotNull; + +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.math.Vector3; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.NullWorld; +import com.sk89q.worldedit.world.entity.EntityTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +import java.lang.ref.WeakReference; + +import javax.annotation.Nullable; + +class FabricEntity implements Entity { + + private final WeakReference entityRef; + + FabricEntity(net.minecraft.entity.Entity entity) { + checkNotNull(entity); + this.entityRef = new WeakReference<>(entity); + } + + @Override + public BaseEntity getState() { + net.minecraft.entity.Entity entity = entityRef.get(); + if (entity != null) { + Identifier id = Registry.ENTITY_TYPE.getId(entity.getType()); + CompoundTag tag = new CompoundTag(); + entity.toTag(tag); + return new BaseEntity(EntityTypes.get(id.toString()), NBTConverter.fromNative(tag)); + } else { + return null; + } + } + + @Override + public Location getLocation() { + net.minecraft.entity.Entity entity = entityRef.get(); + if (entity != null) { + Vector3 position = Vector3.at(entity.x, entity.y, entity.z); + float yaw = entity.yaw; + float pitch = entity.pitch; + + return new Location(FabricAdapter.adapt(entity.world), position, yaw, pitch); + } else { + return new Location(NullWorld.getInstance()); + } + } + + @Override + public boolean setLocation(Location location) { + // TODO unused atm + return false; + } + + @Override + public Extent getExtent() { + net.minecraft.entity.Entity entity = entityRef.get(); + if (entity != null) { + return FabricAdapter.adapt(entity.world); + } else { + return NullWorld.getInstance(); + } + } + + @Override + public boolean remove() { + net.minecraft.entity.Entity entity = entityRef.get(); + if (entity != null) { + entity.remove(); + } + return true; + } + + @SuppressWarnings("unchecked") + @Nullable + @Override + public T getFacet(Class cls) { + net.minecraft.entity.Entity entity = entityRef.get(); + if (entity != null) { + if (EntityProperties.class.isAssignableFrom(cls)) { + return (T) new FabricEntityProperties(entity); + } else { + return null; + } + } else { + return null; + } + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntityProperties.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntityProperties.java new file mode 100644 index 000000000..eafbe5c94 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricEntityProperties.java @@ -0,0 +1,151 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.sk89q.worldedit.entity.metadata.EntityProperties; +import net.minecraft.entity.EnderEyeEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.ExperienceOrbEntity; +import net.minecraft.entity.FallingBlockEntity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.Npc; +import net.minecraft.entity.TntEntity; +import net.minecraft.entity.boss.dragon.EnderDragonEntity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.entity.decoration.ItemFrameEntity; +import net.minecraft.entity.decoration.painting.PaintingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.mob.MobEntity; +import net.minecraft.entity.passive.AnimalEntity; +import net.minecraft.entity.passive.TameableEntity; +import net.minecraft.entity.passive.GolemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.Projectile; +import net.minecraft.entity.vehicle.AbstractMinecartEntity; +import net.minecraft.entity.vehicle.BoatEntity; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.village.Trader; + +public class FabricEntityProperties implements EntityProperties { + + private final Entity entity; + + public FabricEntityProperties(Entity entity) { + checkNotNull(entity); + this.entity = entity; + } + + @Override + public boolean isPlayerDerived() { + return entity instanceof PlayerEntity; + } + + @Override + public boolean isProjectile() { + return entity instanceof EnderEyeEntity || entity instanceof Projectile; + } + + @Override + public boolean isItem() { + return entity instanceof ItemEntity; + } + + @Override + public boolean isFallingBlock() { + return entity instanceof FallingBlockEntity; + } + + @Override + public boolean isPainting() { + return entity instanceof PaintingEntity; + } + + @Override + public boolean isItemFrame() { + return entity instanceof ItemFrameEntity; + } + + @Override + public boolean isBoat() { + return entity instanceof BoatEntity; + } + + @Override + public boolean isMinecart() { + return entity instanceof AbstractMinecartEntity; + } + + @Override + public boolean isTNT() { + return entity instanceof TntEntity; + } + + @Override + public boolean isExperienceOrb() { + return entity instanceof ExperienceOrbEntity; + } + + @Override + public boolean isLiving() { + return entity instanceof MobEntity; + } + + @Override + public boolean isAnimal() { + return entity instanceof AnimalEntity; + } + + @Override + public boolean isAmbient() { + return entity instanceof AmbientEntity; + } + + @Override + public boolean isNPC() { + return entity instanceof Npc || entity instanceof Trader; + } + + @Override + public boolean isGolem() { + return entity instanceof GolemEntity; + } + + @Override + public boolean isTamed() { + return entity instanceof TameableEntity && ((TameableEntity) entity).isTamed(); + } + + @Override + public boolean isTagged() { + return entity.hasCustomName(); + } + + @Override + public boolean isArmorStand() { + return entity instanceof ArmorStandEntity; + } + + @Override + public boolean isPasteable() { + return !(entity instanceof ServerPlayerEntity || entity instanceof EnderDragonEntity); + } +} 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 new file mode 100644 index 000000000..e8ec3e43f --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemCategoryRegistry.java @@ -0,0 +1,46 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.registry.Category; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.registry.ItemCategoryRegistry; +import net.minecraft.tag.ItemTags; +import net.minecraft.tag.Tag; +import net.minecraft.util.Identifier; + +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.getContainer().get(new Identifier(category))) + .map(Tag::values).orElse(Collections.emptySet()) + .stream().map(FabricAdapter::adapt).collect(Collectors.toSet()); + } + + @Override + public Set getAll(Category category) { + return getCategorisedByName(category.getId()); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemRegistry.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemRegistry.java new file mode 100644 index 000000000..e3497234f --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricItemRegistry.java @@ -0,0 +1,42 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.registry.BundledItemRegistry; +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.item.Item; + +import javax.annotation.Nullable; + +public class FabricItemRegistry extends BundledItemRegistry { + + @Nullable + @Override + public String getName(ItemType itemType) { + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) { + final Item item = FabricAdapter.adapt(itemType); + return I18n.translate(item.getTranslationKey()); + } + return super.getName(itemType); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPermissionsProvider.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPermissionsProvider.java new file mode 100644 index 000000000..68ff54bef --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPermissionsProvider.java @@ -0,0 +1,50 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.world.GameMode; + +public interface FabricPermissionsProvider { + + boolean hasPermission(ServerPlayerEntity player, String permission); + + void registerPermission(String permission); + + class VanillaPermissionsProvider implements FabricPermissionsProvider { + + private FabricPlatform platform; + + public VanillaPermissionsProvider(FabricPlatform platform) { + this.platform = platform; + } + + @Override + public boolean hasPermission(ServerPlayerEntity player, String permission) { + FabricConfiguration configuration = platform.getConfiguration(); + return configuration.cheatMode || + player.server.getPlayerManager().isOperator(player.getGameProfile()) || + (configuration.creativeEnable && player.interactionManager.getGameMode() == GameMode.CREATIVE); + } + + @Override + public void registerPermission(String permission) {} + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java new file mode 100644 index 000000000..69ea094b2 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlatform.java @@ -0,0 +1,202 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.worldedit.command.util.PermissionCondition; +import com.sk89q.worldedit.entity.Player; +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.world.DataFixer; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.registry.Registries; +import net.minecraft.SharedConstants; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.enginehub.piston.Command; +import org.enginehub.piston.CommandManager; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static java.util.stream.Collectors.toList; + +class FabricPlatform extends AbstractPlatform implements MultiUserPlatform { + + private final FabricWorldEdit mod; + private final MinecraftServer server; + private final FabricDataFixer dataFixer; + private boolean hookingEvents = false; + + FabricPlatform(FabricWorldEdit mod, MinecraftServer server) { + this.mod = mod; + this.server = server; + this.dataFixer = new FabricDataFixer(getDataVersion()); + } + + boolean isHookingEvents() { + return hookingEvents; + } + + @Override + public Registries getRegistries() { + return FabricRegistries.getInstance(); + } + + @Override + public int getDataVersion() { + return SharedConstants.getGameVersion().getWorldVersion(); + } + + @Override + public DataFixer getDataFixer() { + return dataFixer; + } + + @Override + public boolean isValidMobType(String type) { + return Registry.ENTITY_TYPE.containsId(new Identifier(type)); + } + + @Override + public void reload() { + getConfiguration().load(); + } + + @Override + public int schedule(long delay, long period, Runnable task) { + return -1; + } + + @Override + public List getWorlds() { + Iterable worlds = server.getWorlds(); + List ret = new ArrayList<>(); + for (ServerWorld world : worlds) { + ret.add(new FabricWorld(world)); + } + return ret; + } + + @Nullable + @Override + public Player matchPlayer(Player player) { + if (player instanceof FabricPlayer) { + return player; + } else { + ServerPlayerEntity entity = server.getPlayerManager().getPlayer(player.getName()); + return entity != null ? new FabricPlayer(entity) : null; + } + } + + @Nullable + @Override + public World matchWorld(World world) { + if (world instanceof FabricWorld) { + return world; + } else { + for (ServerWorld ws : server.getWorlds()) { + if (ws.getLevelProperties().getLevelName().equals(world.getName())) { + return new FabricWorld(ws); + } + } + + return null; + } + } + + @Override + public void registerCommands(CommandManager manager) { + if (server == null) return; + net.minecraft.server.command.CommandManager mcMan = server.getCommandManager(); + + for (Command command : manager.getAllCommands().collect(toList())) { + CommandWrapper.register(mcMan.getDispatcher(), command); + Set perms = command.getCondition().as(PermissionCondition.class) + .map(PermissionCondition::getPermissions) + .orElseGet(Collections::emptySet); + if (!perms.isEmpty()) { + perms.forEach(FabricWorldEdit.inst.getPermissionsProvider()::registerPermission); + } + } + } + + @Override + public void registerGameHooks() { + // We registered the events already anyway, so we just 'turn them on' + hookingEvents = true; + } + + @Override + public FabricConfiguration getConfiguration() { + return mod.getConfig(); + } + + @Override + public String getVersion() { + return mod.getInternalVersion(); + } + + @Override + public String getPlatformName() { + return "Fabric-Official"; + } + + @Override + public String getPlatformVersion() { + return mod.getInternalVersion(); + } + + @Override + public Map getCapabilities() { + Map capabilities = new EnumMap<>(Capability.class); + capabilities.put(Capability.CONFIGURATION, Preference.PREFER_OTHERS); + capabilities.put(Capability.WORLDEDIT_CUI, Preference.NORMAL); + capabilities.put(Capability.GAME_HOOKS, Preference.NORMAL); + capabilities.put(Capability.PERMISSIONS, Preference.NORMAL); + capabilities.put(Capability.USER_COMMANDS, Preference.NORMAL); + capabilities.put(Capability.WORLD_EDITING, Preference.PREFERRED); + return capabilities; + } + + @Override + public Collection getConnectedUsers() { + List users = new ArrayList<>(); + PlayerManager scm = server.getPlayerManager(); + for (ServerPlayerEntity entity : scm.getPlayerList()) { + if (entity != null) { + users.add(new FabricPlayer(entity)); + } + } + return users; + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java new file mode 100644 index 000000000..1944b3063 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricPlayer.java @@ -0,0 +1,267 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.util.StringUtil; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.extension.platform.AbstractPlayerActor; +import com.sk89q.worldedit.extent.inventory.BlockBag; +import com.sk89q.worldedit.fabric.net.handler.WECUIPacketHandler; +import com.sk89q.worldedit.internal.cui.CUIEvent; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.Vector3; +import com.sk89q.worldedit.session.SessionKey; +import com.sk89q.worldedit.util.HandSide; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.formatting.text.Component; +import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer; +import com.sk89q.worldedit.world.World; +import com.sk89q.worldedit.world.block.BaseBlock; +import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.block.BlockTypes; +import io.netty.buffer.Unpooled; +import net.minecraft.ChatFormat; +import net.minecraft.block.Block; +import net.minecraft.client.network.packet.BlockEntityUpdateS2CPacket; +import net.minecraft.client.network.packet.BlockUpdateS2CPacket; +import net.minecraft.client.network.packet.CustomPayloadS2CPacket; +import net.minecraft.item.ItemStack; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.PacketByteBuf; +import net.minecraft.util.math.BlockPos; + +import java.io.IOException; +import java.util.UUID; + +import javax.annotation.Nullable; + +public class FabricPlayer extends AbstractPlayerActor { + + // see ClientPlayNetHandler: search for "invalid update packet", lots of hardcoded consts + private static final int STRUCTURE_BLOCK_PACKET_ID = 7; + private final ServerPlayerEntity player; + + protected FabricPlayer(ServerPlayerEntity player) { + this.player = player; + ThreadSafeCache.getInstance().getOnlineIds().add(getUniqueId()); + } + + @Override + public UUID getUniqueId() { + return player.getUuid(); + } + + @Override + public BaseItemStack getItemInHand(HandSide handSide) { + ItemStack is = this.player.getStackInHand(handSide == HandSide.MAIN_HAND ? Hand.MAIN_HAND : Hand.OFF_HAND); + return FabricAdapter.adapt(is); + } + + @Override + public String getName() { + return this.player.getName().getFormattedText(); + } + + @Override + public BaseEntity getState() { + throw new UnsupportedOperationException("Cannot create a state from this object"); + } + + @Override + public Location getLocation() { + Vector3 position = Vector3.at(this.player.x, this.player.y, this.player.z); + return new Location( + FabricWorldEdit.inst.getWorld(this.player.world), + position, + this.player.yaw, + this.player.pitch); + } + + @Override + public boolean setLocation(Location location) { + // TODO + return false; + } + + @Override + public com.sk89q.worldedit.world.World getWorld() { + return FabricWorldEdit.inst.getWorld(this.player.world); + } + + @Override + public void giveItem(BaseItemStack itemStack) { + this.player.inventory.insertStack(FabricAdapter.adapt(itemStack)); + } + + @Override + public void dispatchCUIEvent(CUIEvent event) { + String[] params = event.getParameters(); + String send = event.getTypeId(); + if (params.length > 0) { + send = send + "|" + StringUtil.joinString(params, "|"); + } + PacketByteBuf buffer = new PacketByteBuf(Unpooled.copiedBuffer(send.getBytes(WECUIPacketHandler.UTF_8_CHARSET))); + CustomPayloadS2CPacket packet = new CustomPayloadS2CPacket(new Identifier(FabricWorldEdit.MOD_ID, FabricWorldEdit.CUI_PLUGIN_CHANNEL), buffer); + this.player.networkHandler.sendPacket(packet); + } + + @Override + public void printRaw(String msg) { + for (String part : msg.split("\n")) { + this.player.sendMessage(new TextComponent(part)); + } + } + + @Override + public void printDebug(String msg) { + sendColorized(msg, ChatFormat.GRAY); + } + + @Override + public void print(String msg) { + sendColorized(msg, ChatFormat.LIGHT_PURPLE); + } + + @Override + public void printError(String msg) { + sendColorized(msg, ChatFormat.RED); + } + + @Override + public void print(Component component) { + this.player.sendMessage(net.minecraft.network.chat.Component.Serializer.fromJsonString(GsonComponentSerializer.INSTANCE.serialize(component))); + } + + private void sendColorized(String msg, ChatFormat formatting) { + for (String part : msg.split("\n")) { + TextComponent component = new TextComponent(part); + component.getStyle().setColor(formatting); + this.player.sendMessage(component); + } + } + + @Override + public void setPosition(Vector3 pos, float pitch, float yaw) { + this.player.networkHandler.requestTeleport(pos.getX(), pos.getY(), pos.getZ(), yaw, pitch); + } + + @Override + public String[] getGroups() { + return new String[]{}; // WorldEditMod.inst.getPermissionsResolver().getGroups(this.player.username); + } + + @Override + public BlockBag getInventoryBlockBag() { + return null; + } + + @Override + public boolean hasPermission(String perm) { + return FabricWorldEdit.inst.getPermissionsProvider().hasPermission(player, perm); + } + + @Nullable + @Override + public T getFacet(Class cls) { + return null; + } + + @Override + public > void sendFakeBlock(BlockVector3 pos, B block) { + World world = getWorld(); + if (!(world instanceof FabricWorld)) { + return; + } + BlockPos loc = FabricAdapter.toBlockPos(pos); + if (block == null) { + final BlockUpdateS2CPacket packetOut = new BlockUpdateS2CPacket(((FabricWorld) world).getWorld(), loc); + player.networkHandler.sendPacket(packetOut); + } else { + final BlockUpdateS2CPacket packetOut = new BlockUpdateS2CPacket(); + PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer()); + buf.writeBlockPos(loc); + buf.writeVarInt(Block.getRawIdFromState(FabricAdapter.adapt(block.toImmutableState()))); + try { + packetOut.read(buf); + } catch (IOException e) { + return; + } + player.networkHandler.sendPacket(packetOut); + if (block instanceof BaseBlock && block.getBlockType().equals(BlockTypes.STRUCTURE_BLOCK)) { + final BaseBlock baseBlock = (BaseBlock) block; + final CompoundTag nbtData = baseBlock.getNbtData(); + if (nbtData != null) { + player.networkHandler.sendPacket(new BlockEntityUpdateS2CPacket( + new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()), + STRUCTURE_BLOCK_PACKET_ID, + NBTConverter.toNative(nbtData)) + ); + } + } + } + } + + @Override + public SessionKey getSessionKey() { + return new SessionKeyImpl(player.getUuid(), player.getName().getString()); + } + + private static class SessionKeyImpl implements SessionKey { + // If not static, this will leak a reference + + private final UUID uuid; + private final String name; + + private SessionKeyImpl(UUID uuid, String name) { + this.uuid = uuid; + this.name = name; + } + + @Override + public UUID getUniqueId() { + return uuid; + } + + @Nullable + @Override + public String getName() { + return name; + } + + @Override + public boolean isActive() { + // We can't directly check if the player is online because + // the list of players is not thread safe + return ThreadSafeCache.getInstance().getOnlineIds().contains(uuid); + } + + @Override + public boolean isPersistent() { + return true; + } + + } + +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricRegistries.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricRegistries.java new file mode 100644 index 000000000..7c703b723 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricRegistries.java @@ -0,0 +1,75 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +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 Fabric platform. + */ +class FabricRegistries extends BundledRegistries { + + private static final FabricRegistries INSTANCE = new FabricRegistries(); + private final BlockRegistry blockRegistry = new FabricBlockRegistry(); + private final BiomeRegistry biomeRegistry = new FabricBiomeRegistry(); + private final ItemRegistry itemRegistry = new FabricItemRegistry(); + private final BlockCategoryRegistry blockCategoryRegistry = new FabricBlockCategoryRegistry(); + private final ItemCategoryRegistry itemCategoryRegistry = new FabricItemCategoryRegistry(); + + @Override + public BlockRegistry getBlockRegistry() { + return blockRegistry; + } + + @Override + public BiomeRegistry getBiomeRegistry() { + return biomeRegistry; + } + + @Override + public ItemRegistry getItemRegistry() { + return itemRegistry; + } + + @Override + public BlockCategoryRegistry getBlockCategoryRegistry() { + return blockCategoryRegistry; + } + + @Override + public ItemCategoryRegistry getItemCategoryRegistry() { + return itemCategoryRegistry; + } + + /** + * Get a static instance. + * + * @return an instance + */ + public static FabricRegistries getInstance() { + return INSTANCE; + } + +} 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 new file mode 100644 index 000000000..8125fb0cb --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java @@ -0,0 +1,597 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.io.Files; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.blocks.BaseItemStack; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.entity.Entity; +import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.internal.block.BlockStateIdAccess; +import com.sk89q.worldedit.math.BlockVector2; +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.util.Direction; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.util.TreeGenerator.TreeType; +import com.sk89q.worldedit.world.AbstractWorld; +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.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.block.LeavesBlock; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUsageContext; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.WorldGenerationProgressListener; +import net.minecraft.server.world.ServerChunkManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Clearable; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.World; +import net.minecraft.world.WorldSaveHandler; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkManager; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.gen.feature.BirchTreeFeature; +import net.minecraft.world.gen.feature.DarkOakTreeFeature; +import net.minecraft.world.gen.feature.DefaultFeatureConfig; +import net.minecraft.world.gen.feature.Feature; +import net.minecraft.world.gen.feature.FeatureConfig; +import net.minecraft.world.gen.feature.HugeBrownMushroomFeature; +import net.minecraft.world.gen.feature.HugeRedMushroomFeature; +import net.minecraft.world.gen.feature.JungleGroundBushFeature; +import net.minecraft.world.gen.feature.JungleTreeFeature; +import net.minecraft.world.gen.feature.LargeOakTreeFeature; +import net.minecraft.world.gen.feature.MegaJungleTreeFeature; +import net.minecraft.world.gen.feature.MegaPineTreeFeature; +import net.minecraft.world.gen.feature.OakTreeFeature; +import net.minecraft.world.gen.feature.PineTreeFeature; +import net.minecraft.world.gen.feature.PlantedFeatureConfig; +import net.minecraft.world.gen.feature.SavannaTreeFeature; +import net.minecraft.world.gen.feature.SpruceTreeFeature; +import net.minecraft.world.gen.feature.SwampTreeFeature; +import net.minecraft.world.level.LevelProperties; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.Random; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; + +/** + * An adapter to Minecraft worlds for WorldEdit. + */ +public class FabricWorld extends AbstractWorld { + + private static final Random random = new Random(); + private static final int UPDATE = 1, NOTIFY = 2; + + private static final net.minecraft.block.BlockState JUNGLE_LOG = Blocks.JUNGLE_LOG.getDefaultState(); + private static final net.minecraft.block.BlockState JUNGLE_LEAF = Blocks.JUNGLE_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); + private static final net.minecraft.block.BlockState JUNGLE_SHRUB = Blocks.OAK_LEAVES.getDefaultState().with(LeavesBlock.PERSISTENT, Boolean.TRUE); + + private final WeakReference worldRef; + + /** + * Construct a new world. + * + * @param world the world + */ + FabricWorld(World world) { + checkNotNull(world); + this.worldRef = new WeakReference<>(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 WorldReferenceLostException("The reference to the world was lost (i.e. the world may have been unloaded)"); + } + } + + /** + * 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) + */ + public World getWorld() { + World world = worldRef.get(); + if (world != null) { + return world; + } else { + throw new RuntimeException("The reference to the world was lost (i.e. the world may have been unloaded)"); + } + } + + @Override + public String getName() { + return getWorld().getLevelProperties().getLevelName(); + } + + @Override + public Path getStoragePath() { + final World world = getWorld(); + if (world instanceof ServerWorld) { + return ((ServerWorld) world).getSaveHandler().getWorldDir().toPath(); + } + return null; + } + + @Override + public > boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException { + checkNotNull(position); + checkNotNull(block); + + World world = getWorldChecked(); + int x = position.getBlockX(); + int y = position.getBlockY(); + int z = position.getBlockZ(); + + // First set the block + Chunk chunk = world.getChunk(x >> 4, z >> 4); + BlockPos pos = new BlockPos(x, y, z); + net.minecraft.block.BlockState old = chunk.getBlockState(pos); + OptionalInt stateId = BlockStateIdAccess.getBlockStateId(block.toImmutableState()); + net.minecraft.block.BlockState newState = stateId.isPresent() ? Block.getStateFromRawId(stateId.getAsInt()) : FabricAdapter.adapt(block.toImmutableState()); + net.minecraft.block.BlockState successState = chunk.setBlockState(pos, newState, false); + boolean successful = successState != null; + + // Create the TileEntity + if (successful || old == newState) { + if (block instanceof BaseBlock) { + CompoundTag tag = ((BaseBlock) block).getNbtData(); + if (tag != null) { + net.minecraft.nbt.CompoundTag nativeTag = NBTConverter.toNative(tag); + nativeTag.putString("id", ((BaseBlock) block).getNbtId()); + TileEntityUtils.setTileEntity(world, position, nativeTag); + successful = true; // update if TE changed as well + } + } + } + + if (successful && notifyAndLight) { + world.getChunkManager().getLightingProvider().enqueueLightUpdate(pos); + world.updateListeners(pos, old, newState, UPDATE | NOTIFY); + } + + return successful; + } + + @Override + public boolean notifyAndLightBlock(BlockVector3 position, BlockState previousType) throws WorldEditException { + BlockPos pos = new BlockPos(position.getX(), position.getY(), position.getZ()); + getWorld().updateListeners(pos, FabricAdapter.adapt(previousType), getWorld().getBlockState(pos), 1 | 2); + return true; + } + + @Override + public int getBlockLightLevel(BlockVector3 position) { + checkNotNull(position); + return getWorld().getLightLevel(FabricAdapter.toBlockPos(position)); + } + + @Override + public boolean clearContainerBlockContents(BlockVector3 position) { + checkNotNull(position); + BlockEntity tile = getWorld().getBlockEntity(FabricAdapter.toBlockPos(position)); + if ((tile instanceof Clearable)) { + ((Clearable) tile).clear(); + return true; + } + return false; + } + + @Override + public BiomeType getBiome(BlockVector2 position) { + checkNotNull(position); + return FabricAdapter.adapt(getWorld().getBiome(new BlockPos(position.getBlockX(), 0, position.getBlockZ()))); + } + + @Override + public boolean setBiome(BlockVector2 position, BiomeType biome) { + checkNotNull(position); + checkNotNull(biome); + + Chunk chunk = getWorld().getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4, ChunkStatus.FULL, false); + if (chunk == null) { + return false; + } + chunk.getBiomeArray()[((position.getBlockZ() & 0xF) << 4 | position.getBlockX() & 0xF)] = FabricAdapter.adapt(biome); + return true; + } + + private static final LoadingCache fakePlayers + = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(WorldEditFakePlayer::new)); + + @Override + public boolean useItem(BlockVector3 position, BaseItem item, Direction face) { + ItemStack stack = FabricAdapter.adapt(new BaseItemStack(item.getType(), item.getNbtData(), 1)); + ServerWorld world = (ServerWorld) getWorld(); + final WorldEditFakePlayer fakePlayer; + try { + fakePlayer = fakePlayers.get(world); + } catch (ExecutionException ignored) { + return false; + } + fakePlayer.setStackInHand(Hand.MAIN_HAND, stack); + fakePlayer.setPositionAndAngles(position.getBlockX(), position.getBlockY(), position.getBlockZ(), + (float) face.toVector().toYaw(), (float) face.toVector().toPitch()); + final BlockPos blockPos = FabricAdapter.toBlockPos(position); + final BlockHitResult rayTraceResult = new BlockHitResult(FabricAdapter.toVec3(position), + FabricAdapter.adapt(face), blockPos, false); + ItemUsageContext itemUseContext = new ItemUsageContext(fakePlayer, Hand.MAIN_HAND, rayTraceResult); + ActionResult used = stack.useOnBlock(itemUseContext); + if (used != ActionResult.SUCCESS) { + // try activating the block + if (getWorld().getBlockState(blockPos).activate(world, fakePlayer, Hand.MAIN_HAND, rayTraceResult)) { + used = ActionResult.SUCCESS; + } else { + used = stack.getItem().use(world, fakePlayer, Hand.MAIN_HAND).getResult(); + } + } + return used == ActionResult.SUCCESS; + } + + @Override + public void dropItem(Vector3 position, BaseItemStack item) { + checkNotNull(position); + checkNotNull(item); + + if (item.getType() == ItemTypes.AIR) { + return; + } + + ItemEntity entity = new ItemEntity(getWorld(), position.getX(), position.getY(), position.getZ(), FabricAdapter.adapt(item)); + entity.setPickupDelay(10); + getWorld().spawnEntity(entity); + } + + @Override + public void simulateBlockMine(BlockVector3 position) { + BlockPos pos = FabricAdapter.toBlockPos(position); + getWorld().breakBlock(pos, true); + } + + @Override + public boolean regenerate(Region region, EditSession editSession) { + // Don't even try to regen if it's going to fail. + ChunkManager provider = getWorld().getChunkManager(); + if (!(provider instanceof ServerChunkManager)) { + return false; + } + + File saveFolder = Files.createTempDir(); + // register this just in case something goes wrong + // normally it should be deleted at the end of this method + saveFolder.deleteOnExit(); + try { + ServerWorld originalWorld = (ServerWorld) getWorld(); + + MinecraftServer server = originalWorld.getServer(); + WorldSaveHandler saveHandler = new WorldSaveHandler(saveFolder, originalWorld.getSaveHandler().getWorldDir().getName(), server, server.getDataFixer()); + World freshWorld = new ServerWorld(server, server.getWorkerExecutor(), saveHandler, originalWorld.getLevelProperties(), + originalWorld.dimension.getType(), originalWorld.getProfiler(), new NoOpChunkStatusListener()); + + // Pre-gen all the chunks + // We need to also pull one more chunk in every direction + CuboidRegion expandedPreGen = new CuboidRegion(region.getMinimumPoint().subtract(16, 0, 16), region.getMaximumPoint().add(16, 0, 16)); + for (BlockVector2 chunk : expandedPreGen.getChunks()) { + freshWorld.getChunk(chunk.getBlockX(), chunk.getBlockZ()); + } + + FabricWorld from = new FabricWorld(freshWorld); + for (BlockVector3 vec : region) { + editSession.setBlock(vec, from.getFullBlock(vec)); + } + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } finally { + saveFolder.delete(); + } + + return true; + } + + @Nullable + private static Feature createTreeFeatureGenerator(TreeType type) { + switch (type) { + case TREE: return new OakTreeFeature(DefaultFeatureConfig::deserialize, true); + case BIG_TREE: return new LargeOakTreeFeature(DefaultFeatureConfig::deserialize, true); + case REDWOOD: return new PineTreeFeature(DefaultFeatureConfig::deserialize); + case TALL_REDWOOD: return new SpruceTreeFeature(DefaultFeatureConfig::deserialize, true); + case BIRCH: return new BirchTreeFeature(DefaultFeatureConfig::deserialize, true, false); + case JUNGLE: return new MegaJungleTreeFeature(DefaultFeatureConfig::deserialize, true, 10, 20, JUNGLE_LOG, JUNGLE_LEAF); + case SMALL_JUNGLE: return new JungleTreeFeature(DefaultFeatureConfig::deserialize, true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, false); + case SHORT_JUNGLE: return new JungleTreeFeature(DefaultFeatureConfig::deserialize, true, 4 + random.nextInt(7), JUNGLE_LOG, JUNGLE_LEAF, true); + case JUNGLE_BUSH: return new JungleGroundBushFeature(DefaultFeatureConfig::deserialize, JUNGLE_LOG, JUNGLE_SHRUB); + case SWAMP: return new SwampTreeFeature(DefaultFeatureConfig::deserialize); + case ACACIA: return new SavannaTreeFeature(DefaultFeatureConfig::deserialize, true); + case DARK_OAK: return new DarkOakTreeFeature(DefaultFeatureConfig::deserialize, true); + case MEGA_REDWOOD: return new MegaPineTreeFeature(DefaultFeatureConfig::deserialize, true, random.nextBoolean()); + case TALL_BIRCH: return new BirchTreeFeature(DefaultFeatureConfig::deserialize, true, true); + case RED_MUSHROOM: return new HugeRedMushroomFeature(PlantedFeatureConfig::deserialize); + case BROWN_MUSHROOM: return new HugeBrownMushroomFeature(PlantedFeatureConfig::deserialize); + case RANDOM: return createTreeFeatureGenerator(TreeType.values()[ThreadLocalRandom.current().nextInt(TreeType.values().length)]); + default: + return null; + } + } + + private FeatureConfig createFeatureConfig(TreeType type) { + if (type == TreeType.RED_MUSHROOM || type == TreeType.BROWN_MUSHROOM) { + return new PlantedFeatureConfig(true); + } else { + return new DefaultFeatureConfig(); + } + } + + @Override + public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position) throws MaxChangedBlocksException { + @SuppressWarnings("unchecked") + Feature generator = (Feature) createTreeFeatureGenerator(type); + return generator != null + && generator.generate(getWorld(), getWorld().getChunkManager().getChunkGenerator(), random, + FabricAdapter.toBlockPos(position), createFeatureConfig(type)); + } + + @Override + public void checkLoadedChunk(BlockVector3 pt) { + getWorld().getChunk(FabricAdapter.toBlockPos(pt)); + } + + @Override + public void fixAfterFastMode(Iterable chunks) { + fixLighting(chunks); + } + + @Override + public void fixLighting(Iterable chunks) { + World world = getWorld(); + for (BlockVector2 chunk : chunks) { + world.getChunkManager().getLightingProvider().suppressLight(new ChunkPos(chunk.getBlockX(), chunk.getBlockZ()), true); + } + } + + @Override + public boolean playEffect(Vector3 position, int type, int data) { + getWorld().playLevelEvent(type, FabricAdapter.toBlockPos(position.toBlockPoint()), data); + return true; + } + + @Override + public WeatherType getWeather() { + LevelProperties info = getWorld().getLevelProperties(); + if (info.isThundering()) { + return WeatherTypes.THUNDER_STORM; + } + if (info.isRaining()) { + return WeatherTypes.RAIN; + } + return WeatherTypes.CLEAR; + } + + @Override + public long getRemainingWeatherDuration() { + LevelProperties info = getWorld().getLevelProperties(); + if (info.isThundering()) { + return info.getThunderTime(); + } + if (info.isRaining()) { + return info.getRainTime(); + } + return info.getClearWeatherTime(); + } + + @Override + public void setWeather(WeatherType weatherType) { + setWeather(weatherType, 0); + } + + @Override + public void setWeather(WeatherType weatherType, long duration) { + LevelProperties info = getWorld().getLevelProperties(); + if (weatherType == WeatherTypes.THUNDER_STORM) { + info.setClearWeatherTime(0); + info.setThundering(true); + info.setThunderTime((int) duration); + } else if (weatherType == WeatherTypes.RAIN) { + info.setClearWeatherTime(0); + info.setRaining(true); + info.setRainTime((int) duration); + } else if (weatherType == WeatherTypes.CLEAR) { + info.setRaining(false); + info.setThundering(false); + info.setClearWeatherTime((int) duration); + } + } + + @Override + public int getMaxY() { + return getWorld().getHeight() - 1; + } + + @Override + public BlockVector3 getSpawnPosition() { + return FabricAdapter.adapt(getWorld().getSpawnPos()); + } + + @Override + public BlockState getBlock(BlockVector3 position) { + net.minecraft.block.BlockState mcState = getWorld() + .getChunk(position.getBlockX() >> 4, position.getBlockZ() >> 4) + .getBlockState(FabricAdapter.toBlockPos(position)); + + BlockState matchingBlock = BlockStateIdAccess.getBlockStateById(Block.getRawIdFromState(mcState)); + if (matchingBlock != null) { + return matchingBlock; + } + + return FabricAdapter.adapt(mcState); + } + + @Override + public BaseBlock getFullBlock(BlockVector3 position) { + BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()); + // Avoid creation by using the CHECK mode -- if it's needed, it'll be re-created anyways + BlockEntity tile = ((WorldChunk) getWorld().getChunk(pos)).getBlockEntity(pos, WorldChunk.CreationType.CHECK); + + if (tile != null) { + return getBlock(position).toBaseBlock(NBTConverter.fromNative(TileEntityUtils.copyNbtData(tile))); + } else { + return getBlock(position).toBaseBlock(); + } + } + + @Override + public int hashCode() { + return getWorld().hashCode(); + } + + @Override + public boolean equals(Object o) { + if ((o instanceof FabricWorld)) { + FabricWorld other = ((FabricWorld) o); + World otherWorld = other.worldRef.get(); + World thisWorld = worldRef.get(); + return otherWorld != null && otherWorld.equals(thisWorld); + } else if (o instanceof com.sk89q.worldedit.world.World) { + return ((com.sk89q.worldedit.world.World) o).getName().equals(getName()); + } else { + return false; + } + } + + @Override + public List getEntities(Region region) { + final World world = getWorld(); + if (!(world instanceof ServerWorld)) { + return Collections.emptyList(); + } + return ((ServerWorld) world).getEntities(null, entity -> true) + .stream() + .filter(e -> region.contains(FabricAdapter.adapt(e.getBlockPos()))) + .map(FabricEntity::new).collect(Collectors.toList()); + } + + @Override + public List getEntities() { + final World world = getWorld(); + if (!(world instanceof ServerWorld)) { + return Collections.emptyList(); + } + return ((ServerWorld) world).getEntities(null, entity -> true) + .stream() + .map(FabricEntity::new) + .collect(Collectors.toList()); + } + + @Nullable + @Override + public Entity createEntity(Location location, BaseEntity entity) { + World world = getWorld(); + final Optional> entityType = EntityType.get(entity.getType().getId()); + if (!entityType.isPresent()) return null; + net.minecraft.entity.Entity createdEntity = entityType.get().create(world); + if (createdEntity != null) { + CompoundTag nativeTag = entity.getNbtData(); + if (nativeTag != null) { + net.minecraft.nbt.CompoundTag tag = NBTConverter.toNative(entity.getNbtData()); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + createdEntity.fromTag(tag); + } + + createdEntity.setPositionAndAngles(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.spawnEntity(createdEntity); + return new FabricEntity(createdEntity); + } else { + return null; + } + } + + /** + * Thrown when the reference to the world is lost. + */ + @SuppressWarnings("serial") + private static final class WorldReferenceLostException extends WorldEditException { + private WorldReferenceLostException(String message) { + super(message); + } + } + + private static class NoOpChunkStatusListener implements WorldGenerationProgressListener { + @Override + public void start(ChunkPos chunkPos) { + } + + @Override + public void setChunkStatus(ChunkPos chunkPos, @Nullable ChunkStatus chunkStatus) { + } + + @Override + public void stop() { + } + } +} 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 new file mode 100644 index 000000000..0175a618a --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java @@ -0,0 +1,326 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.google.common.base.Preconditions.checkNotNull; +import static com.sk89q.worldedit.fabric.FabricAdapter.adaptPlayer; + +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.event.platform.PlatformReadyEvent; +import com.sk89q.worldedit.extension.platform.Platform; +import com.sk89q.worldedit.fabric.net.handler.WECUIPacketHandler; +import com.sk89q.worldedit.util.Location; +import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockCategory; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.entity.EntityType; +import com.sk89q.worldedit.world.item.ItemCategory; +import com.sk89q.worldedit.world.item.ItemType; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.event.player.AttackBlockCallback; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.fabricmc.fabric.api.event.server.ServerStartCallback; +import net.fabricmc.fabric.api.event.server.ServerStopCallback; +import net.fabricmc.fabric.api.event.server.ServerTickCallback; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.tag.BlockTags; +import net.minecraft.tag.ItemTags; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * The Fabric implementation of WorldEdit. + */ +public class FabricWorldEdit implements ModInitializer { + + private static final Logger LOGGER = LogManager.getLogger(); + public static final String MOD_ID = "worldedit"; + public static final String CUI_PLUGIN_CHANNEL = "cui"; + + private FabricPermissionsProvider provider; + + public static FabricWorldEdit inst; + + private FabricPlatform platform; + private FabricConfiguration config; + private Path workingDir; + + private ModContainer container; + + public FabricWorldEdit() { + inst = this; + } + + @Override + public void onInitialize() { + this.container = FabricLoader.getInstance().getModContainer("worldedit").orElseThrow( + () -> new IllegalStateException("WorldEdit mod missing in Fabric") + ); + + // Setup working directory + workingDir = new File(FabricLoader.getInstance().getConfigDirectory(), "worldedit").toPath(); + if (!Files.exists(workingDir)) { + try { + Files.createDirectory(workingDir); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + WECUIPacketHandler.init(); + + ServerTickCallback.EVENT.register(ThreadSafeCache.getInstance()); + ServerStartCallback.EVENT.register(this::onStartServer); + ServerStopCallback.EVENT.register(this::onStopServer); + AttackBlockCallback.EVENT.register(this::onLeftClickBlock); + UseBlockCallback.EVENT.register(this::onRightClickBlock); + UseItemCallback.EVENT.register(this::onRightClickAir); + LOGGER.info("WorldEdit for Fabric (version " + getInternalVersion() + ") is loaded"); + } + + private void setupPlatform(MinecraftServer server) { + this.platform = new FabricPlatform(this, server); + + WorldEdit.getInstance().getPlatformManager().register(platform); + + this.provider = new FabricPermissionsProvider.VanillaPermissionsProvider(platform); + } + + private void setupRegistries() { + // Blocks + for (Identifier name : Registry.BLOCK.getIds()) { + if (BlockType.REGISTRY.get(name.toString()) == null) { + BlockType.REGISTRY.register(name.toString(), new BlockType(name.toString(), + input -> FabricAdapter.adapt(FabricAdapter.adapt(input.getBlockType()).getDefaultState()))); + } + } + // Items + for (Identifier name : Registry.ITEM.getIds()) { + if (ItemType.REGISTRY.get(name.toString()) == null) { + ItemType.REGISTRY.register(name.toString(), new ItemType(name.toString())); + } + } + // Entities + for (Identifier name : Registry.ENTITY_TYPE.getIds()) { + if (EntityType.REGISTRY.get(name.toString()) == null) { + EntityType.REGISTRY.register(name.toString(), new EntityType(name.toString())); + } + } + // Biomes + for (Identifier name : Registry.BIOME.getIds()) { + if (BiomeType.REGISTRY.get(name.toString()) == null) { + BiomeType.REGISTRY.register(name.toString(), new BiomeType(name.toString())); + } + } + // Tags + for (Identifier name : BlockTags.getContainer().getKeys()) { + if (BlockCategory.REGISTRY.get(name.toString()) == null) { + BlockCategory.REGISTRY.register(name.toString(), new BlockCategory(name.toString())); + } + } + for (Identifier name : ItemTags.getContainer().getKeys()) { + if (ItemCategory.REGISTRY.get(name.toString()) == null) { + ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(name.toString())); + } + } + } + + private void onStartServer(MinecraftServer minecraftServer) { + setupPlatform(minecraftServer); + setupRegistries(); + + config = new FabricConfiguration(this); + config.load(); + WorldEdit.getInstance().getEventBus().post(new PlatformReadyEvent()); + } + + private void onStopServer(MinecraftServer minecraftServer) { + WorldEdit worldEdit = WorldEdit.getInstance(); + worldEdit.getSessionManager().unload(); + worldEdit.getPlatformManager().unregister(platform); + } + + private boolean shouldSkip() { + if (platform == null) { + return true; + } + + return !platform.isHookingEvents(); // We have to be told to catch these events + } + + private ActionResult onLeftClickBlock(PlayerEntity playerEntity, World world, Hand hand, BlockPos blockPos, Direction direction) { + if (shouldSkip() || hand == Hand.OFF_HAND || world.isClient) { + return ActionResult.PASS; + } + + WorldEdit we = WorldEdit.getInstance(); + FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity); + FabricWorld localWorld = getWorld(world); + Location pos = new Location(localWorld, + blockPos.getX(), + blockPos.getY(), + blockPos.getZ() + ); + + if (we.handleBlockLeftClick(player, pos)) { + return ActionResult.SUCCESS; + } + + if (we.handleArmSwing(player)) { + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + } + + public void onLeftClickAir(PlayerEntity playerEntity, World world, Hand hand) { + WorldEdit we = WorldEdit.getInstance(); + FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity); + we.handleArmSwing(player); + } + + private ActionResult onRightClickBlock(PlayerEntity playerEntity, World world, Hand hand, BlockHitResult blockHitResult) { + if (shouldSkip() || hand == Hand.OFF_HAND || world.isClient) { + return ActionResult.PASS; + } + + WorldEdit we = WorldEdit.getInstance(); + FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity); + FabricWorld localWorld = getWorld(world); + Location pos = new Location(localWorld, + blockHitResult.getBlockPos().getX(), + blockHitResult.getBlockPos().getY(), + blockHitResult.getBlockPos().getZ() + ); + + if (we.handleBlockRightClick(player, pos)) { + return ActionResult.SUCCESS; + } + + if (we.handleRightClick(player)) { + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + } + + private ActionResult onRightClickAir(PlayerEntity playerEntity, World world, Hand hand) { + if (shouldSkip() || hand == Hand.OFF_HAND || world.isClient) { + return ActionResult.PASS; + } + + WorldEdit we = WorldEdit.getInstance(); + FabricPlayer player = adaptPlayer((ServerPlayerEntity) playerEntity); + + if (we.handleRightClick(player)) { + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + } + + // TODO Pass empty left click to server + + /** + * Get the configuration. + * + * @return the Fabric configuration + */ + FabricConfiguration getConfig() { + return this.config; + } + + /** + * Get the session for a player. + * + * @param player the player + * @return the session + */ + public LocalSession getSession(ServerPlayerEntity player) { + checkNotNull(player); + return WorldEdit.getInstance().getSessionManager().get(adaptPlayer(player)); + } + + /** + * Get the WorldEdit proxy for the given world. + * + * @param world the world + * @return the WorldEdit world + */ + public FabricWorld getWorld(World world) { + checkNotNull(world); + return new FabricWorld(world); + } + + /** + * Get the WorldEdit proxy for the platform. + * + * @return the WorldEdit platform + */ + public Platform getPlatform() { + return this.platform; + } + + /** + * Get the working directory where WorldEdit's files are stored. + * + * @return the working directory + */ + public File getWorkingDir() { + return this.workingDir.toFile(); + } + + /** + * Get the version of the WorldEdit-Fabric implementation. + * + * @return a version string + */ + String getInternalVersion() { + return container.getMetadata().getVersion().getFriendlyString(); + } + + public void setPermissionsProvider(FabricPermissionsProvider provider) { + this.provider = provider; + } + + public FabricPermissionsProvider getPermissionsProvider() { + return provider; + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java new file mode 100644 index 000000000..450dfcb65 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/NBTConverter.java @@ -0,0 +1,252 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +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.LongTag; +import com.sk89q.jnbt.ShortTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +/** + * Converts between JNBT and Minecraft NBT classes. + */ +final class NBTConverter { + + private NBTConverter() { + } + + public static net.minecraft.nbt.Tag toNative(Tag tag) { + if (tag instanceof IntArrayTag) { + return toNative((IntArrayTag) tag); + + } else if (tag instanceof ListTag) { + return toNative((ListTag) tag); + + } else if (tag instanceof LongTag) { + return toNative((LongTag) tag); + + } else if (tag instanceof StringTag) { + return toNative((StringTag) tag); + + } else if (tag instanceof IntTag) { + return toNative((IntTag) tag); + + } else if (tag instanceof ByteTag) { + return toNative((ByteTag) tag); + + } else if (tag instanceof ByteArrayTag) { + return toNative((ByteArrayTag) tag); + + } else if (tag instanceof CompoundTag) { + return toNative((CompoundTag) tag); + + } else if (tag instanceof FloatTag) { + return toNative((FloatTag) tag); + + } else if (tag instanceof ShortTag) { + return toNative((ShortTag) tag); + + } else if (tag instanceof DoubleTag) { + return toNative((DoubleTag) tag); + } else { + throw new IllegalArgumentException("Can't convert tag of type " + tag.getClass().getCanonicalName()); + } + } + + public static net.minecraft.nbt.IntArrayTag toNative(IntArrayTag tag) { + int[] value = tag.getValue(); + return new net.minecraft.nbt.IntArrayTag(Arrays.copyOf(value, value.length)); + } + + public static net.minecraft.nbt.ListTag toNative(ListTag tag) { + net.minecraft.nbt.ListTag list = new net.minecraft.nbt.ListTag(); + for (Tag child : tag.getValue()) { + if (child instanceof EndTag) { + continue; + } + list.add(toNative(child)); + } + return list; + } + + public static net.minecraft.nbt.LongTag toNative(LongTag tag) { + return new net.minecraft.nbt.LongTag(tag.getValue()); + } + + public static net.minecraft.nbt.StringTag toNative(StringTag tag) { + return new net.minecraft.nbt.StringTag(tag.getValue()); + } + + public static net.minecraft.nbt.IntTag toNative(IntTag tag) { + return new net.minecraft.nbt.IntTag(tag.getValue()); + } + + public static net.minecraft.nbt.ByteTag toNative(ByteTag tag) { + return new net.minecraft.nbt.ByteTag(tag.getValue()); + } + + public static net.minecraft.nbt.ByteArrayTag toNative(ByteArrayTag tag) { + byte[] value = tag.getValue(); + return new net.minecraft.nbt.ByteArrayTag(Arrays.copyOf(value, value.length)); + } + + public static net.minecraft.nbt.CompoundTag toNative(CompoundTag tag) { + net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag(); + for (Entry child : tag.getValue().entrySet()) { + compound.put(child.getKey(), toNative(child.getValue())); + } + return compound; + } + + public static net.minecraft.nbt.FloatTag toNative(FloatTag tag) { + return new net.minecraft.nbt.FloatTag(tag.getValue()); + } + + public static net.minecraft.nbt.ShortTag toNative(ShortTag tag) { + return new net.minecraft.nbt.ShortTag(tag.getValue()); + } + + public static net.minecraft.nbt.DoubleTag toNative(DoubleTag tag) { + return new net.minecraft.nbt.DoubleTag(tag.getValue()); + } + + public static Tag fromNative(net.minecraft.nbt.Tag other) { + if (other instanceof net.minecraft.nbt.IntArrayTag) { + return fromNative((net.minecraft.nbt.IntArrayTag) other); + + } else if (other instanceof net.minecraft.nbt.ListTag) { + return fromNative((net.minecraft.nbt.ListTag) other); + + } else if (other instanceof net.minecraft.nbt.EndTag) { + return fromNative((net.minecraft.nbt.EndTag) other); + + } else if (other instanceof net.minecraft.nbt.LongTag) { + return fromNative((net.minecraft.nbt.LongTag) other); + + } else if (other instanceof net.minecraft.nbt.StringTag) { + return fromNative((net.minecraft.nbt.StringTag) other); + + } else if (other instanceof net.minecraft.nbt.IntTag) { + return fromNative((net.minecraft.nbt.IntTag) other); + + } else if (other instanceof net.minecraft.nbt.ByteTag) { + return fromNative((net.minecraft.nbt.ByteTag) other); + + } else if (other instanceof net.minecraft.nbt.ByteArrayTag) { + return fromNative((net.minecraft.nbt.ByteArrayTag) other); + + } else if (other instanceof net.minecraft.nbt.CompoundTag) { + return fromNative((net.minecraft.nbt.CompoundTag) other); + + } else if (other instanceof net.minecraft.nbt.FloatTag) { + return fromNative((net.minecraft.nbt.FloatTag) other); + + } else if (other instanceof net.minecraft.nbt.ShortTag) { + return fromNative((net.minecraft.nbt.ShortTag) other); + + } else if (other instanceof net.minecraft.nbt.DoubleTag) { + return fromNative((net.minecraft.nbt.DoubleTag) other); + } else { + throw new IllegalArgumentException("Can't convert other of type " + other.getClass().getCanonicalName()); + } + } + + public static IntArrayTag fromNative(net.minecraft.nbt.IntArrayTag other) { + int[] value = other.getIntArray(); + return new IntArrayTag(Arrays.copyOf(value, value.length)); + } + + public static ListTag fromNative(net.minecraft.nbt.ListTag other) { + other = (net.minecraft.nbt.ListTag) other.copy(); + List list = new ArrayList<>(); + Class listClass = StringTag.class; + int tags = other.size(); + for (int i = 0; i < tags; i++) { + Tag child = fromNative(other.remove(0)); + list.add(child); + listClass = child.getClass(); + } + return new ListTag(listClass, list); + } + + public static EndTag fromNative(net.minecraft.nbt.EndTag other) { + return new EndTag(); + } + + public static LongTag fromNative(net.minecraft.nbt.LongTag other) { + return new LongTag(other.getLong()); + } + + public static StringTag fromNative(net.minecraft.nbt.StringTag other) { + return new StringTag(other.asString()); + } + + public static IntTag fromNative(net.minecraft.nbt.IntTag other) { + return new IntTag(other.getInt()); + } + + public static ByteTag fromNative(net.minecraft.nbt.ByteTag other) { + return new ByteTag(other.getByte()); + } + + public static ByteArrayTag fromNative(net.minecraft.nbt.ByteArrayTag other) { + byte[] value = other.getByteArray(); + return new ByteArrayTag(Arrays.copyOf(value, value.length)); + } + + public static CompoundTag fromNative(net.minecraft.nbt.CompoundTag other) { + Set tags = other.getKeys(); + Map map = new HashMap<>(); + for (String tagName : tags) { + map.put(tagName, fromNative(other.getTag(tagName))); + } + return new CompoundTag(map); + } + + public static FloatTag fromNative(net.minecraft.nbt.FloatTag other) { + return new FloatTag(other.getFloat()); + } + + public static ShortTag fromNative(net.minecraft.nbt.ShortTag other) { + return new ShortTag(other.getShort()); + } + + public static DoubleTag fromNative(net.minecraft.nbt.DoubleTag other) { + return new DoubleTag(other.getDouble()); + } + +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/PropertyAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/PropertyAdapter.java new file mode 100644 index 000000000..fb46415cd --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/PropertyAdapter.java @@ -0,0 +1,70 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.collect.ImmutableList; +import com.sk89q.worldedit.registry.state.Property; + +import java.util.List; +import java.util.Optional; + +class PropertyAdapter> implements Property { + + private final net.minecraft.state.property.Property property; + private final List values; + + public PropertyAdapter(net.minecraft.state.property.Property property) { + this.property = property; + this.values = ImmutableList.copyOf(property.getValues()); + } + + @Override + public String getName() { + return property.getName(); + } + + @Override + public List getValues() { + return values; + } + + @Override + public T getValueFor(String string) throws IllegalArgumentException { + Optional val = property.getValue(string); + checkArgument(val.isPresent(), "%s has no value for %s", getName(), string); + return val.get(); + } + + @Override + public int hashCode() { + return getName().hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Property)) { + return false; + } + return getName().equals(((Property) obj).getName()); + } + +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/ThreadSafeCache.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/ThreadSafeCache.java new file mode 100644 index 000000000..491d80638 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/ThreadSafeCache.java @@ -0,0 +1,76 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import net.fabricmc.fabric.api.event.server.ServerTickCallback; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArraySet; + +/** + * Caches data that cannot be accessed from another thread safely. + */ +public class ThreadSafeCache implements ServerTickCallback { + + private static final long REFRESH_DELAY = 1000 * 30; + private static final ThreadSafeCache INSTANCE = new ThreadSafeCache(); + private Set onlineIds = Collections.emptySet(); + private long lastRefresh = 0; + + /** + * Get an concurrent-safe set of UUIDs of online players. + * + * @return a set of UUIDs + */ + public Set getOnlineIds() { + return onlineIds; + } + + @Override + public void tick(MinecraftServer server) { + long now = System.currentTimeMillis(); + + if (now - lastRefresh > REFRESH_DELAY) { + Set onlineIds = new HashSet<>(); + + if (server == null) { + return; + } + for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) { + if (player != null) { + onlineIds.add(player.getUuid()); + } + } + + this.onlineIds = new CopyOnWriteArraySet<>(onlineIds); + + lastRefresh = now; + } + } + + public static ThreadSafeCache getInstance() { + return INSTANCE; + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/TileEntityUtils.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/TileEntityUtils.java new file mode 100644 index 000000000..4faec9d9e --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/TileEntityUtils.java @@ -0,0 +1,79 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.sk89q.worldedit.math.BlockVector3; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.IntTag; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +/** + * Utility methods for setting tile entities in the world. + */ +final class TileEntityUtils { + + private TileEntityUtils() { + } + + /** + * Update the given tag compound with position information. + * + * @param tag the tag + * @param position the position + */ + private static void updateForSet(CompoundTag tag, BlockVector3 position) { + checkNotNull(tag); + checkNotNull(position); + + tag.put("x", new IntTag(position.getBlockX())); + tag.put("y", new IntTag(position.getBlockY())); + tag.put("z", new IntTag(position.getBlockZ())); + } + + /** + * Set a tile entity at the given location using the tile entity ID from + * the tag. + * + * @param world the world + * @param position the position + * @param tag the tag for the tile entity (may be null to do nothing) + */ + static void setTileEntity(World world, BlockVector3 position, @Nullable CompoundTag tag) { + if (tag != null) { + updateForSet(tag, position); + BlockEntity tileEntity = BlockEntity.createFromTag(tag); + if (tileEntity != null) { + world.setBlockEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()), tileEntity); + } + } + } + + public static CompoundTag copyNbtData(BlockEntity tile) { + CompoundTag tag = new CompoundTag(); + tile.toTag(tag); + return tag; + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/WorldEditFakePlayer.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/WorldEditFakePlayer.java new file mode 100644 index 000000000..ea65774b7 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/WorldEditFakePlayer.java @@ -0,0 +1,73 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric; + +import com.mojang.authlib.GameProfile; +import net.minecraft.entity.Entity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.network.chat.Component; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.ServerPlayerInteractionManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.stat.Stat; +import net.minecraft.world.dimension.DimensionType; + +import java.util.UUID; + +import javax.annotation.Nullable; + +public class WorldEditFakePlayer extends ServerPlayerEntity { + private static final GameProfile FAKE_WORLDEDIT_PROFILE = new GameProfile(UUID.nameUUIDFromBytes("worldedit".getBytes()), "[WorldEdit]"); + + public WorldEditFakePlayer(ServerWorld world) { + super(world.getServer(), world, FAKE_WORLDEDIT_PROFILE, new ServerPlayerInteractionManager(world)); + } + + @Override + public void tick() { + } + + @Override + public void increaseStat(Stat stat, int incrementer) { + } + + @Override + public void incrementStat(Stat stat) { + } + + @Override + public void sendMessage(Component component) { + } + + @Override + public void addChatMessage(Component component, boolean opt) { + } + + @Nullable + @Override + public Entity changeDimension(DimensionType dimensionType) { + return this; + } + + @Override + public boolean isInvulnerableTo(DamageSource damageSource) { + return true; + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinServerPlayerEntity.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinServerPlayerEntity.java new file mode 100644 index 000000000..fbad989c1 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/mixin/MixinServerPlayerEntity.java @@ -0,0 +1,45 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.mixin; + +import com.mojang.authlib.GameProfile; +import com.sk89q.worldedit.fabric.FabricWorldEdit; +import net.minecraft.container.ContainerListener; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.Hand; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ServerPlayerEntity.class) +public abstract class MixinServerPlayerEntity extends PlayerEntity implements ContainerListener { + + public MixinServerPlayerEntity(World world, GameProfile gameProfile) { + super(world, gameProfile); + } + + @Inject(method = "swingHand", at = @At(value = "HEAD")) + public void onSwing(Hand hand, CallbackInfo injectionInfo) { + FabricWorldEdit.inst.onLeftClickAir(this, this.world, hand); + } +} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java new file mode 100644 index 000000000..816d6c76f --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/net/handler/WECUIPacketHandler.java @@ -0,0 +1,61 @@ +/* + * 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 Lesser 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 Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.net.handler; + +import com.sk89q.worldedit.LocalSession; +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.fabric.FabricPlayer; +import com.sk89q.worldedit.fabric.FabricWorldEdit; +import net.fabricmc.fabric.api.network.PacketConsumer; +import net.fabricmc.fabric.api.network.PacketContext; +import net.fabricmc.fabric.api.network.ServerSidePacketRegistry; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.packet.CustomPayloadS2CPacket; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.network.packet.CustomPayloadC2SPacket; +import net.minecraft.util.Identifier; +import net.minecraft.util.PacketByteBuf; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +public final class WECUIPacketHandler { + private WECUIPacketHandler() { + } + + public static final Charset UTF_8_CHARSET = StandardCharsets.UTF_8; + private static final Identifier CUI_IDENTIFIER = new Identifier(FabricWorldEdit.MOD_ID, FabricWorldEdit.CUI_PLUGIN_CHANNEL); + + public static void init() { + ServerSidePacketRegistry.INSTANCE.register(CUI_IDENTIFIER, (packetContext, packetByteBuf) -> { + ServerPlayerEntity player = (ServerPlayerEntity) packetContext.getPlayer(); + LocalSession session = FabricWorldEdit.inst.getSession(player); + + if (session.hasCUISupport()) { + return; + } + + String text = packetByteBuf.toString(UTF_8_CHARSET); + final FabricPlayer actor = FabricAdapter.adaptPlayer(player); + session.handleCUIInitializationMessage(text, actor); + session.describeCUI(actor); + }); + } +} \ No newline at end of file diff --git a/worldedit-fabric/src/main/resources/assets/worldedit/icon.png b/worldedit-fabric/src/main/resources/assets/worldedit/icon.png new file mode 100644 index 000000000..dc269dcf7 Binary files /dev/null and b/worldedit-fabric/src/main/resources/assets/worldedit/icon.png differ diff --git a/worldedit-fabric/src/main/resources/defaults/worldedit.properties b/worldedit-fabric/src/main/resources/defaults/worldedit.properties new file mode 100644 index 000000000..d0296c15c --- /dev/null +++ b/worldedit-fabric/src/main/resources/defaults/worldedit.properties @@ -0,0 +1,33 @@ +#Don't put comments; they get removed +default-max-polygon-points=-1 +schematic-save-dir=schematics +super-pickaxe-many-drop-items=true +register-help=true +nav-wand-item=minecraft:compass +profile=false +trace-unflushed-sessions=false +super-pickaxe-drop-items=true +disallowed-blocks=minecraft:oak_sapling,minecraft:jungle_sapling,minecraft:dark_oak_sapling,minecraft:spruce_sapling,minecraft:birch_sapling,minecraft:acacia_sapling,minecraft:black_bed,minecraft:blue_bed,minecraft:brown_bed,minecraft:cyan_bed,minecraft:gray_bed,minecraft:green_bed,minecraft:light_blue_bed,minecraft:light_gray_bed,minecraft:lime_bed,minecraft:magenta_bed,minecraft:orange_bed,minecraft:pink_bed,minecraft:purple_bed,minecraft:red_bed,minecraft:white_bed,minecraft:yellow_bed,minecraft:powered_rail,minecraft:detector_rail,minecraft:grass,minecraft:dead_bush,minecraft:moving_piston,minecraft:piston_head,minecraft:sunflower,minecraft:rose_bush,minecraft:dandelion,minecraft:poppy,minecraft:brown_mushroom,minecraft:red_mushroom,minecraft:tnt,minecraft:torch,minecraft:fire,minecraft:redstone_wire,minecraft:wheat,minecraft:potatoes,minecraft:carrots,minecraft:melon_stem,minecraft:pumpkin_stem,minecraft:beetroots,minecraft:rail,minecraft:lever,minecraft:redstone_torch,minecraft:redstone_wall_torch,minecraft:repeater,minecraft:comparator,minecraft:stone_button,minecraft:birch_button,minecraft:acacia_button,minecraft:dark_oak_button,minecraft:jungle_button,minecraft:oak_button,minecraft:spruce_button,minecraft:cactus,minecraft:sugar_cane,minecraft:bedrock +max-super-pickaxe-size=5 +max-brush-radius=10 +craftscript-dir=craftscripts +no-double-slash=false +wand-item=minecraft:wooden_axe +shell-save-type= +scripting-timeout=3000 +snapshots-dir= +use-inventory-creative-override=false +log-file=worldedit.log +log-format=[%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s]: %5$s%6$s%n +max-changed-blocks=-1 +nav-wand-distance=50 +butcher-default-radius=-1 +default-max-changed-blocks=-1 +history-size=15 +use-inventory=false +allow-symbolic-links=false +use-inventory-override=false +log-commands=false +butcher-max-radius=-1 +max-polygon-points=20 +max-radius=-1 diff --git a/worldedit-fabric/src/main/resources/fabric.mod.json b/worldedit-fabric/src/main/resources/fabric.mod.json new file mode 100644 index 000000000..ee3e7b612 --- /dev/null +++ b/worldedit-fabric/src/main/resources/fabric.mod.json @@ -0,0 +1,37 @@ +{ + "schemaVersion": 1, + "id": "worldedit", + "version": "${version}", + + "name": "WorldEdit", + "description": "WorldEdit is an easy-to-use in-game world editor for Minecraft, supporting both single- and multi-player.", + "authors": [ + "sk89q", + "wizjany", + "TomyLobo", + "kenzierocks", + "Me4502" + ], + "contact": { + "homepage": "https://enginehub.org/worldedit/", + "sources": "https://github.com/EngineHub/WorldEdit" + }, + + "license": "GPL3", + "icon": "assets/worldedit/icon.png", + + "environment": "*", + "entrypoints": { + "main": [ + "com.sk89q.worldedit.fabric.FabricWorldEdit" + ] + }, + + "depends": { + "fabricloader": ">=0.4.0", + "fabric": "*" + }, + "mixins": [ + "worldedit.mixins.json" + ] +} \ No newline at end of file diff --git a/worldedit-fabric/src/main/resources/pack.mcmeta b/worldedit-fabric/src/main/resources/pack.mcmeta new file mode 100644 index 000000000..b48da3b7d --- /dev/null +++ b/worldedit-fabric/src/main/resources/pack.mcmeta @@ -0,0 +1,6 @@ +{ + "pack": { + "description": "WorldEdit Resources", + "pack_format": 4 + } +} \ No newline at end of file diff --git a/worldedit-fabric/src/main/resources/worldedit.mixins.json b/worldedit-fabric/src/main/resources/worldedit.mixins.json new file mode 100644 index 000000000..165bf44ce --- /dev/null +++ b/worldedit-fabric/src/main/resources/worldedit.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "package": "com.sk89q.worldedit.fabric.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinServerPlayerEntity" + ], + "server": [ + ], + "injectors": { + "defaultRequire": 1 + } +} \ No newline at end of file diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java index 37f1a67ff..a48ff6256 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgePlayer.java @@ -124,7 +124,7 @@ public class ForgePlayer extends AbstractPlayerActor { send = send + "|" + StringUtil.joinString(params, "|"); } PacketBuffer buffer = new PacketBuffer(Unpooled.copiedBuffer(send.getBytes(WECUIPacketHandler.UTF_8_CHARSET))); - SCustomPayloadPlayPacket packet = new SCustomPayloadPlayPacket(new ResourceLocation(ForgeWorldEdit.CUI_PLUGIN_CHANNEL), buffer); + SCustomPayloadPlayPacket packet = new SCustomPayloadPlayPacket(new ResourceLocation(ForgeWorldEdit.MOD_ID, ForgeWorldEdit.CUI_PLUGIN_CHANNEL), buffer); this.player.connection.sendPacket(packet); }