feature(fabric): added fabric support (#491)

* Initial work towards Fabric compat. This does not compile yet

* Further updates - should compile but Gradle is being weird.

* Remove useless buildscript extras

* Added mixins to buildscript classpath to fix Loom crash

* Make it compile

* Got it building and added interaction

* Fixed review comments

* Use ServerPlayerEntity for FakePlayer

* Use method references for nicer names

* Fixed remaining comments and added networking for CUI

* Output as dist.jar

* Added mixins for left click air

* Use regex for cleanliness
This commit is contained in:
Matthew Miller 2019-06-27 22:25:02 +10:00 committed by GitHub
parent 7d558ccffa
commit aa8d34c913
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 6159 additions and 6 deletions

View File

@ -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 {

View File

@ -60,6 +60,18 @@
<allow pkg="com.mojang.datafixers" />
</subpackage>
<subpackage name="fabric">
<allow pkg="net.minecraft"/>
<allow pkg="net.fabricmc"/>
<allow pkg="com.mojang.authlib"/>
<allow pkg="org.apache.logging.log4j"/>
<allow pkg="org.lwjgl"/>
<allow pkg="io.netty.buffer"/>
<allow pkg="org.spongepowered" />
<allow pkg="com.mojang.brigadier" />
<allow pkg="com.mojang.datafixers" />
</subpackage>
<subpackage name="sponge">
<allow pkg="net.minecraft"/>
<allow pkg="com.google.inject" />

View File

@ -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"
}

View File

@ -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)

View File

@ -0,0 +1,128 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<ServerCommandSource> dispatcher, org.enginehub.piston.Command command) {
ImmutableList.Builder<String> aliases = ImmutableList.builder();
aliases.add(command.getName()).addAll(command.getAliases());
Command<ServerCommandSource> 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<ServerCommandSource> 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<ServerCommandSource> 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<Suggestions> suggest(CommandContext<ServerCommandSource> context,
SuggestionsBuilder builder) throws CommandSyntaxException {
CommandSuggestionEvent event = new CommandSuggestionEvent(
FabricAdapter.adaptPlayer(context.getSource().getPlayer()),
builder.getInput()
);
WorldEdit.getInstance().getEventBus().post(event);
List<Substring> suggestions = event.getSuggestions();
ImmutableList.Builder<Suggestion> 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())
);
}
}

View File

@ -0,0 +1,241 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<Property<?>, Object> adaptProperties(BlockType block, Map<net.minecraft.state.property.Property<?>, Comparable<?>> mcProps) {
Map<Property<?>, Object> props = new TreeMap<>(Comparator.comparing(Property::getName));
for (Map.Entry<net.minecraft.state.property.Property<?>, 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<Block, net.minecraft.block.BlockState> stateContainer,
net.minecraft.block.BlockState newState, Map<Property<?>, Object> states) {
for (Map.Entry<Property<?>, 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<Property<?>, 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);
}
}

View File

@ -0,0 +1,58 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}
}

View File

@ -0,0 +1,46 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<BlockType> 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<BlockType> getAll(Category<BlockType> category) {
return getCategorisedByName(category.getId());
}
}

View File

@ -0,0 +1,93 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -0,0 +1,80 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<Material, FabricBlockMaterial> 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<String, ? extends Property<?>> getProperties(BlockType blockType) {
Block block = FabricAdapter.adapt(blockType);
Map<String, Property<?>> map = new TreeMap<>();
Collection<net.minecraft.state.property.Property<?>> 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));
}
}

View File

@ -0,0 +1,45 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,116 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<net.minecraft.entity.Entity> 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> T getFacet(Class<? extends T> 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;
}
}
}

View File

@ -0,0 +1,151 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@ -0,0 +1,46 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<ItemType> 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<ItemType> getAll(Category<ItemType> category) {
return getCategorisedByName(category.getId());
}
}

View File

@ -0,0 +1,42 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@ -0,0 +1,50 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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) {}
}
}

View File

@ -0,0 +1,202 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<? extends World> getWorlds() {
Iterable<ServerWorld> worlds = server.getWorlds();
List<World> 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<String> 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<Capability, Preference> getCapabilities() {
Map<Capability, Preference> 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<Actor> getConnectedUsers() {
List<Actor> users = new ArrayList<>();
PlayerManager scm = server.getPlayerManager();
for (ServerPlayerEntity entity : scm.getPlayerList()) {
if (entity != null) {
users.add(new FabricPlayer(entity));
}
}
return users;
}
}

View File

@ -0,0 +1,267 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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> T getFacet(Class<? extends T> cls) {
return null;
}
@Override
public <B extends BlockStateHolder<B>> 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;
}
}
}

View File

@ -0,0 +1,75 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -0,0 +1,597 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<World> 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 <B extends BlockStateHolder<B>> 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<ServerWorld, WorldEditFakePlayer> 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<? extends FeatureConfig> 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<FeatureConfig> generator = (Feature<FeatureConfig>) 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<BlockVector2> chunks) {
fixLighting(chunks);
}
@Override
public void fixLighting(Iterable<BlockVector2> 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<? extends Entity> 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<? extends Entity> 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 = 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() {
}
}
}

View File

@ -0,0 +1,326 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -0,0 +1,252 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<String, Tag> 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<Tag> list = new ArrayList<>();
Class<? extends Tag> 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<String> tags = other.getKeys();
Map<String, Tag> 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());
}
}

View File

@ -0,0 +1,70 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<T extends Comparable<T>> implements Property<T> {
private final net.minecraft.state.property.Property<T> property;
private final List<T> values;
public PropertyAdapter(net.minecraft.state.property.Property<T> property) {
this.property = property;
this.values = ImmutableList.copyOf(property.getValues());
}
@Override
public String getName() {
return property.getName();
}
@Override
public List<T> getValues() {
return values;
}
@Override
public T getValueFor(String string) throws IllegalArgumentException {
Optional<T> 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());
}
}

View File

@ -0,0 +1,76 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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<UUID> onlineIds = Collections.emptySet();
private long lastRefresh = 0;
/**
* Get an concurrent-safe set of UUIDs of online players.
*
* @return a set of UUIDs
*/
public Set<UUID> getOnlineIds() {
return onlineIds;
}
@Override
public void tick(MinecraftServer server) {
long now = System.currentTimeMillis();
if (now - lastRefresh > REFRESH_DELAY) {
Set<UUID> 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;
}
}

View File

@ -0,0 +1,79 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -0,0 +1,73 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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;
}
}

View File

@ -0,0 +1,45 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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);
}
}

View File

@ -0,0 +1,61 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* 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 <http://www.gnu.org/licenses/>.
*/
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);
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -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

View File

@ -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"
]
}

View File

@ -0,0 +1,6 @@
{
"pack": {
"description": "WorldEdit Resources",
"pack_format": 4
}
}

View File

@ -0,0 +1,13 @@
{
"required": true,
"package": "com.sk89q.worldedit.fabric.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MixinServerPlayerEntity"
],
"server": [
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -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);
}