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

大宝贝

This commit is contained in:
XiaoMoMi
2025-06-19 03:50:14 +08:00
parent 15104ef7b9
commit 4e1de1c9af
7 changed files with 125 additions and 229 deletions

View File

@@ -1,12 +1,21 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MItems;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
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.util.VersionHelper;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.concurrent.Callable;
public class WaterLoggedBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
@@ -18,64 +27,52 @@ public class WaterLoggedBlockBehavior extends BukkitBlockBehavior {
this.waterloggedProperty = waterloggedProperty;
}
// TODO create real waterlogged blocks, needs to have real waterlogged property
// @Override
// public Object pickupBlock(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// if (this.waterloggedProperty == null) return Reflections.instance$ItemStack$EMPTY;
// Object blockState;
// Object world;
// Object pos;
// if (VersionHelper.isVersionNewerThan1_20_2()) {
// world = args[1];
// pos = args[2];
// blockState = args[3];
// } else {
// world = args[0];
// pos = args[1];
// blockState = args[2];
// }
// ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
// if (immutableBlockState != null) {
// if (immutableBlockState.get(this.waterloggedProperty)) {
// Reflections.method$LevelWriter$setBlock.invoke(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().handle(), 3);
// // TODO check can survive
// Object itemStack = Reflections.constructor$ItemStack.newInstance(Reflections.instance$Items$WATER_BUCKET);
// return itemStack;
// }
// }
// return Reflections.instance$ItemStack$EMPTY;
// }
//
// @Override
// public boolean placeLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// if (this.waterloggedProperty == null) return false;
// Object blockState = args[2];
// ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
// if (immutableBlockState != null) {
// Object fluidType = Reflections.method$FluidState$getType.invoke(args[3]);
// if (!immutableBlockState.get(this.waterloggedProperty) && fluidType == Reflections.instance$Fluids$WATER) {
// Reflections.method$LevelWriter$setBlock.invoke(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().handle(), 3);
// Reflections.method$LevelAccessor$scheduleTick.invoke(args[0], fluidType, Reflections.method$Fluid$getTickDelay.invoke(fluidType, args[0]));
// return true;
// }
// }
// return false;
// }
//
// // use water
// @Override
// public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// return super.canPlaceLiquid(thisBlock, args, superMethod);
// }
//
// @Override
// public Object getFluidState(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
// Object blockState = args[0];
// ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
// if (state == null || state.isEmpty() || this.waterloggedProperty == null) return super.getFluidState(thisBlock, args, superMethod);
// boolean waterlogged = state.get(this.waterloggedProperty);
// return waterlogged ? Reflections.method$FlowingFluid$getSource.invoke(Reflections.instance$Fluids$WATER, false) : super.getFluidState(thisBlock, args, superMethod);
// }
@Override
public Object pickupBlock(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.waterloggedProperty == null) return CoreReflections.instance$ItemStack$EMPTY;
Object blockState;
Object world;
Object pos;
if (VersionHelper.isOrAbove1_20_2()) {
world = args[1];
pos = args[2];
blockState = args[3];
} else {
world = args[0];
pos = args[1];
blockState = args[2];
}
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState != null) {
if (immutableBlockState.get(this.waterloggedProperty)) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, pos, immutableBlockState.with(this.waterloggedProperty, false).customBlockState().handle(), 3);
return FastNMS.INSTANCE.constructor$ItemStack(MItems.WATER_BUCKET, 1);
}
}
return CoreReflections.instance$ItemStack$EMPTY;
}
@Override
public boolean placeLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.waterloggedProperty == null) return false;
Object blockState = args[2];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (immutableBlockState != null) {
Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(args[3]);
if (!immutableBlockState.get(this.waterloggedProperty) && fluidType == MFluids.WATER) {
FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], args[1], immutableBlockState.with(this.waterloggedProperty, true).customBlockState().handle(), 3);
FastNMS.INSTANCE.method$LevelAccessor$scheduleFluidTick(args[0], args[1], fluidType, 5);
return true;
}
}
return false;
}
@Override
public boolean canPlaceLiquid(Object thisBlock, Object[] args, Callable<Object> superMethod) {
if (this.waterloggedProperty == null) return false;
return (VersionHelper.isOrAbove1_20_2() ? args[4] : args[3]) == MFluids.WATER;
}
@SuppressWarnings("unchecked")
public static class Factory implements BlockBehaviorFactory {

View File

@@ -1,8 +1,6 @@
package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.item.behavior.BucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.FlintAndSteelItemBehavior;
import net.momirealms.craftengine.bukkit.item.behavior.WaterBucketItemBehavior;
import net.momirealms.craftengine.bukkit.item.factory.BukkitItemFactory;
import net.momirealms.craftengine.bukkit.item.listener.ArmorEventListener;
import net.momirealms.craftengine.bukkit.item.listener.DebugStickListener;
@@ -40,8 +38,8 @@ import java.util.Set;
public class BukkitItemManager extends AbstractItemManager<ItemStack> {
static {
registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
// registerVanillaItemExtraBehavior(WaterBucketItemBehavior.INSTANCE, ItemKeys.WATER_BUCKETS);
// registerVanillaItemExtraBehavior(BucketItemBehavior.INSTANCE, ItemKeys.BUCKET);
registerVanillaItemExtraBehavior(FlintAndSteelItemBehavior.INSTANCE, ItemKeys.FLINT_AND_STEEL);
}

View File

@@ -1,91 +0,0 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.RayTraceResult;
import java.nio.file.Path;
import java.util.Map;
public class BucketItemBehavior extends ItemBehavior {
public static final BucketItemBehavior INSTANCE = new BucketItemBehavior();
public static final Factory FACTORY = new Factory();
@Override
public InteractionResult useOnBlock(UseOnContext context) {
return use(context.getLevel(), context.getPlayer(), context.getHand());
}
@Override
public InteractionResult use(net.momirealms.craftengine.core.world.World world, net.momirealms.craftengine.core.entity.player.Player player, InteractionHand hand) {
if (player.isAdventureMode()) return InteractionResult.PASS;
Player bukkitPlayer = (Player) player.platformPlayer();
RayTraceResult result = bukkitPlayer.rayTraceBlocks(player.getCachedInteractionRange(), FluidCollisionMode.SOURCE_ONLY);
if (result == null) return InteractionResult.PASS;
Block block = result.getHitBlock();
if (block == null) return InteractionResult.PASS;
return tryFillBucket(world, player, hand, new BlockPos(block.getX(), block.getY(), block.getZ()));
}
@SuppressWarnings("unchecked")
private InteractionResult tryFillBucket(net.momirealms.craftengine.core.world.World world,
net.momirealms.craftengine.core.entity.player.Player player,
InteractionHand hand,
BlockPos pos) {
Object nmsPos = LocationUtils.toBlockPos(pos.x(), pos.y(), pos.z());
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world.serverWorld(), nmsPos);
ImmutableBlockState customState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (customState == null || customState.isEmpty()) return InteractionResult.PASS;
CustomBlock customBlock = customState.owner().value();
Property<Boolean> waterlogged = (Property<Boolean>) customBlock.getProperty("waterlogged");
if (waterlogged == null) return InteractionResult.PASS;
boolean waterloggedState = customState.get(waterlogged);
if (!waterloggedState) return InteractionResult.PASS;
EquipmentSlot slot = hand == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND;
FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), nmsPos, customState.with(waterlogged, false).customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
Player bukkitPlayer = (Player) player.platformPlayer();
if (player.isSurvivalMode()) {
// to prevent dupe in moment
bukkitPlayer.getInventory().setItem(slot, new ItemStack(Material.AIR));
if (VersionHelper.isFolia()) {
bukkitPlayer.getScheduler().run(BukkitCraftEngine.instance().javaPlugin(), (t) -> bukkitPlayer.getInventory().setItem(slot, new ItemStack(Material.WATER_BUCKET)), () -> {});
} else {
BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> bukkitPlayer.getInventory().setItem(slot, new ItemStack(Material.WATER_BUCKET)));
}
}
bukkitPlayer.setStatistic(Statistic.USE_ITEM, Material.BUCKET, bukkitPlayer.getStatistic(Statistic.USE_ITEM, Material.BUCKET) + 1);
// client will assume it has sounds
// context.getPlayer().level().playBlockSound(Vec3d.atCenterOf(context.getClickedPos()), ITEM_BUCKET_FILL, 1, 1);
return InteractionResult.SUCCESS_AND_CANCEL;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -8,8 +8,6 @@ public class BukkitItemBehaviors extends ItemBehaviors {
public static final Key BLOCK_ITEM = Key.from("craftengine:block_item");
public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item");
public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item");
public static final Key WATER_BUCKET_ITEM = Key.from("craftengine:water_bucket_item");
public static final Key BUCKET_ITEM = Key.from("craftengine:bucket_item");
public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item");
public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item");
@@ -18,8 +16,6 @@ public class BukkitItemBehaviors extends ItemBehaviors {
register(BLOCK_ITEM, BlockItemBehavior.FACTORY);
register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY);
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
register(WATER_BUCKET_ITEM, WaterBucketItemBehavior.FACTORY);
register(BUCKET_ITEM, BucketItemBehavior.FACTORY);
register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY);
register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY);
}

View File

@@ -1,70 +0,0 @@
package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.UpdateOption;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockPos;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import java.nio.file.Path;
import java.util.Map;
public class WaterBucketItemBehavior extends ItemBehavior {
public static final WaterBucketItemBehavior INSTANCE = new WaterBucketItemBehavior();
public static final Factory FACTORY = new Factory();
// todo 需要修复不完整方块取水
@SuppressWarnings("unchecked")
@Override
public InteractionResult useOnBlock(UseOnContext context) {
if (context.getPlayer().isAdventureMode()) return InteractionResult.PASS;
BlockPos pos = context.getClickedPos();
BukkitBlockInWorld clicked = (BukkitBlockInWorld) context.getLevel().getBlockAt(pos);
Block block = clicked.block();
ImmutableBlockState state = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockDataToId(block.getBlockData()));
if (state == null || state.isEmpty()) return InteractionResult.PASS;
CustomBlock customBlock = state.owner().value();
Property<Boolean> waterlogged = (Property<Boolean>) customBlock.getProperty("waterlogged");
if (waterlogged == null) return InteractionResult.PASS;
Player player = (Player) context.getPlayer().platformPlayer();
World world = player.getWorld();
Location location = new Location(world, pos.x(), pos.y(), pos.z());
// TODO Refactor all of this because it's playing a trick with the server
ImmutableBlockState nextState = state.with(waterlogged, true);
block.setBlockData(BlockStateUtils.fromBlockData(nextState.vanillaBlockState().handle()), false);
// actually we should broadcast this change
context.getPlayer().sendPacket(BlockStateUtils.createBlockUpdatePacket(pos, state), true);
BukkitCraftEngine.instance().scheduler().sync().runDelayed(() -> {
Object blockPos = LocationUtils.toBlockPos(pos);
FastNMS.INSTANCE.method$LevelWriter$setBlock(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), blockPos, nextState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
FastNMS.INSTANCE.method$LevelAccessor$scheduleFluidTick(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), blockPos, MFluids.WATER, 5);
}, world, location.getBlockX() >> 4, location.getBlockZ() >> 4);
return InteractionResult.SUCCESS;
}
public static class Factory implements ItemBehaviorFactory {
@Override
public ItemBehavior create(Pack pack, Path path, Key id, Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -55,7 +55,7 @@ public final class BlockGenerator {
// should always implement this interface
.implement(CoreReflections.clazz$Fallable)
.implement(CoreReflections.clazz$BonemealableBlock)
// TODO .implement(CoreReflections.clazz$SimpleWaterloggedBlock)
.implement(CoreReflections.clazz$SimpleWaterloggedBlock)
// internal interfaces
.implement(BehaviorHolder.class)
.implement(ShapeHolder.class)
@@ -152,7 +152,16 @@ public final class BlockGenerator {
.intercept(MethodDelegation.to(OnExplosionHitInterceptor.INSTANCE))
// neighborChanged
.method(ElementMatchers.is(CoreReflections.method$BlockBehaviour$neighborChanged))
.intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE));
.intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE))
// pickupBlock
.method(ElementMatchers.is(CoreReflections.method$SimpleWaterloggedBlock$pickupBlock))
.intercept(MethodDelegation.to(PickUpBlockInterceptor.INSTANCE))
// placeLiquid
.method(ElementMatchers.is(CoreReflections.method$SimpleWaterloggedBlock$placeLiquid))
.intercept(MethodDelegation.to(PlaceLiquidInterceptor.INSTANCE))
// canPlaceLiquid
.method(ElementMatchers.is(CoreReflections.method$SimpleWaterloggedBlock$canPlaceLiquid))
.intercept(MethodDelegation.to(CanPlaceLiquidInterceptor.INSTANCE));
Class<?> clazz$CraftEngineBlock = builder.make().load(BlockGenerator.class.getClassLoader()).getLoaded();
constructor$CraftEngineBlock = MethodHandles.publicLookup().in(clazz$CraftEngineBlock)
.findConstructor(clazz$CraftEngineBlock, MethodType.methodType(void.class, CoreReflections.clazz$BlockBehaviour$Properties))
@@ -468,4 +477,49 @@ public final class BlockGenerator {
}
}
}
public static class PickUpBlockInterceptor {
public static final PickUpBlockInterceptor INSTANCE = new PickUpBlockInterceptor();
@RuntimeType
public Object intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().pickupBlock(thisObj, args, () -> CoreReflections.instance$ItemStack$EMPTY);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run pickupBlock", e);
return CoreReflections.instance$ItemStack$EMPTY;
}
}
}
public static class PlaceLiquidInterceptor {
public static final PlaceLiquidInterceptor INSTANCE = new PlaceLiquidInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) throws Exception {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().placeLiquid(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run placeLiquid", e);
return false;
}
}
}
public static class CanPlaceLiquidInterceptor {
public static final CanPlaceLiquidInterceptor INSTANCE = new CanPlaceLiquidInterceptor();
@RuntimeType
public boolean intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
return holder.value().canPlaceLiquid(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run canPlaceLiquid", e);
return false;
}
}
}
}

View File

@@ -98,4 +98,16 @@ public abstract class BlockBehavior {
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
return InteractionResult.PASS;
}
public Object pickupBlock(Object thisObj, Object[] args, Callable<Object> superMethod) throws Exception {
return superMethod.call();
}
public boolean placeLiquid(Object thisObj, Object[] args, Callable<Object> superMethod) {
return false;
}
public boolean canPlaceLiquid(Object thisObj, Object[] args, Callable<Object> superMethod) {
return false;
}
}