From c2da4953c788689e74d9618a71c279faf2d3706e Mon Sep 17 00:00:00 2001 From: Madeline Miller Date: Sat, 31 Dec 2022 21:20:56 +1000 Subject: [PATCH 1/4] Add a feature generator and allow undoing of feature placement [WIP] Apply changes to Forge as well Use proper translatable components --- .../accepted-core-public-api-changes.json | 7 + .../worldedit/command/GenerationCommands.java | 18 ++ .../command/argument/RegistryConverter.java | 4 +- .../java/com/sk89q/worldedit/world/World.java | 13 + .../generation/ConfiguredFeatureType.java | 38 +++ .../src/main/resources/lang/strings.json | 2 + .../sk89q/worldedit/fabric/FabricAdapter.java | 11 + .../sk89q/worldedit/fabric/FabricWorld.java | 11 +- .../worldedit/fabric/FabricWorldEdit.java | 7 + .../internal/FabricEditSessionDelegate.java | 286 ++++++++++++++++++ .../com/sk89q/worldedit/forge/ForgeWorld.java | 11 +- .../sk89q/worldedit/forge/ForgeWorldEdit.java | 7 + .../internal/ForgeEditSessionDelegate.java | 284 +++++++++++++++++ 13 files changed, 696 insertions(+), 3 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java create mode 100644 worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java diff --git a/verification/src/changes/accepted-core-public-api-changes.json b/verification/src/changes/accepted-core-public-api-changes.json index 2d0bde7e9c..8400941833 100644 --- a/verification/src/changes/accepted-core-public-api-changes.json +++ b/verification/src/changes/accepted-core-public-api-changes.json @@ -122,6 +122,13 @@ "changes": [ "METHOD_NEW_DEFAULT" ] + }, + { + "type": "com.sk89q.worldedit.world.World", + "member": "Method com.sk89q.worldedit.world.World.generateFeature(com.sk89q.worldedit.world.generation.ConfiguredFeatureType,com.sk89q.worldedit.EditSession,com.sk89q.worldedit.math.BlockVector3)", + "changes": [ + "METHOD_NEW_DEFAULT" + ] } ], "LazyReference was never publicly extensible": [ diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java index c1f769d859..491942fb00 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/GenerationCommands.java @@ -39,6 +39,7 @@ import com.sk89q.worldedit.util.formatting.text.TextComponent; import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; @@ -235,6 +236,23 @@ public int pumpkins(Actor actor, LocalSession session, EditSession editSession, 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(TranslatableComponent.of("worldedit.feature.created")); + } else { + actor.printError(TranslatableComponent.of("worldedit.feature.failed")); + } + return 0; + } + @Command( name = "/hpyramid", desc = "Generate a hollow pyramid" diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java index ad07203a66..38edcec64a 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/argument/RegistryConverter.java @@ -32,6 +32,7 @@ import com.sk89q.worldedit.world.fluid.FluidCategory; import com.sk89q.worldedit.world.fluid.FluidType; 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.ItemType; import com.sk89q.worldedit.world.weather.WeatherType; @@ -62,7 +63,8 @@ public static void register(CommandManager commandManager) { FluidType.class, FluidCategory.class, GameMode.class, - WeatherType.class + WeatherType.class, + ConfiguredFeatureType.class ) .stream() .map(c -> (Class) c) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java index 276b206ff9..bfbb3f7fdd 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/World.java @@ -41,6 +41,7 @@ import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockStateHolder; import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.weather.WeatherType; import java.nio.file.Path; @@ -273,6 +274,18 @@ default boolean regenerate(Region region, Extent extent, RegenOptions options) { */ boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 position) throws 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. * diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java new file mode 100644 index 0000000000..67ddea478e --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java @@ -0,0 +1,38 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.generation; + +import com.sk89q.worldedit.registry.Keyed; +import com.sk89q.worldedit.registry.NamespacedRegistry; + +public class ConfiguredFeatureType implements Keyed { + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("configured feature type"); + + private final String id; + + public ConfiguredFeatureType(String id) { + this.id = id; + } + + @Override + public String getId() { + return this.id; + } +} diff --git a/worldedit-core/src/main/resources/lang/strings.json b/worldedit-core/src/main/resources/lang/strings.json index 84921ef860..2d328da7fc 100644 --- a/worldedit-core/src/main/resources/lang/strings.json +++ b/worldedit-core/src/main/resources/lang/strings.json @@ -245,6 +245,8 @@ "worldedit.sphere.created": "{0} blocks have been created.", "worldedit.forestgen.created": "{0} trees 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.generate.created": "{0} blocks have been created.", "worldedit.generatebiome.changed": "{0} biomes affected.", diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java index a4bfa0f365..f63fd6b72a 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricAdapter.java @@ -31,6 +31,7 @@ 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.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; @@ -47,6 +48,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.properties.DirectionProperty; import net.minecraft.world.phys.Vec3; @@ -196,6 +198,15 @@ public static BlockState adapt(net.minecraft.world.level.block.state.BlockState return worldEdit; } + public static BaseBlock adapt(BlockEntity blockEntity) { + int blockStateId = Block.getId(blockEntity.getBlockState()); + BlockState worldEdit = BlockStateIdAccess.getBlockStateById(blockStateId); + if (worldEdit == null) { + worldEdit = FabricTransmogrifier.transmogToWorldEdit(blockEntity.getBlockState()); + } + return worldEdit.toBaseBlock(LazyReference.from(() -> NBTConverter.fromNative(blockEntity.saveWithId()))); + } + public static Block adapt(BlockType blockType) { return FabricWorldEdit.getRegistry(Registries.BLOCK).get(new ResourceLocation(blockType.getId())); } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java index fa811ef8e9..ee17530411 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java @@ -34,6 +34,7 @@ import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.fabric.internal.ExtendedMinecraftServer; +import com.sk89q.worldedit.fabric.internal.FabricEditSessionDelegate; import com.sk89q.worldedit.fabric.internal.FabricWorldNativeAccess; import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.fabric.mixin.AccessorDerivedLevelData; @@ -60,6 +61,7 @@ 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.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; @@ -463,11 +465,18 @@ public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position = position.add(0, 1, 0); } return generator != null && generator.place( - world, chunkManager.getGenerator(), random, + new FabricEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, FabricAdapter.toBlockPos(position) ); } + public boolean generateFeature(ConfiguredFeatureType type, EditSession editSession, BlockVector3 position) { + ServerLevel world = (ServerLevel) getWorld(); + ConfiguredFeature k = world.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.getId())); + ServerChunkCache chunkManager = world.getChunkSource(); + return k != null && k.place(new FabricEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, FabricAdapter.toBlockPos(position)); + } + @Override public void checkLoadedChunk(BlockVector3 pt) { getWorld().getChunk(FabricAdapter.toBlockPos(pt)); diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java index a4045c0cfb..5747cb4865 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorldEdit.java @@ -40,6 +40,7 @@ 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.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import net.fabricmc.api.ModInitializer; @@ -228,6 +229,12 @@ private void setupRegistries(MinecraftServer server) { ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(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())); + } + } } private void onStartingServer(MinecraftServer minecraftServer) { diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java new file mode 100644 index 0000000000..ae689c69fa --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java @@ -0,0 +1,286 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.internal; + +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.MaxChangedBlocksException; +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.fabric.internal.NBTConverter; +import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.fluid.FluidType; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.ticks.LevelTickAccess; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +public class FabricEditSessionDelegate implements WorldGenLevel { + + private final EditSession editSession; + private final ServerLevel level; + + public FabricEditSessionDelegate(EditSession editSession, ServerLevel level) { + this.editSession = editSession; + this.level = level; + } + + @Override + public long getSeed() { + return this.level.getSeed(); + } + + @Override + public ServerLevel getLevel() { + return this.level; + } + + @Override + public long nextSubTickCount() { + return this.level.nextSubTickCount(); + } + + @Override + public LevelTickAccess getBlockTicks() { + return this.level.getBlockTicks(); + } + + @Override + public LevelTickAccess getFluidTicks() { + return this.level.getFluidTicks(); + } + + @Override + public LevelData getLevelData() { + return this.level.getLevelData(); + } + + @Override + public DifficultyInstance getCurrentDifficultyAt(BlockPos blockPos) { + return this.level.getCurrentDifficultyAt(blockPos); + } + + @Nullable + @Override + public MinecraftServer getServer() { + return this.level.getServer(); + } + + @Override + public ChunkSource getChunkSource() { + return this.level.getChunkSource(); + } + + @Override + public RandomSource getRandom() { + // TODO investigate overriding this for supplying seeds + return this.level.getRandom(); + } + + @Override + public void playSound(@Nullable Player player, BlockPos blockPos, SoundEvent soundEvent, SoundSource soundSource, float f, float g) { + this.level.playSound(player, blockPos, soundEvent, soundSource, f, g); + } + + @Override + public void addParticle(ParticleOptions particleOptions, double d, double e, double f, double g, double h, double i) { + this.level.addParticle(particleOptions, d, e, f, g, h, i); + } + + @Override + public void levelEvent(@Nullable Player player, int i, BlockPos blockPos, int j) { + this.level.levelEvent(player, i, blockPos, j); + } + + @Override + public void gameEvent(GameEvent gameEvent, Vec3 vec3, GameEvent.Context context) { + this.level.gameEvent(gameEvent, vec3, context); + } + + @Override + public float getShade(Direction direction, boolean bl) { + return this.level.getShade(direction, bl); + } + + @Override + public LevelLightEngine getLightEngine() { + return this.level.getLightEngine(); + } + + @Override + public WorldBorder getWorldBorder() { + return this.level.getWorldBorder(); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos blockPos) { + BlockEntity tileEntity = this.level.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; + } + + @Override + public BlockState getBlockState(BlockPos blockPos) { + return FabricAdapter.adapt(this.editSession.getBlock(FabricAdapter.adapt(blockPos))); + } + + @Override + public FluidState getFluidState(BlockPos blockPos) { + return this.level.getFluidState(blockPos); + } + + @Override + public List getEntities(@Nullable Entity entity, AABB aabb, Predicate predicate) { + return this.level.getEntities(entity, aabb, predicate); + } + + @Override + public List getEntities(EntityTypeTest entityTypeTest, AABB aabb, Predicate predicate) { + return this.level.getEntities(entityTypeTest, aabb, predicate); + } + + @Override + public List players() { + return this.level.players(); + } + + @Nullable + @Override + public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) { + return this.level.getChunk(i, j, chunkStatus, bl); + } + + @Override + public int getHeight(Heightmap.Types types, int i, int j) { + return this.level.getHeight(types, i, j); + } + + @Override + public int getSkyDarken() { + return this.level.getSkyDarken(); + } + + @Override + public BiomeManager getBiomeManager() { + return this.level.getBiomeManager(); + } + + @Override + public Holder getUncachedNoiseBiome(int i, int j, int k) { + return this.level.getUncachedNoiseBiome(i, j, k); + } + + @Override + public boolean isClientSide() { + return false; + } + + @Override + public int getSeaLevel() { + return this.level.getSeaLevel(); + } + + @Override + public DimensionType dimensionType() { + return this.level.dimensionType(); + } + + @Override + public RegistryAccess registryAccess() { + return this.level.registryAccess(); + } + + @Override + public FeatureFlagSet enabledFeatures() { + return this.level.enabledFeatures(); + } + + @Override + public boolean isStateAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getBlockState(blockPos)); + } + + @Override + public boolean isFluidAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getFluidState(blockPos)); + } + + @Override + public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { + try { + return editSession.setBlock(FabricAdapter.adapt(blockPos), FabricAdapter.adapt(blockState)); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + @Override + public 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 boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { + return removeBlock(blockPos, bl); + } +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java index 4d6049ba3a..0912a0af2f 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -33,6 +33,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; +import com.sk89q.worldedit.forge.internal.ForgeEditSessionDelegate; import com.sk89q.worldedit.forge.internal.ForgeWorldNativeAccess; import com.sk89q.worldedit.forge.internal.NBTConverter; import com.sk89q.worldedit.forge.internal.TileEntityUtils; @@ -57,6 +58,7 @@ 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.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; @@ -448,10 +450,17 @@ public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 position = position.add(0, 1, 0); } return generator != null && generator.place( - world, chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position) + new ForgeEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position) ); } + public boolean generateFeature(ConfiguredFeatureType type, EditSession editSession, BlockVector3 position) { + ServerLevel world = getWorld(); + ConfiguredFeature k = world.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.getId())); + ServerChunkCache chunkManager = world.getChunkSource(); + return k != null && k.place(new ForgeEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position)); + } + @Override public void checkLoadedChunk(BlockVector3 pt) { getWorld().getChunk(ForgeAdapter.toBlockPos(pt)); diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java index 9d5eb57a81..ac27beb5da 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorldEdit.java @@ -42,6 +42,7 @@ 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.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemCategory; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.commands.CommandSourceStack; @@ -208,6 +209,12 @@ private void setupRegistries(MinecraftServer server) { ItemCategory.REGISTRY.register(name.toString(), new ItemCategory(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())); + } + } } @SubscribeEvent diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java new file mode 100644 index 0000000000..5a3a9405ac --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java @@ -0,0 +1,284 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.forge.internal; + +import com.sk89q.worldedit.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.core.Direction; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.flag.FeatureFlagSet; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.entity.EntityTypeTest; +import net.minecraft.world.level.gameevent.GameEvent; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.storage.LevelData; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.ticks.LevelTickAccess; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +public class ForgeEditSessionDelegate implements WorldGenLevel { + + private final EditSession editSession; + private final ServerLevel level; + + public ForgeEditSessionDelegate(EditSession editSession, ServerLevel level) { + this.editSession = editSession; + this.level = level; + } + + @Override + public long getSeed() { + return this.level.getSeed(); + } + + @Override + public ServerLevel getLevel() { + return this.level; + } + + @Override + public long nextSubTickCount() { + return this.level.nextSubTickCount(); + } + + @Override + public LevelTickAccess getBlockTicks() { + return this.level.getBlockTicks(); + } + + @Override + public LevelTickAccess getFluidTicks() { + return this.level.getFluidTicks(); + } + + @Override + public LevelData getLevelData() { + return this.level.getLevelData(); + } + + @Override + public DifficultyInstance getCurrentDifficultyAt(BlockPos blockPos) { + return this.level.getCurrentDifficultyAt(blockPos); + } + + @Nullable + @Override + public MinecraftServer getServer() { + return this.level.getServer(); + } + + @Override + public ChunkSource getChunkSource() { + return this.level.getChunkSource(); + } + + @Override + public RandomSource getRandom() { + // TODO investigate overriding this for supplying seeds + return this.level.getRandom(); + } + + @Override + public void playSound(@Nullable Player player, BlockPos blockPos, SoundEvent soundEvent, SoundSource soundSource, float f, float g) { + this.level.playSound(player, blockPos, soundEvent, soundSource, f, g); + } + + @Override + public void addParticle(ParticleOptions particleOptions, double d, double e, double f, double g, double h, double i) { + this.level.addParticle(particleOptions, d, e, f, g, h, i); + } + + @Override + public void levelEvent(@Nullable Player player, int i, BlockPos blockPos, int j) { + this.level.levelEvent(player, i, blockPos, j); + } + + @Override + public void gameEvent(GameEvent gameEvent, Vec3 vec3, GameEvent.Context context) { + this.level.gameEvent(gameEvent, vec3, context); + } + + @Override + public float getShade(Direction direction, boolean bl) { + return this.level.getShade(direction, bl); + } + + @Override + public LevelLightEngine getLightEngine() { + return this.level.getLightEngine(); + } + + @Override + public WorldBorder getWorldBorder() { + return this.level.getWorldBorder(); + } + + @Nullable + @Override + public BlockEntity getBlockEntity(BlockPos blockPos) { + BlockEntity tileEntity = this.level.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; + } + + @Override + public BlockState getBlockState(BlockPos blockPos) { + return ForgeAdapter.adapt(this.editSession.getBlock(ForgeAdapter.adapt(blockPos))); + } + + @Override + public FluidState getFluidState(BlockPos blockPos) { + return this.level.getFluidState(blockPos); + } + + @Override + public List getEntities(@Nullable Entity entity, AABB aabb, Predicate predicate) { + return this.level.getEntities(entity, aabb, predicate); + } + + @Override + public List getEntities(EntityTypeTest entityTypeTest, AABB aabb, Predicate predicate) { + return this.level.getEntities(entityTypeTest, aabb, predicate); + } + + @Override + public List players() { + return this.level.players(); + } + + @Nullable + @Override + public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) { + return this.level.getChunk(i, j, chunkStatus, bl); + } + + @Override + public int getHeight(Heightmap.Types types, int i, int j) { + return this.level.getHeight(types, i, j); + } + + @Override + public int getSkyDarken() { + return this.level.getSkyDarken(); + } + + @Override + public BiomeManager getBiomeManager() { + return this.level.getBiomeManager(); + } + + @Override + public Holder getUncachedNoiseBiome(int i, int j, int k) { + return this.level.getUncachedNoiseBiome(i, j, k); + } + + @Override + public boolean isClientSide() { + return false; + } + + @Override + public int getSeaLevel() { + return this.level.getSeaLevel(); + } + + @Override + public DimensionType dimensionType() { + return this.level.dimensionType(); + } + + @Override + public RegistryAccess registryAccess() { + return this.level.registryAccess(); + } + + @Override + public FeatureFlagSet enabledFeatures() { + return this.level.enabledFeatures(); + } + + @Override + public boolean isStateAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getBlockState(blockPos)); + } + + @Override + public boolean isFluidAtPosition(BlockPos blockPos, Predicate predicate) { + return predicate.test(this.getFluidState(blockPos)); + } + + @Override + public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { + try { + return editSession.setBlock(ForgeAdapter.adapt(blockPos), ForgeAdapter.adapt(blockState)); + } catch (MaxChangedBlocksException e) { + throw new RuntimeException(e); + } + } + + @Override + public 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 boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { + return removeBlock(blockPos, bl); + } +} From f5dc2b1f86a8fc38bef8a66a353978a110d0efe0 Mon Sep 17 00:00:00 2001 From: Madeline Miller Date: Sat, 31 Dec 2022 22:54:28 +1000 Subject: [PATCH 2/4] Add a brush version of the feature command Use Java proxy classes --- .../worldedit/command/BrushCommands.java | 20 ++ .../factory/FeatureGeneratorFactory.java | 44 +++ .../function/generator/FeatureGenerator.java | 52 ++++ .../generation/ConfiguredFeatureType.java | 5 + .../sk89q/worldedit/fabric/FabricWorld.java | 9 +- .../internal/FabricEditSessionDelegate.java | 286 ------------------ .../FabricServerLevelDelegateProxy.java | 109 +++++++ .../com/sk89q/worldedit/forge/ForgeWorld.java | 9 +- .../internal/ForgeEditSessionDelegate.java | 284 ----------------- .../ForgeServerLevelDelegateProxy.java | 109 +++++++ 10 files changed, 351 insertions(+), 576 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/FeatureGeneratorFactory.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FeatureGenerator.java delete mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java create mode 100644 worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java delete mode 100644 worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java create mode 100644 worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java index 0446a11d11..38bbd93eb9 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/BrushCommands.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.command.argument.HeightConverter; +import com.sk89q.worldedit.command.factory.FeatureGeneratorFactory; import com.sk89q.worldedit.command.factory.ReplaceFactory; import com.sk89q.worldedit.command.factory.TreeGeneratorFactory; import com.sk89q.worldedit.command.tool.BrushTool; @@ -78,6 +79,7 @@ import com.sk89q.worldedit.util.formatting.text.format.TextColor; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BlockTypes; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import org.enginehub.piston.annotation.Command; import org.enginehub.piston.annotation.CommandContainer; import org.enginehub.piston.annotation.param.Arg; @@ -546,6 +548,24 @@ public void forest(Player player, LocalSession localSession, new Paint(new TreeGeneratorFactory(type), density / 100), shape, "worldedit.brush.forest"); } + @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"); + } + @Command( name = "raise", desc = "Raise brush, raise all blocks by one" diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/FeatureGeneratorFactory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/FeatureGeneratorFactory.java new file mode 100644 index 0000000000..6434f016bb --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/factory/FeatureGeneratorFactory.java @@ -0,0 +1,44 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.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 { + 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; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FeatureGenerator.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FeatureGenerator.java new file mode 100644 index 0000000000..6c49138db9 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/generator/FeatureGenerator.java @@ -0,0 +1,52 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.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); + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java index 67ddea478e..56888acd98 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/generation/ConfiguredFeatureType.java @@ -35,4 +35,9 @@ public ConfiguredFeatureType(String id) { public String getId() { return this.id; } + + @Override + public String toString() { + return this.id; + } } diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java index ee17530411..d42c1642cb 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/FabricWorld.java @@ -34,7 +34,7 @@ import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.fabric.internal.ExtendedMinecraftServer; -import com.sk89q.worldedit.fabric.internal.FabricEditSessionDelegate; +import com.sk89q.worldedit.fabric.internal.FabricServerLevelDelegateProxy; import com.sk89q.worldedit.fabric.internal.FabricWorldNativeAccess; import com.sk89q.worldedit.fabric.internal.NBTConverter; import com.sk89q.worldedit.fabric.mixin.AccessorDerivedLevelData; @@ -86,6 +86,7 @@ import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.entity.BlockEntity; @@ -464,8 +465,9 @@ public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 if (type == TreeType.CHORUS_PLANT) { position = position.add(0, 1, 0); } + WorldGenLevel proxyLevel = FabricServerLevelDelegateProxy.newInstance(editSession, world); return generator != null && generator.place( - new FabricEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, + proxyLevel, chunkManager.getGenerator(), random, FabricAdapter.toBlockPos(position) ); } @@ -474,7 +476,8 @@ public boolean generateFeature(ConfiguredFeatureType type, EditSession editSessi ServerLevel world = (ServerLevel) getWorld(); ConfiguredFeature k = world.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.getId())); ServerChunkCache chunkManager = world.getChunkSource(); - return k != null && k.place(new FabricEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, FabricAdapter.toBlockPos(position)); + WorldGenLevel proxyLevel = FabricServerLevelDelegateProxy.newInstance(editSession, world); + return k != null && k.place(proxyLevel, chunkManager.getGenerator(), random, FabricAdapter.toBlockPos(position)); } @Override diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java deleted file mode 100644 index ae689c69fa..0000000000 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricEditSessionDelegate.java +++ /dev/null @@ -1,286 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.fabric.internal; - -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.fabric.FabricAdapter; -import com.sk89q.worldedit.fabric.internal.NBTConverter; -import com.sk89q.worldedit.world.block.BlockTypes; -import com.sk89q.worldedit.world.fluid.FluidType; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.border.WorldBorder; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkSource; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.entity.EntityTypeTest; -import net.minecraft.world.level.gameevent.GameEvent; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.lighting.LevelLightEngine; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.storage.LevelData; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.ticks.LevelTickAccess; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Predicate; - -public class FabricEditSessionDelegate implements WorldGenLevel { - - private final EditSession editSession; - private final ServerLevel level; - - public FabricEditSessionDelegate(EditSession editSession, ServerLevel level) { - this.editSession = editSession; - this.level = level; - } - - @Override - public long getSeed() { - return this.level.getSeed(); - } - - @Override - public ServerLevel getLevel() { - return this.level; - } - - @Override - public long nextSubTickCount() { - return this.level.nextSubTickCount(); - } - - @Override - public LevelTickAccess getBlockTicks() { - return this.level.getBlockTicks(); - } - - @Override - public LevelTickAccess getFluidTicks() { - return this.level.getFluidTicks(); - } - - @Override - public LevelData getLevelData() { - return this.level.getLevelData(); - } - - @Override - public DifficultyInstance getCurrentDifficultyAt(BlockPos blockPos) { - return this.level.getCurrentDifficultyAt(blockPos); - } - - @Nullable - @Override - public MinecraftServer getServer() { - return this.level.getServer(); - } - - @Override - public ChunkSource getChunkSource() { - return this.level.getChunkSource(); - } - - @Override - public RandomSource getRandom() { - // TODO investigate overriding this for supplying seeds - return this.level.getRandom(); - } - - @Override - public void playSound(@Nullable Player player, BlockPos blockPos, SoundEvent soundEvent, SoundSource soundSource, float f, float g) { - this.level.playSound(player, blockPos, soundEvent, soundSource, f, g); - } - - @Override - public void addParticle(ParticleOptions particleOptions, double d, double e, double f, double g, double h, double i) { - this.level.addParticle(particleOptions, d, e, f, g, h, i); - } - - @Override - public void levelEvent(@Nullable Player player, int i, BlockPos blockPos, int j) { - this.level.levelEvent(player, i, blockPos, j); - } - - @Override - public void gameEvent(GameEvent gameEvent, Vec3 vec3, GameEvent.Context context) { - this.level.gameEvent(gameEvent, vec3, context); - } - - @Override - public float getShade(Direction direction, boolean bl) { - return this.level.getShade(direction, bl); - } - - @Override - public LevelLightEngine getLightEngine() { - return this.level.getLightEngine(); - } - - @Override - public WorldBorder getWorldBorder() { - return this.level.getWorldBorder(); - } - - @Nullable - @Override - public BlockEntity getBlockEntity(BlockPos blockPos) { - BlockEntity tileEntity = this.level.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; - } - - @Override - public BlockState getBlockState(BlockPos blockPos) { - return FabricAdapter.adapt(this.editSession.getBlock(FabricAdapter.adapt(blockPos))); - } - - @Override - public FluidState getFluidState(BlockPos blockPos) { - return this.level.getFluidState(blockPos); - } - - @Override - public List getEntities(@Nullable Entity entity, AABB aabb, Predicate predicate) { - return this.level.getEntities(entity, aabb, predicate); - } - - @Override - public List getEntities(EntityTypeTest entityTypeTest, AABB aabb, Predicate predicate) { - return this.level.getEntities(entityTypeTest, aabb, predicate); - } - - @Override - public List players() { - return this.level.players(); - } - - @Nullable - @Override - public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) { - return this.level.getChunk(i, j, chunkStatus, bl); - } - - @Override - public int getHeight(Heightmap.Types types, int i, int j) { - return this.level.getHeight(types, i, j); - } - - @Override - public int getSkyDarken() { - return this.level.getSkyDarken(); - } - - @Override - public BiomeManager getBiomeManager() { - return this.level.getBiomeManager(); - } - - @Override - public Holder getUncachedNoiseBiome(int i, int j, int k) { - return this.level.getUncachedNoiseBiome(i, j, k); - } - - @Override - public boolean isClientSide() { - return false; - } - - @Override - public int getSeaLevel() { - return this.level.getSeaLevel(); - } - - @Override - public DimensionType dimensionType() { - return this.level.dimensionType(); - } - - @Override - public RegistryAccess registryAccess() { - return this.level.registryAccess(); - } - - @Override - public FeatureFlagSet enabledFeatures() { - return this.level.enabledFeatures(); - } - - @Override - public boolean isStateAtPosition(BlockPos blockPos, Predicate predicate) { - return predicate.test(this.getBlockState(blockPos)); - } - - @Override - public boolean isFluidAtPosition(BlockPos blockPos, Predicate predicate) { - return predicate.test(this.getFluidState(blockPos)); - } - - @Override - public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { - try { - return editSession.setBlock(FabricAdapter.adapt(blockPos), FabricAdapter.adapt(blockState)); - } catch (MaxChangedBlocksException e) { - throw new RuntimeException(e); - } - } - - @Override - public 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 boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { - return removeBlock(blockPos, bl); - } -} diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java new file mode 100644 index 0000000000..eb4b3c3d16 --- /dev/null +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java @@ -0,0 +1,109 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.fabric.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.entity.Entity; +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 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); + } + } + + private boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { + return removeBlock(blockPos, bl); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("getBlockState") && args.length == 1 && args[0] instanceof BlockPos blockPos) { + return getBlockState(blockPos); + } else if (method.getName().equals("getBlockEntity") && args.length == 1 && args[0] instanceof BlockPos blockPos) { + return getBlockEntity(blockPos); + } else if (method.getName().equals("setBlock") && args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) { + return setBlock(blockPos, blockState); + } else if (method.getName().equals("removeBlock") && args.length == 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) { + return removeBlock(blockPos, bl); + } else if (method.getName().equals("destroyBlock") && args.length == 4 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl && args[2] instanceof Entity entity && args[3] instanceof Integer i) { + return destroyBlock(blockPos, bl, entity, i); + } else { + return method.invoke(this.serverLevel, args); + } + } + +} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java index 0912a0af2f..c9584405aa 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/ForgeWorld.java @@ -33,7 +33,7 @@ import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.extent.Extent; -import com.sk89q.worldedit.forge.internal.ForgeEditSessionDelegate; +import com.sk89q.worldedit.forge.internal.ForgeServerLevelDelegateProxy; import com.sk89q.worldedit.forge.internal.ForgeWorldNativeAccess; import com.sk89q.worldedit.forge.internal.NBTConverter; import com.sk89q.worldedit.forge.internal.TileEntityUtils; @@ -83,6 +83,7 @@ import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; +import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.entity.BlockEntity; @@ -449,8 +450,9 @@ public boolean generateTree(TreeType type, EditSession editSession, BlockVector3 if (type == TreeType.CHORUS_PLANT) { position = position.add(0, 1, 0); } + WorldGenLevel levelProxy = ForgeServerLevelDelegateProxy.newInstance(editSession, world); return generator != null && generator.place( - new ForgeEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position) + levelProxy, chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position) ); } @@ -458,7 +460,8 @@ public boolean generateFeature(ConfiguredFeatureType type, EditSession editSessi ServerLevel world = getWorld(); ConfiguredFeature k = world.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE).get(ResourceLocation.tryParse(type.getId())); ServerChunkCache chunkManager = world.getChunkSource(); - return k != null && k.place(new ForgeEditSessionDelegate(editSession, world), chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position)); + WorldGenLevel levelProxy = ForgeServerLevelDelegateProxy.newInstance(editSession, world); + return k != null && k.place(levelProxy, chunkManager.getGenerator(), random, ForgeAdapter.toBlockPos(position)); } @Override diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java deleted file mode 100644 index 5a3a9405ac..0000000000 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeEditSessionDelegate.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.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.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.RandomSource; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.border.WorldBorder; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkSource; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.entity.EntityTypeTest; -import net.minecraft.world.level.gameevent.GameEvent; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.lighting.LevelLightEngine; -import net.minecraft.world.level.material.Fluid; -import net.minecraft.world.level.material.FluidState; -import net.minecraft.world.level.storage.LevelData; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.ticks.LevelTickAccess; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.function.Predicate; - -public class ForgeEditSessionDelegate implements WorldGenLevel { - - private final EditSession editSession; - private final ServerLevel level; - - public ForgeEditSessionDelegate(EditSession editSession, ServerLevel level) { - this.editSession = editSession; - this.level = level; - } - - @Override - public long getSeed() { - return this.level.getSeed(); - } - - @Override - public ServerLevel getLevel() { - return this.level; - } - - @Override - public long nextSubTickCount() { - return this.level.nextSubTickCount(); - } - - @Override - public LevelTickAccess getBlockTicks() { - return this.level.getBlockTicks(); - } - - @Override - public LevelTickAccess getFluidTicks() { - return this.level.getFluidTicks(); - } - - @Override - public LevelData getLevelData() { - return this.level.getLevelData(); - } - - @Override - public DifficultyInstance getCurrentDifficultyAt(BlockPos blockPos) { - return this.level.getCurrentDifficultyAt(blockPos); - } - - @Nullable - @Override - public MinecraftServer getServer() { - return this.level.getServer(); - } - - @Override - public ChunkSource getChunkSource() { - return this.level.getChunkSource(); - } - - @Override - public RandomSource getRandom() { - // TODO investigate overriding this for supplying seeds - return this.level.getRandom(); - } - - @Override - public void playSound(@Nullable Player player, BlockPos blockPos, SoundEvent soundEvent, SoundSource soundSource, float f, float g) { - this.level.playSound(player, blockPos, soundEvent, soundSource, f, g); - } - - @Override - public void addParticle(ParticleOptions particleOptions, double d, double e, double f, double g, double h, double i) { - this.level.addParticle(particleOptions, d, e, f, g, h, i); - } - - @Override - public void levelEvent(@Nullable Player player, int i, BlockPos blockPos, int j) { - this.level.levelEvent(player, i, blockPos, j); - } - - @Override - public void gameEvent(GameEvent gameEvent, Vec3 vec3, GameEvent.Context context) { - this.level.gameEvent(gameEvent, vec3, context); - } - - @Override - public float getShade(Direction direction, boolean bl) { - return this.level.getShade(direction, bl); - } - - @Override - public LevelLightEngine getLightEngine() { - return this.level.getLightEngine(); - } - - @Override - public WorldBorder getWorldBorder() { - return this.level.getWorldBorder(); - } - - @Nullable - @Override - public BlockEntity getBlockEntity(BlockPos blockPos) { - BlockEntity tileEntity = this.level.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; - } - - @Override - public BlockState getBlockState(BlockPos blockPos) { - return ForgeAdapter.adapt(this.editSession.getBlock(ForgeAdapter.adapt(blockPos))); - } - - @Override - public FluidState getFluidState(BlockPos blockPos) { - return this.level.getFluidState(blockPos); - } - - @Override - public List getEntities(@Nullable Entity entity, AABB aabb, Predicate predicate) { - return this.level.getEntities(entity, aabb, predicate); - } - - @Override - public List getEntities(EntityTypeTest entityTypeTest, AABB aabb, Predicate predicate) { - return this.level.getEntities(entityTypeTest, aabb, predicate); - } - - @Override - public List players() { - return this.level.players(); - } - - @Nullable - @Override - public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) { - return this.level.getChunk(i, j, chunkStatus, bl); - } - - @Override - public int getHeight(Heightmap.Types types, int i, int j) { - return this.level.getHeight(types, i, j); - } - - @Override - public int getSkyDarken() { - return this.level.getSkyDarken(); - } - - @Override - public BiomeManager getBiomeManager() { - return this.level.getBiomeManager(); - } - - @Override - public Holder getUncachedNoiseBiome(int i, int j, int k) { - return this.level.getUncachedNoiseBiome(i, j, k); - } - - @Override - public boolean isClientSide() { - return false; - } - - @Override - public int getSeaLevel() { - return this.level.getSeaLevel(); - } - - @Override - public DimensionType dimensionType() { - return this.level.dimensionType(); - } - - @Override - public RegistryAccess registryAccess() { - return this.level.registryAccess(); - } - - @Override - public FeatureFlagSet enabledFeatures() { - return this.level.enabledFeatures(); - } - - @Override - public boolean isStateAtPosition(BlockPos blockPos, Predicate predicate) { - return predicate.test(this.getBlockState(blockPos)); - } - - @Override - public boolean isFluidAtPosition(BlockPos blockPos, Predicate predicate) { - return predicate.test(this.getFluidState(blockPos)); - } - - @Override - public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { - try { - return editSession.setBlock(ForgeAdapter.adapt(blockPos), ForgeAdapter.adapt(blockState)); - } catch (MaxChangedBlocksException e) { - throw new RuntimeException(e); - } - } - - @Override - public 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 boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { - return removeBlock(blockPos, bl); - } -} diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java new file mode 100644 index 0000000000..17e27281bc --- /dev/null +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java @@ -0,0 +1,109 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.forge.internal; + +import com.sk89q.worldedit.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.entity.Entity; +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 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); + } + } + + private boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { + return removeBlock(blockPos, bl); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("getBlockState") && args.length == 1 && args[0] instanceof BlockPos blockPos) { + return getBlockState(blockPos); + } else if (method.getName().equals("getBlockEntity") && args.length == 1 && args[0] instanceof BlockPos blockPos) { + return getBlockEntity(blockPos); + } else if (method.getName().equals("setBlock") && args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) { + return setBlock(blockPos, blockState); + } else if (method.getName().equals("removeBlock") && args.length == 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) { + return removeBlock(blockPos, bl); + } else if (method.getName().equals("destroyBlock") && args.length == 4 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl && args[2] instanceof Entity entity && args[3] instanceof Integer i) { + return destroyBlock(blockPos, bl, entity, i); + } else { + return method.invoke(this.serverLevel, args); + } + } + +} From 3a0a588823fc8eaa453fafcb474f4982ddb19d3b Mon Sep 17 00:00:00 2001 From: Madeline Miller Date: Sun, 1 Jan 2023 00:18:59 +1000 Subject: [PATCH 3/4] 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 --- .../impl/v1_19_R2/PaperweightAdapter.java | 48 +++++-- .../PaperweightServerLevelDelegateProxy.java | 120 ++++++++++++++++++ .../sk89q/worldedit/bukkit/BukkitWorld.java | 11 ++ .../bukkit/adapter/BukkitImplAdapter.java | 16 +++ .../FabricServerLevelDelegateProxy.java | 42 +++--- .../ForgeServerLevelDelegateProxy.java | 42 +++--- 6 files changed, 236 insertions(+), 43 deletions(-) create mode 100644 worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightServerLevelDelegateProxy.java diff --git a/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java index 009ab2da02..9bfcaa3463 100644 --- a/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java @@ -27,6 +27,7 @@ import com.google.common.util.concurrent.Futures; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Lifecycle; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; @@ -63,6 +64,7 @@ import com.sk89q.worldedit.world.block.BlockType; import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.entity.EntityTypes; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.Util; import net.minecraft.core.BlockPos; @@ -92,6 +94,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.util.RandomSource; import net.minecraft.util.StringRepresentable; import net.minecraft.util.thread.BlockableEventLoop; import net.minecraft.world.Clearable; @@ -104,6 +107,7 @@ import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.LevelSettings; +import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -116,6 +120,7 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.dimension.LevelStem; 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.PrimaryLevelData; import net.minecraft.world.phys.BlockHitResult; @@ -188,6 +193,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter { private final Field chunkProviderExecutorField; private final Watchdog watchdog; + private static final RandomSource random = RandomSource.create(); + // ------------------------------------------------------------------------ // Code that may break between versions of Minecraft // ------------------------------------------------------------------------ @@ -320,6 +327,21 @@ public OptionalInt getInternalBlockStateId(BlockState state) { 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 public BlockState getBlock(Location location) { checkNotNull(location); @@ -333,14 +355,7 @@ public BlockState getBlock(Location location) { LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); - int internalId = Block.getId(blockData); - BlockState state = BlockStateIdAccess.getBlockStateById(internalId); - if (state == null) { - org.bukkit.block.Block bukkitBlock = location.getBlock(); - state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); - } - - return state; + return adapt(blockData); } @Override @@ -873,9 +888,24 @@ public void initializeRegistries() { 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())); + } + } } - // ------------------------------------------------------------------------ + 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 // ------------------------------------------------------------------------ diff --git a/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightServerLevelDelegateProxy.java b/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightServerLevelDelegateProxy.java new file mode 100644 index 0000000000..ee899df47d --- /dev/null +++ b/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightServerLevelDelegateProxy.java @@ -0,0 +1,120 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.bukkit.adapter.impl.v1_19_R2; + +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.fromNative(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); + } + +} diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index 4e8592ece3..df9db2e58b 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -44,6 +44,7 @@ import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockStateHolder; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.weather.WeatherType; import com.sk89q.worldedit.world.weather.WeatherTypes; import io.papermc.lib.PaperLib; @@ -435,6 +436,16 @@ public boolean canPlaceAt(BlockVector3 position, com.sk89q.worldedit.world.block 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; @Override diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index 3d08909250..afbbc7f543 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -19,6 +19,7 @@ package com.sk89q.worldedit.bukkit.adapter; +import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.entity.BaseEntity; @@ -36,6 +37,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.generation.ConfiguredFeatureType; import com.sk89q.worldedit.world.item.ItemType; import org.bukkit.Location; import org.bukkit.World; @@ -254,6 +256,7 @@ default boolean clearContainerBlockContents(World world, BlockVector3 pt) { } /** +<<<<<<< HEAD * Checks if this adapter supports custom biomes. * @return if custom biomes are supported */ @@ -279,6 +282,19 @@ default BiomeType getBiome(Location location) { throw new UnsupportedOperationException("This adapter does not support custom biomes."); } + /** + * 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."); + } + /** * Initialize registries that require NMS access. */ diff --git a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java index eb4b3c3d16..07ee8d81c5 100644 --- a/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java +++ b/worldedit-fabric/src/main/java/com/sk89q/worldedit/fabric/internal/FabricServerLevelDelegateProxy.java @@ -25,7 +25,6 @@ import com.sk89q.worldedit.world.block.BlockTypes; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -34,6 +33,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Arrays; public class FabricServerLevelDelegateProxy implements InvocationHandler { @@ -85,25 +85,33 @@ private boolean removeBlock(BlockPos blockPos, boolean bl) { } } - private boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { - return removeBlock(blockPos, bl); - } - @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getName().equals("getBlockState") && args.length == 1 && args[0] instanceof BlockPos blockPos) { - return getBlockState(blockPos); - } else if (method.getName().equals("getBlockEntity") && args.length == 1 && args[0] instanceof BlockPos blockPos) { - return getBlockEntity(blockPos); - } else if (method.getName().equals("setBlock") && args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) { - return setBlock(blockPos, blockState); - } else if (method.getName().equals("removeBlock") && args.length == 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) { - return removeBlock(blockPos, bl); - } else if (method.getName().equals("destroyBlock") && args.length == 4 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl && args[2] instanceof Entity entity && args[3] instanceof Integer i) { - return destroyBlock(blockPos, bl, entity, i); - } else { - return method.invoke(this.serverLevel, args); + 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); } } diff --git a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java index 17e27281bc..ff9e64d4e8 100644 --- a/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java +++ b/worldedit-forge/src/main/java/com/sk89q/worldedit/forge/internal/ForgeServerLevelDelegateProxy.java @@ -25,7 +25,6 @@ import com.sk89q.worldedit.world.block.BlockTypes; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -34,6 +33,7 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Arrays; public class ForgeServerLevelDelegateProxy implements InvocationHandler { @@ -85,25 +85,33 @@ private boolean removeBlock(BlockPos blockPos, boolean bl) { } } - private boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { - return removeBlock(blockPos, bl); - } - @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - if (method.getName().equals("getBlockState") && args.length == 1 && args[0] instanceof BlockPos blockPos) { - return getBlockState(blockPos); - } else if (method.getName().equals("getBlockEntity") && args.length == 1 && args[0] instanceof BlockPos blockPos) { - return getBlockEntity(blockPos); - } else if (method.getName().equals("setBlock") && args.length >= 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof BlockState blockState) { - return setBlock(blockPos, blockState); - } else if (method.getName().equals("removeBlock") && args.length == 2 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl) { - return removeBlock(blockPos, bl); - } else if (method.getName().equals("destroyBlock") && args.length == 4 && args[0] instanceof BlockPos blockPos && args[1] instanceof Boolean bl && args[2] instanceof Entity entity && args[3] instanceof Integer i) { - return destroyBlock(blockPos, bl, entity, i); - } else { - return method.invoke(this.serverLevel, args); + 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); } } From b41d035c3cb41f3f6389f350cd6596ab38b32767 Mon Sep 17 00:00:00 2001 From: Madeline Miller Date: Fri, 10 Mar 2023 21:18:06 +1000 Subject: [PATCH 4/4] checkstyle --- .../bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java index 9bfcaa3463..077949fce6 100644 --- a/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1.19.3/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/v1_19_R2/PaperweightAdapter.java @@ -905,7 +905,7 @@ public boolean generateFeature(ConfiguredFeatureType type, World world, EditSess return k != null && k.place(proxyLevel, chunkManager.getGenerator(), random, new BlockPos(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ())); } - // ------------------------------------------------------------------------ + // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------