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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user