9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-25 18:09:27 +00:00

fix(block): 修复一些奇妙bug

This commit is contained in:
jhqwqmc
2025-06-24 10:03:22 +08:00
parent bf41c572bf
commit ba6ff17f97
4 changed files with 65 additions and 69 deletions

View File

@@ -5,15 +5,24 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
import net.momirealms.craftengine.core.block.BlockBehavior;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.WorldPosition;
import java.util.List;
import java.util.Map;
@@ -43,9 +52,27 @@ public class PickaxeBlockBehavior extends FacingTriggerableBlockBehavior {
public void tick(Object state, Object level, Object pos) {
ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(state));
if (blockState == null || blockState.isEmpty()) return;
Object blockPos = FastNMS.INSTANCE.method$BlockPos$relative(pos, DirectionUtils.toNMSDirection(blockState.get(this.facingProperty)));
if (blockCheckByBlockState(FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos))) {
FastNMS.INSTANCE.method$LevelWriter$destroyBlock(level, blockPos, true, null, 512);
Object breakPos = FastNMS.INSTANCE.method$BlockPos$relative(pos, DirectionUtils.toNMSDirection(blockState.get(this.facingProperty)));
Object breakState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, breakPos);
if (blockCheckByBlockState(breakState)) {
FastNMS.INSTANCE.method$LevelWriter$destroyBlock(level, breakPos, true, null, 512);
tryHandleDoubleBlock(level, breakState, breakPos);
}
}
private static void tryHandleDoubleBlock(Object level, Object state, Object blockPos) {
int stateId = BlockStateUtils.blockStateToId(state);
ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (blockState == null || blockState.isEmpty()) return;
for (Property<?> property : blockState.getProperties()) {
if (property.valueClass() != DoubleBlockHalf.class) continue;
World world = BukkitWorldManager.instance().wrap(level);
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(LocationUtils.fromBlockPos(blockPos)));
ContextHolder.Builder builder = ContextHolder.builder().withParameter(DirectContextParameters.POSITION, position);
blockState.getDrops(builder, world, null).forEach(item -> world.dropItemNaturally(position, item));
world.playBlockSound(position, blockState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
return;
}
}

View File

@@ -139,7 +139,7 @@ public class BukkitCraftEngine extends CraftEngine {
logger.info("Patching the server...");
LevelInjector.patch();
} catch (Exception e) {
throw new InjectionException("Error injecting level", e);
throw new InjectionException("Error injecting Level", e);
}
super.onPluginLoad();
super.blockManager.init();

View File

@@ -15,16 +15,17 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.properties.DoubleBlockHalf;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.*;
import org.bukkit.Bukkit;
import org.bukkit.block.Block;
import org.jetbrains.annotations.NotNull;
import java.lang.invoke.MethodHandle;
@@ -33,37 +34,14 @@ import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@SuppressWarnings("all")
public class LevelInjector {
public static void patch() {
Class<?> holderClass = new ByteBuddy()
.subclass(Object.class)
.name("net.momirealms.craftengine.bukkit.plugin.agent.LevelInjectorHolder")
.defineField("levelInjector", Object.class, Modifier.PUBLIC | Modifier.STATIC)
.defineMethod("setLevelInjector", void.class, Modifier.PUBLIC | Modifier.STATIC)
.withParameters(Object.class)
.intercept(new Implementation() {
@Override
public @NotNull InstrumentedType prepare(@NotNull InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public @NotNull ByteCodeAppender appender(@NotNull Target implementationTarget) {
return (methodVisitor, implementationContext, instrumentedMethod) -> {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC,
"net/momirealms/craftengine/bukkit/plugin/agent/LevelInjectorHolder",
"levelInjector",
"Ljava/lang/Object;");
methodVisitor.visitInsn(Opcodes.RETURN);
return new ByteCodeAppender.Size(1, 1);
};
}
})
.defineField("destroyBlockEventMethod", MethodHandle.class, Modifier.PUBLIC | Modifier.STATIC)
.defineMethod("setDestroyBlockEventMethod", void.class, Modifier.PUBLIC | Modifier.STATIC)
.defineField("callEventMethod", MethodHandle.class, Modifier.PUBLIC | Modifier.STATIC)
.defineMethod("setCallEventMethod", void.class, Modifier.PUBLIC | Modifier.STATIC)
.withParameters(MethodHandle.class)
.intercept(new Implementation() {
@Override
@@ -77,7 +55,7 @@ public class LevelInjector {
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitFieldInsn(Opcodes.PUTSTATIC,
"net/momirealms/craftengine/bukkit/plugin/agent/LevelInjectorHolder",
"destroyBlockEventMethod",
"callEventMethod",
"Ljava/lang/invoke/MethodHandle;");
methodVisitor.visitInsn(Opcodes.RETURN);
return new ByteCodeAppender.Size(1, 1);
@@ -88,64 +66,62 @@ public class LevelInjector {
.load(Bukkit.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded();
try {
Method setLevelInjector = holderClass.getMethod("setLevelInjector", Object.class);
setLevelInjector.invoke(null, new LevelInjector());
Method setDestroyBlockEventMethod = holderClass.getMethod("setDestroyBlockEventMethod", MethodHandle.class);
Method destroyBlockEvent = LevelInjector.class.getMethod("destroyBlockEvent", Object.class, Object[].class);
MethodHandle destroyBlockEventMethod = MethodHandles.lookup().unreflect(destroyBlockEvent)
.asType(MethodType.methodType(void.class, Object.class, Object.class, Object[].class));
setDestroyBlockEventMethod.invoke(null, destroyBlockEventMethod);
Method setCallEventMethod = holderClass.getMethod("setCallEventMethod", MethodHandle.class);
Method callEvent = LevelInjector.class.getMethod("callEvent", Object.class, Object[].class);
MethodHandle callEventMethod = MethodHandles.lookup().unreflect(callEvent)
.asType(MethodType.methodType(void.class, Object.class, Object[].class));
setCallEventMethod.invoke(null, callEventMethod);
} catch (Exception e) {
throw new RuntimeException(e);
}
agentmain();
}
public static void agentmain() {
new AgentBuilder.Default()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
.type(ElementMatchers.named("net.minecraft.world.level.Level").or(ElementMatchers.named("net.minecraft.world.level.World")))
.type(ElementMatchers.is(CoreReflections.clazz$Level))
.transform((builder, typeDescription, classLoader, module, protectionDomain) ->
builder.visit(Advice.to(DestroyBlockAdvice.class)
builder.visit(Advice.to(CallEventAdvice.class)
.on(ElementMatchers.is(CoreReflections.method$Level$destroyBlock))))
.installOn(ByteBuddyAgent.install());
}
public static class DestroyBlockAdvice {
public static class CallEventAdvice {
@Advice.OnMethodEnter
public static void onEnter(@Advice.This Object level, @Advice.AllArguments Object[] args) {
try {
Class<?> holder = Class.forName("net.momirealms.craftengine.bukkit.plugin.agent.LevelInjectorHolder");
Object instance = holder.getField("levelInjector").get(null);
MethodHandle destroyBlockEventMethod = (MethodHandle) holder.getField("destroyBlockEventMethod").get(null);
destroyBlockEventMethod.invokeExact(instance, level, args);
MethodHandle destroyBlockEventMethod = (MethodHandle) holder.getField("callEventMethod").get(null);
destroyBlockEventMethod.invokeExact(level, args);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
public void destroyBlockEvent(Object level, Object[] args) {
Object blockPos = args[0];
public static void callEvent(Object level, Object[] args) {
Object pos = args[0];
boolean dropBlock = (boolean) args[1];
Object entity = args[2];
if (!dropBlock || entity != null) return;
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, blockPos);
if (!dropBlock) return;
Object state = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, pos);
int stateId = BlockStateUtils.blockStateToId(state);
ImmutableBlockState blockState = BukkitBlockManager.instance().getImmutableBlockState(stateId);
if (blockState == null || blockState.isEmpty()) return;
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level));
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
BlockPos blockPos = LocationUtils.fromBlockPos(pos);
for (Property<?> property : blockState.getProperties()) {
if (property.valueClass() == DoubleBlockHalf.class) return; // 退退退不处理了气死我了
}
org.bukkit.World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
World world = BukkitWorldManager.instance().wrap(bukkitWorld);
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(blockPos));
Block block = bukkitWorld.getBlockAt(blockPos.x(), blockPos.y(), blockPos.z());
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position);
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.BLOCK, new BukkitBlockInWorld(block));
for (Item<Object> item : blockState.getDrops(builder, world, null)) {
world.dropItemNaturally(position, item);
}
world.playBlockSound(position, blockState.sounds().breakSound());
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, stateId);
FastNMS.INSTANCE.method$Level$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, pos, stateId);
}
}

View File

@@ -99,11 +99,4 @@ public class LocationUtils {
centerLoc.setY(location.getBlockY());
return centerLoc;
}
public static Vec3d atCenterOf(Vec3d vec) {
return atLowerCornerWithOffset(vec, 0.5F, 0.5F, 0.5F);
}
public static Vec3d atLowerCornerWithOffset(Vec3d vec, double deltaX, double deltaY, double deltaZ) {
return new Vec3d(vec.x() + deltaX, vec.y() + deltaY, vec.z() + deltaZ);
}
}