Skip to content

Commit

Permalink
Add for Bukkit (only 1.19.3 for now)
Browse files Browse the repository at this point in the history
Clean up the proxies to use a switch

Checkstyle is grumpy

Add the obfuscated versions

Remove debug text

Fix missed "destroyBlock" deobfuscated proxy function
  • Loading branch information
me4502 committed Mar 10, 2023
1 parent f5dc2b1 commit 3a0a588
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 43 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
// ------------------------------------------------------------------------
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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
// ------------------------------------------------------------------------

Expand Down
@@ -0,0 +1,120 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package com.sk89q.worldedit.bukkit.adapter.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);
}

}
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
*/
Expand All @@ -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.
*/
Expand Down
Expand Up @@ -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;
Expand All @@ -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 {

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

}

0 comments on commit 3a0a588

Please sign in to comment.