3
0
Mirror von https://github.com/IntellectualSites/FastAsyncWorldEdit.git synchronisiert 2024-11-19 17:30:08 +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:
Maddy Miller 2023-03-10 21:39:24 +10:00 committet von dordsor21
Ursprung 0566bd359e
Commit 9de9bb3498
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 1E53E88969FFCF0B
14 geänderte Dateien mit 617 neuen und 10 gelöschten Zeilen

Datei anzeigen

@ -28,6 +28,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;
@ -78,6 +79,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;
@ -93,6 +95,7 @@ import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener; import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable; import net.minecraft.util.StringRepresentable;
import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.world.Clearable; import net.minecraft.world.Clearable;
@ -105,6 +108,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;
@ -117,12 +121,14 @@ 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;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer; import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
@ -173,6 +179,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
private final Field chunkProviderExecutorField; private final Field chunkProviderExecutorField;
private final Watchdog watchdog; private final Watchdog watchdog;
private static final RandomSource random = RandomSource.create();
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// Code that may break between versions of Minecraft // Code that may break between versions of Minecraft
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -308,6 +316,21 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId); return combinedId == 0 && state.getBlockType() != BlockTypes.AIR ? OptionalInt.empty() : OptionalInt.of(combinedId);
} }
public BlockState adapt(net.minecraft.world.level.block.state.BlockState blockState) {
int internalId = Block.getId(blockState);
BlockState state = BlockStateIdAccess.getBlockStateById(internalId);
if (state == null) {
state = BukkitAdapter.adapt(CraftBlockData.createData(blockState));
}
return state;
}
public net.minecraft.world.level.block.state.BlockState adapt(BlockState blockState) {
int internalId = BlockStateIdAccess.getBlockStateId(blockState);
return Block.stateById(internalId);
}
@Override @Override
public BlockState getBlock(Location location) { public BlockState getBlock(Location location) {
checkNotNull(location); checkNotNull(location);
@ -321,14 +344,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); LevelChunk chunk = handle.getChunk(x >> 4, z >> 4);
final BlockPos blockPos = new BlockPos(x, y, z); final BlockPos blockPos = new BlockPos(x, y, z);
final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos);
int internalId = Block.getId(blockData); return adapt(blockData);
BlockState state = BlockStateIdAccess.getBlockStateById(internalId);
if (state == null) {
org.bukkit.block.Block bukkitBlock = location.getBlock();
state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
}
return state;
} }
@Override @Override
@ -865,7 +881,23 @@ 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()));
}
}
}*/
@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

Datei anzeigen

@ -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_R1;
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.fromNativeBinary(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);
}
}

Datei anzeigen

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

Datei anzeigen

@ -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.AdventureNBTConverter; import com.sk89q.jnbt.AdventureNBTConverter;
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;
@ -49,6 +50,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;
@ -280,6 +282,19 @@ public interface BukkitImplAdapter<T> extends IBukkitAdapter {
throw new UnsupportedOperationException("This adapter does not support clearing block contents."); throw new UnsupportedOperationException("This adapter does not support clearing block contents.");
} }
/**
* 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
default BlockMaterial getMaterial(BlockType blockType) { default BlockMaterial getMaterial(BlockType blockType) {
return getMaterial(blockType.getDefaultState()); return getMaterial(blockType.getDefaultState());

Datei anzeigen

@ -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;
@ -1458,6 +1460,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 {

Datei anzeigen

@ -56,6 +56,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;
@ -345,6 +346,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"

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -498,6 +498,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.",

Datei anzeigen

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

Datei anzeigen

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