Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-11-17 00:20:09 +01:00
Add a feature generator and allow undoing of feature placement (https://github.com/EngineHub/WorldEdit/pull/2239)
* Add a feature generator and allow undoing of feature placement [WIP] Apply changes to Forge as well Use proper translatable components * Add a brush version of the feature command Use Java proxy classes * Add for Bukkit (only 1.19.3 for now) Clean up the proxies to use a switch Checkstyle is grumpy Add the obfuscated versions Remove debug text Fix missed "destroyBlock" deobfuscated proxy function * checkstyle
Dieser Commit ist enthalten in:
Ursprung
b4635e85c9
Commit
17d7850cf3
@ -29,6 +29,7 @@ import com.google.common.util.concurrent.Futures;
|
|||||||
import com.mojang.datafixers.util.Either;
|
import com.mojang.datafixers.util.Either;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
import com.sk89q.jnbt.NBTConstants;
|
import com.sk89q.jnbt.NBTConstants;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.blocks.BaseItem;
|
import com.sk89q.worldedit.blocks.BaseItem;
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
@ -65,6 +66,7 @@ import com.sk89q.worldedit.world.block.BlockState;
|
|||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@ -95,6 +97,7 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.world.item.context.UseOnContext;
|
import net.minecraft.world.item.context.UseOnContext;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.LevelSettings;
|
import net.minecraft.world.level.LevelSettings;
|
||||||
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
@ -107,6 +110,7 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
|||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.dimension.LevelStem;
|
import net.minecraft.world.level.dimension.LevelStem;
|
||||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||||
|
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import net.minecraft.world.level.storage.PrimaryLevelData;
|
import net.minecraft.world.level.storage.PrimaryLevelData;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
@ -890,6 +894,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
|||||||
BiomeType.REGISTRY.register(name.toString(), new BiomeType(name.toString()));
|
BiomeType.REGISTRY.register(name.toString(), new BiomeType(name.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Features
|
||||||
|
for (ResourceLocation name: server.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).keySet()) {
|
||||||
|
if (ConfiguredFeatureType.REGISTRY.get(name.toString()) == null) {
|
||||||
|
ConfiguredFeatureType.REGISTRY.register(name.toString(), new ConfiguredFeatureType(name.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// BiomeCategories
|
// BiomeCategories
|
||||||
Registry<Biome> biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME);
|
Registry<Biome> biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME);
|
||||||
@ -920,6 +930,15 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
|||||||
originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks);
|
originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean generateFeature(ConfiguredFeatureType type, World world, EditSession session, BlockVector3 pt) {
|
||||||
|
ServerLevel originalWorld = ((CraftWorld) world).getHandle();
|
||||||
|
ConfiguredFeature<?, ?> k = originalWorld.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.getId()));
|
||||||
|
ServerChunkCache chunkManager = originalWorld.getChunkSource();
|
||||||
|
WorldGenLevel proxyLevel = PaperweightServerLevelDelegateProxy.newInstance(session, originalWorld, this);
|
||||||
|
return k != null && k.place(proxyLevel, chunkManager.getGenerator(), random, new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()));
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Code that is less likely to break
|
// Code that is less likely to break
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
@ -0,0 +1,121 @@
|
|||||||
|
/*
|
||||||
|
* 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 General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.bukkit.adapter.ext.fawe.v1_20_R3;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
|
||||||
|
public class PaperweightServerLevelDelegateProxy implements InvocationHandler {
|
||||||
|
|
||||||
|
private final EditSession editSession;
|
||||||
|
private final ServerLevel serverLevel;
|
||||||
|
private final PaperweightAdapter adapter;
|
||||||
|
|
||||||
|
private PaperweightServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
|
||||||
|
this.editSession = editSession;
|
||||||
|
this.serverLevel = serverLevel;
|
||||||
|
this.adapter = adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel, PaperweightAdapter adapter) {
|
||||||
|
return (WorldGenLevel) Proxy.newProxyInstance(
|
||||||
|
serverLevel.getClass().getClassLoader(),
|
||||||
|
serverLevel.getClass().getInterfaces(),
|
||||||
|
new PaperweightServerLevelDelegateProxy(editSession, serverLevel, adapter)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BlockEntity getBlockEntity(BlockPos blockPos) {
|
||||||
|
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
|
||||||
|
if (tileEntity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
|
||||||
|
newEntity.load((CompoundTag) adapter.fromNativeLin(this.editSession.getFullBlock(BlockVector3.at(blockPos.getX(),
|
||||||
|
blockPos.getY(), blockPos.getZ())).getNbtReference().getValue()));
|
||||||
|
|
||||||
|
return newEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockState getBlockState(BlockPos blockPos) {
|
||||||
|
return adapter.adapt(this.editSession.getBlock(BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
|
||||||
|
try {
|
||||||
|
return editSession.setBlock(BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ()), adapter.adapt(blockState));
|
||||||
|
} catch (MaxChangedBlocksException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean removeBlock(BlockPos blockPos, boolean bl) {
|
||||||
|
try {
|
||||||
|
return editSession.setBlock(BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ()), BlockTypes.AIR.getDefaultState());
|
||||||
|
} catch (MaxChangedBlocksException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "a_", "getBlockState" -> {
|
||||||
|
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
|
||||||
|
// getBlockState
|
||||||
|
return getBlockState(blockPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "c_", "getBlockEntity" -> {
|
||||||
|
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
|
||||||
|
// getBlockEntity
|
||||||
|
return getBlockEntity(blockPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "a", "setBlock", "removeBlock", "destroyBlock" -> {
|
||||||
|
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
|
||||||
|
// setBlock
|
||||||
|
return setBlock(blockPos, blockState);
|
||||||
|
} else if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
|
||||||
|
// removeBlock (and also matches destroyBlock)
|
||||||
|
return removeBlock(blockPos, bl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return method.invoke(this.serverLevel, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -54,6 +54,7 @@ import com.sk89q.worldedit.world.biome.BiomeType;
|
|||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
import com.sk89q.worldedit.world.weather.WeatherType;
|
import com.sk89q.worldedit.world.weather.WeatherType;
|
||||||
import com.sk89q.worldedit.world.weather.WeatherTypes;
|
import com.sk89q.worldedit.world.weather.WeatherTypes;
|
||||||
import io.papermc.lib.PaperLib;
|
import io.papermc.lib.PaperLib;
|
||||||
@ -519,6 +520,16 @@ public class BukkitWorld extends AbstractWorld {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean generateFeature(ConfiguredFeatureType type, EditSession editSession, BlockVector3 position) {
|
||||||
|
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
|
||||||
|
if (adapter != null) {
|
||||||
|
return adapter.generateFeature(type, getWorld(), editSession, position);
|
||||||
|
}
|
||||||
|
// No adapter, we can't generate this.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static volatile boolean hasWarnedImplError = false;
|
private static volatile boolean hasWarnedImplError = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,6 +29,7 @@ import com.fastasyncworldedit.core.queue.IChunkGet;
|
|||||||
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
import com.fastasyncworldedit.core.queue.implementation.packet.ChunkPacket;
|
||||||
import com.sk89q.jnbt.LinBusConverter;
|
import com.sk89q.jnbt.LinBusConverter;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.blocks.BaseItem;
|
import com.sk89q.worldedit.blocks.BaseItem;
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
@ -48,6 +49,7 @@ import com.sk89q.worldedit.world.biome.BiomeType;
|
|||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
import com.sk89q.worldedit.world.registry.BlockMaterial;
|
||||||
import org.bukkit.Keyed;
|
import org.bukkit.Keyed;
|
||||||
@ -319,6 +321,20 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
|
|||||||
* @param chunks a list of chunk coordinates to send biome updates for
|
* @param chunks a list of chunk coordinates to send biome updates for
|
||||||
*/
|
*/
|
||||||
default void sendBiomeUpdates(World world, Iterable<BlockVector2> chunks) {
|
default void sendBiomeUpdates(World world, Iterable<BlockVector2> chunks) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a Minecraft feature at the given location.
|
||||||
|
*
|
||||||
|
* @param feature The feature
|
||||||
|
* @param world The world
|
||||||
|
* @param session The EditSession
|
||||||
|
* @param pt The location
|
||||||
|
* @return If it succeeded
|
||||||
|
*/
|
||||||
|
default boolean generateFeature(ConfiguredFeatureType feature, World world, EditSession session, BlockVector3 pt) {
|
||||||
|
throw new UnsupportedOperationException("This adapter does not support generating features.");
|
||||||
}
|
}
|
||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
|
@ -66,6 +66,7 @@ import com.sk89q.worldedit.LocalSession;
|
|||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.command.argument.Arguments;
|
import com.sk89q.worldedit.command.argument.Arguments;
|
||||||
|
import com.sk89q.worldedit.command.factory.FeatureGeneratorFactory;
|
||||||
import com.sk89q.worldedit.command.factory.ReplaceFactory;
|
import com.sk89q.worldedit.command.factory.ReplaceFactory;
|
||||||
import com.sk89q.worldedit.command.factory.TreeGeneratorFactory;
|
import com.sk89q.worldedit.command.factory.TreeGeneratorFactory;
|
||||||
import com.sk89q.worldedit.command.tool.BrushTool;
|
import com.sk89q.worldedit.command.tool.BrushTool;
|
||||||
@ -116,6 +117,7 @@ import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
import org.anarres.parallelgzip.ParallelGZIPOutputStream;
|
import org.anarres.parallelgzip.ParallelGZIPOutputStream;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
import org.enginehub.piston.annotation.Command;
|
import org.enginehub.piston.annotation.Command;
|
||||||
import org.enginehub.piston.annotation.CommandContainer;
|
import org.enginehub.piston.annotation.CommandContainer;
|
||||||
import org.enginehub.piston.annotation.param.Arg;
|
import org.enginehub.piston.annotation.param.Arg;
|
||||||
@ -1556,6 +1558,24 @@ public class BrushCommands {
|
|||||||
set(context, new ButcherBrush(flags), "worldedit.brush.butcher").setSize(radius);
|
set(context, new ButcherBrush(flags), "worldedit.brush.butcher").setSize(radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "feature",
|
||||||
|
desc = "Feature brush, paints Minecraft generation features"
|
||||||
|
)
|
||||||
|
@CommandPermissions("worldedit.brush.feature")
|
||||||
|
public void feature(Player player, LocalSession localSession,
|
||||||
|
@Arg(desc = "The shape of the region")
|
||||||
|
RegionFactory shape,
|
||||||
|
@Arg(desc = "The size of the brush", def = "5")
|
||||||
|
double radius,
|
||||||
|
@Arg(desc = "The density of the brush", def = "5")
|
||||||
|
double density,
|
||||||
|
@Arg(desc = "The type of feature to use")
|
||||||
|
ConfiguredFeatureType type) throws WorldEditException {
|
||||||
|
setOperationBasedBrush(player, localSession, radius,
|
||||||
|
new Paint(new FeatureGeneratorFactory(type), density / 100), shape, "worldedit.brush.feature");
|
||||||
|
}
|
||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
public BrushSettings process(Player player, Arguments arguments, BrushSettings settings)
|
public BrushSettings process(Player player, Arguments arguments, BrushSettings settings)
|
||||||
throws WorldEditException {
|
throws WorldEditException {
|
||||||
|
@ -57,6 +57,7 @@ import com.sk89q.worldedit.util.TreeGenerator.TreeType;
|
|||||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
import org.enginehub.piston.annotation.Command;
|
import org.enginehub.piston.annotation.Command;
|
||||||
import org.enginehub.piston.annotation.CommandContainer;
|
import org.enginehub.piston.annotation.CommandContainer;
|
||||||
import org.enginehub.piston.annotation.param.Arg;
|
import org.enginehub.piston.annotation.param.Arg;
|
||||||
@ -359,6 +360,23 @@ public class GenerationCommands {
|
|||||||
return affected;
|
return affected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "/feature",
|
||||||
|
desc = "Generate Minecraft features"
|
||||||
|
)
|
||||||
|
@CommandPermissions("worldedit.generation.feature")
|
||||||
|
@Logging(POSITION)
|
||||||
|
public int feature(Actor actor, LocalSession session, EditSession editSession,
|
||||||
|
@Arg(desc = "The feature")
|
||||||
|
ConfiguredFeatureType feature) throws WorldEditException {
|
||||||
|
if (editSession.getWorld().generateFeature(feature, editSession, session.getPlacementPosition(actor))) {
|
||||||
|
actor.printInfo(Caption.of("worldedit.feature.created"));
|
||||||
|
} else {
|
||||||
|
actor.printError(Caption.of("worldedit.feature.failed"));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
name = "/hpyramid",
|
name = "/hpyramid",
|
||||||
desc = "Generate a hollow pyramid"
|
desc = "Generate a hollow pyramid"
|
||||||
|
@ -32,6 +32,7 @@ import com.sk89q.worldedit.world.entity.EntityType;
|
|||||||
import com.sk89q.worldedit.world.fluid.FluidCategory;
|
import com.sk89q.worldedit.world.fluid.FluidCategory;
|
||||||
import com.sk89q.worldedit.world.fluid.FluidType;
|
import com.sk89q.worldedit.world.fluid.FluidType;
|
||||||
import com.sk89q.worldedit.world.gamemode.GameMode;
|
import com.sk89q.worldedit.world.gamemode.GameMode;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
import com.sk89q.worldedit.world.item.ItemCategory;
|
import com.sk89q.worldedit.world.item.ItemCategory;
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
import com.sk89q.worldedit.world.weather.WeatherType;
|
import com.sk89q.worldedit.world.weather.WeatherType;
|
||||||
@ -62,7 +63,8 @@ public final class RegistryConverter<V extends Keyed> implements ArgumentConvert
|
|||||||
FluidType.class,
|
FluidType.class,
|
||||||
FluidCategory.class,
|
FluidCategory.class,
|
||||||
GameMode.class,
|
GameMode.class,
|
||||||
WeatherType.class
|
WeatherType.class,
|
||||||
|
ConfiguredFeatureType.class
|
||||||
)
|
)
|
||||||
.stream()
|
.stream()
|
||||||
.map(c -> (Class<Keyed>) c)
|
.map(c -> (Class<Keyed>) c)
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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 General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.command.factory;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.function.Contextual;
|
||||||
|
import com.sk89q.worldedit.function.EditContext;
|
||||||
|
import com.sk89q.worldedit.function.generator.FeatureGenerator;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
|
|
||||||
|
public final class FeatureGeneratorFactory implements Contextual<FeatureGenerator> {
|
||||||
|
private final ConfiguredFeatureType type;
|
||||||
|
|
||||||
|
public FeatureGeneratorFactory(ConfiguredFeatureType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeatureGenerator createFromContext(EditContext input) {
|
||||||
|
return new FeatureGenerator((EditSession) input.getDestination(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "feature of type " + type;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* 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 General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.function.generator;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
|
import com.sk89q.worldedit.function.RegionFunction;
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates forests by searching for the ground starting from the given upper Y
|
||||||
|
* coordinate for every column given.
|
||||||
|
*/
|
||||||
|
public class FeatureGenerator implements RegionFunction {
|
||||||
|
|
||||||
|
private final ConfiguredFeatureType featureType;
|
||||||
|
private final EditSession editSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param editSession the edit session
|
||||||
|
* @param featureType the feature type
|
||||||
|
*/
|
||||||
|
public FeatureGenerator(EditSession editSession, ConfiguredFeatureType featureType) {
|
||||||
|
this.editSession = editSession;
|
||||||
|
this.featureType = featureType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(BlockVector3 position) throws WorldEditException {
|
||||||
|
return editSession.getWorld().generateFeature(featureType, editSession, position);
|
||||||
|
}
|
||||||
|
}
|
@ -46,6 +46,7 @@ import com.sk89q.worldedit.world.biome.BiomeType;
|
|||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
|
import com.sk89q.worldedit.world.generation.ConfiguredFeatureType;
|
||||||
import com.sk89q.worldedit.world.weather.WeatherType;
|
import com.sk89q.worldedit.world.weather.WeatherType;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -311,6 +312,18 @@ public interface World extends Extent, Keyed, IChunkCache<IChunkGet> {
|
|||||||
boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws
|
boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws
|
||||||
MaxChangedBlocksException;
|
MaxChangedBlocksException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a feature at the given position.
|
||||||
|
*
|
||||||
|
* @param type The feature type
|
||||||
|
* @param editSession The {@link EditSession}
|
||||||
|
* @param position The position
|
||||||
|
* @return True if the generation was successful
|
||||||
|
*/
|
||||||
|
default boolean generateFeature(ConfiguredFeatureType type, EditSession editSession, BlockVector3 position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the chunk at the given position if it isn't loaded.
|
* Load the chunk at the given position if it isn't loaded.
|
||||||
*
|
*
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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 General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.world.generation;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.registry.Keyed;
|
||||||
|
import com.sk89q.worldedit.registry.NamespacedRegistry;
|
||||||
|
|
||||||
|
public class ConfiguredFeatureType implements Keyed {
|
||||||
|
public static final NamespacedRegistry<ConfiguredFeatureType> REGISTRY = new NamespacedRegistry<>("configured feature type");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public ConfiguredFeatureType(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
}
|
@ -514,6 +514,8 @@
|
|||||||
"worldedit.sphere.created": "{0} blocks have been created.",
|
"worldedit.sphere.created": "{0} blocks have been created.",
|
||||||
"worldedit.forestgen.created": "{0} trees created.",
|
"worldedit.forestgen.created": "{0} trees created.",
|
||||||
"worldedit.pumpkins.created": "{0} pumpkin patches created.",
|
"worldedit.pumpkins.created": "{0} pumpkin patches created.",
|
||||||
|
"worldedit.feature.created": "Feature created.",
|
||||||
|
"worldedit.feature.failed": "Failed to generate feature. Is it a valid spot for it?",
|
||||||
"worldedit.pyramid.created": "{0} blocks have been created.",
|
"worldedit.pyramid.created": "{0} blocks have been created.",
|
||||||
"worldedit.generate.created": "{0} blocks have been created.",
|
"worldedit.generate.created": "{0} blocks have been created.",
|
||||||
"worldedit.generatebiome.changed": "{0} biomes affected.",
|
"worldedit.generatebiome.changed": "{0} biomes affected.",
|
||||||
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* 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 General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.fabric.internal;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
|
import com.sk89q.worldedit.fabric.FabricAdapter;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class FabricServerLevelDelegateProxy implements InvocationHandler {
|
||||||
|
|
||||||
|
private final EditSession editSession;
|
||||||
|
private final ServerLevel serverLevel;
|
||||||
|
|
||||||
|
private FabricServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel) {
|
||||||
|
this.editSession = editSession;
|
||||||
|
this.serverLevel = serverLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel) {
|
||||||
|
return (WorldGenLevel) Proxy.newProxyInstance(
|
||||||
|
serverLevel.getClass().getClassLoader(),
|
||||||
|
serverLevel.getClass().getInterfaces(),
|
||||||
|
new FabricServerLevelDelegateProxy(editSession, serverLevel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BlockEntity getBlockEntity(BlockPos blockPos) {
|
||||||
|
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
|
||||||
|
if (tileEntity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
|
||||||
|
newEntity.load(NBTConverter.toNative(this.editSession.getFullBlock(FabricAdapter.adapt(blockPos)).getNbtReference().getValue()));
|
||||||
|
|
||||||
|
return newEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockState getBlockState(BlockPos blockPos) {
|
||||||
|
return FabricAdapter.adapt(this.editSession.getBlock(FabricAdapter.adapt(blockPos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
|
||||||
|
try {
|
||||||
|
return editSession.setBlock(FabricAdapter.adapt(blockPos), FabricAdapter.adapt(blockState));
|
||||||
|
} catch (MaxChangedBlocksException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean removeBlock(BlockPos blockPos, boolean bl) {
|
||||||
|
try {
|
||||||
|
return editSession.setBlock(FabricAdapter.adapt(blockPos), BlockTypes.AIR.getDefaultState());
|
||||||
|
} catch (MaxChangedBlocksException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "getBlockState", "method_8320" -> {
|
||||||
|
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
|
||||||
|
return getBlockState(blockPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "getBlockEntity", "method_8321" -> {
|
||||||
|
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
|
||||||
|
return getBlockEntity(blockPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "setBlock", "method_8652" -> {
|
||||||
|
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
|
||||||
|
return setBlock(blockPos, blockState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "removeBlock", "destroyBlock", "method_8650", "method_8651" -> {
|
||||||
|
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
|
||||||
|
return removeBlock(blockPos, bl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return method.invoke(this.serverLevel, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
* 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 General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sk89q.worldedit.forge.internal;
|
||||||
|
|
||||||
|
import com.sk89q.worldedit.EditSession;
|
||||||
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
|
import com.sk89q.worldedit.forge.ForgeAdapter;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class ForgeServerLevelDelegateProxy implements InvocationHandler {
|
||||||
|
|
||||||
|
private final EditSession editSession;
|
||||||
|
private final ServerLevel serverLevel;
|
||||||
|
|
||||||
|
private ForgeServerLevelDelegateProxy(EditSession editSession, ServerLevel serverLevel) {
|
||||||
|
this.editSession = editSession;
|
||||||
|
this.serverLevel = serverLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WorldGenLevel newInstance(EditSession editSession, ServerLevel serverLevel) {
|
||||||
|
return (WorldGenLevel) Proxy.newProxyInstance(
|
||||||
|
serverLevel.getClass().getClassLoader(),
|
||||||
|
serverLevel.getClass().getInterfaces(),
|
||||||
|
new ForgeServerLevelDelegateProxy(editSession, serverLevel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private BlockEntity getBlockEntity(BlockPos blockPos) {
|
||||||
|
BlockEntity tileEntity = this.serverLevel.getChunkAt(blockPos).getBlockEntity(blockPos);
|
||||||
|
if (tileEntity == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BlockEntity newEntity = tileEntity.getType().create(blockPos, getBlockState(blockPos));
|
||||||
|
newEntity.load(NBTConverter.toNative(this.editSession.getFullBlock(ForgeAdapter.adapt(blockPos)).getNbtReference().getValue()));
|
||||||
|
|
||||||
|
return newEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockState getBlockState(BlockPos blockPos) {
|
||||||
|
return ForgeAdapter.adapt(this.editSession.getBlock(ForgeAdapter.adapt(blockPos)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setBlock(BlockPos blockPos, BlockState blockState) {
|
||||||
|
try {
|
||||||
|
return editSession.setBlock(ForgeAdapter.adapt(blockPos), ForgeAdapter.adapt(blockState));
|
||||||
|
} catch (MaxChangedBlocksException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean removeBlock(BlockPos blockPos, boolean bl) {
|
||||||
|
try {
|
||||||
|
return editSession.setBlock(ForgeAdapter.adapt(blockPos), BlockTypes.AIR.getDefaultState());
|
||||||
|
} catch (MaxChangedBlocksException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "getBlockState", "m_8055_" -> {
|
||||||
|
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
|
||||||
|
return getBlockState(blockPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "getBlockEntity", "m_7702_" -> {
|
||||||
|
if (args.length == 1 && args[0] instanceof BlockPos blockPos) {
|
||||||
|
return getBlockEntity(blockPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "setBlock", "m_7731_" -> {
|
||||||
|
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) {
|
||||||
|
return setBlock(blockPos, blockState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "removeBlock", "destroyBlock", "m_7471_", "m_7740_" -> {
|
||||||
|
if (args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) {
|
||||||
|
return removeBlock(blockPos, bl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return method.invoke(this.serverLevel, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren