9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-31 04:46:37 +00:00

红石基础

This commit is contained in:
XiaoMoMi
2025-05-25 02:52:33 +08:00
parent 539dcd7d8f
commit de47525c3b
27 changed files with 810 additions and 52 deletions

View File

@@ -17,6 +17,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key SUGARCANE_BLOCK = Key.from("craftengine:sugar_cane_block");
public static final Key CROP_BLOCK = Key.from("craftengine:crop_block");
public static final Key GRASS_BLOCK = Key.from("craftengine:grass_block");
public static final Key LAMP_BLOCK = Key.from("craftengine:lamp_block");
public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -32,5 +33,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(SUGARCANE_BLOCK, SugarCaneBlockBehavior.FACTORY);
register(CROP_BLOCK, CropBlockBehavior.FACTORY);
register(GRASS_BLOCK, GrassBlockBehavior.FACTORY);
register(LAMP_BLOCK, LampBlockBehavior.FACTORY);
}
}

View File

@@ -0,0 +1,65 @@
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.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
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.ResourceConfigUtils;
import net.momirealms.craftengine.shared.block.BlockBehavior;
import java.util.Map;
import java.util.concurrent.Callable;
public class LampBlockBehavior extends BukkitBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<Boolean> litProperty;
public LampBlockBehavior(CustomBlock block, Property<Boolean> litProperty) {
super(block);
this.litProperty = litProperty;
}
@Override
public void tick(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()) return;
Object world = args[1];
Object blockPos = args[2];
if (state.get(this.litProperty) && !FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
// TODO Call Event
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2);
}
}
@Override
public void neighborChanged(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()) return;
Object world = args[1];
Object blockPos = args[2];
boolean lit = state.get(this.litProperty);
if (lit != FastNMS.INSTANCE.method$SignalGetter$hasNeighborSignal(world, blockPos)) {
if (lit) {
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 4);
} else {
// TODO Call Event
FastNMS.INSTANCE.method$LevelWriter$setBlock(world, blockPos, state.cycle(this.litProperty).customBlockState().handle(), 2);
}
}
}
@SuppressWarnings("unchecked")
public static class Factory implements BlockBehaviorFactory {
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<Boolean> lit = (Property<Boolean>) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("lit"), "warning.config.block.behavior.lamp.missing_lit");
return new LampBlockBehavior(block, lit);
}
}
}

View File

@@ -30,6 +30,7 @@ import org.bukkit.event.block.LeavesDecayEvent;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
@@ -78,10 +79,14 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
neighborState = args[2];
}
ImmutableBlockState thisState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (thisState != null && thisState.behavior() instanceof LeavesBlockBehavior behavior) {
int distance = behavior.getDistanceAt(neighborState) + 1;
if (distance != 1 || behavior.getDistance(thisState) != distance) {
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 1);
if (thisState != null) {
Optional<LeavesBlockBehavior> optionalBehavior = thisState.behavior().getAs(LeavesBlockBehavior.class);
if (optionalBehavior.isPresent()) {
LeavesBlockBehavior behavior = optionalBehavior.get();
int distance = behavior.getDistanceAt(neighborState) + 1;
if (distance != 1 || behavior.getDistance(thisState) != distance) {
Reflections.method$LevelAccessor$scheduleTick.invoke(world, blockPos, thisBlock, 1);
}
}
}
return blockState;
@@ -93,13 +98,17 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
Object level = args[1];
Object blockPos = args[2];
ImmutableBlockState currentState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(blockState));
if (currentState != null && !currentState.isEmpty() && currentState.behavior() instanceof LeavesBlockBehavior behavior) {
ImmutableBlockState newState = behavior.updateDistance(currentState, level, blockPos);
if (newState != currentState) {
if (blockState == newState.customBlockState().handle()) {
Reflections.method$BlockStateBase$updateNeighbourShapes.invoke(blockState, level, blockPos, UpdateOption.UPDATE_ALL.flags(), 512);
} else {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
if (currentState != null && !currentState.isEmpty()) {
Optional<LeavesBlockBehavior> optionalBehavior = currentState.behavior().getAs(LeavesBlockBehavior.class);
if (optionalBehavior.isPresent()) {
LeavesBlockBehavior behavior = optionalBehavior.get();
ImmutableBlockState newState = behavior.updateDistance(currentState, level, blockPos);
if (newState != currentState) {
if (blockState == newState.customBlockState().handle()) {
Reflections.method$BlockStateBase$updateNeighbourShapes.invoke(blockState, level, blockPos, UpdateOption.UPDATE_ALL.flags(), 512);
} else {
FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, newState.customBlockState().handle(), UpdateOption.UPDATE_ALL.flags());
}
}
}
}
@@ -110,25 +119,31 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
Object level = args[1];
Object blockPos = args[2];
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockState(BlockStateUtils.blockStateToId(args[0]));
if (immutableBlockState != null && immutableBlockState.behavior() instanceof LeavesBlockBehavior behavior && behavior.isDecaying(immutableBlockState)) {
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
// call bukkit event
LeavesDecayEvent event = new LeavesDecayEvent(bukkitWorld.getBlockAt(pos.x(), pos.y(), pos.z()));
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false);
if (isWaterLogged(immutableBlockState)) {
bukkitWorld.setBlockData(pos.x(), pos.y(), pos.z(), Material.WATER.createBlockData());
}
net.momirealms.craftengine.core.world.World world = new BukkitWorld(bukkitWorld);
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(position, item);
if (immutableBlockState != null) {
Optional<LeavesBlockBehavior> optionalBehavior = immutableBlockState.behavior().getAs(LeavesBlockBehavior.class);
if (optionalBehavior.isPresent()) {
LeavesBlockBehavior behavior = optionalBehavior.get();
if (behavior.isDecaying(immutableBlockState)) {
World bukkitWorld = FastNMS.INSTANCE.method$Level$getCraftWorld(level);
BlockPos pos = LocationUtils.fromBlockPos(blockPos);
// call bukkit event
LeavesDecayEvent event = new LeavesDecayEvent(bukkitWorld.getBlockAt(pos.x(), pos.y(), pos.z()));
Bukkit.getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
FastNMS.INSTANCE.method$Level$removeBlock(level, blockPos, false);
if (isWaterLogged(immutableBlockState)) {
bukkitWorld.setBlockData(pos.x(), pos.y(), pos.z(), Material.WATER.createBlockData());
}
net.momirealms.craftengine.core.world.World world = new BukkitWorld(bukkitWorld);
WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos));
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position);
for (Item<Object> item : immutableBlockState.getDrops(builder, world, null)) {
world.dropItemNaturally(position, item);
}
}
}
}
}
@@ -164,8 +179,8 @@ public class LeavesBlockBehavior extends WaterLoggedBlockBehavior {
return (int) Reflections.method$StateHolder$getValue.invoke(blockState, distanceProperty);
} else {
ImmutableBlockState anotherBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(id);
if (!(anotherBlockState.behavior() instanceof LeavesBlockBehavior otherBehavior)) return this.maxDistance;
return otherBehavior.getDistance(anotherBlockState);
Optional<LeavesBlockBehavior> optionalAnotherBehavior = anotherBlockState.behavior().getAs(LeavesBlockBehavior.class);
return optionalAnotherBehavior.map(leavesBlockBehavior -> leavesBlockBehavior.getDistance(anotherBlockState)).orElse(this.maxDistance);
}
}

View File

@@ -318,6 +318,9 @@ public class BukkitInjector {
.and(ElementMatchers.takesArgument(1, Reflections.clazz$LevelReader).or(ElementMatchers.takesArgument(1, Reflections.clazz$Direction)))
.and(ElementMatchers.named("updateShape").or(ElementMatchers.named("a"))))
.intercept(MethodDelegation.to(UpdateShapeInterceptor.INSTANCE))
// neighborChanged
.method(ElementMatchers.is(Reflections.method$BlockBehaviour$neighborChanged))
.intercept(MethodDelegation.to(NeighborChangedInterceptor.INSTANCE))
// // getFluidState
// .method(ElementMatchers.returns(Reflections.clazz$FluidState)
// .and(ElementMatchers.takesArgument(0, Reflections.clazz$BlockState)))
@@ -1078,6 +1081,20 @@ public class BukkitInjector {
}
}
}
public static class NeighborChangedInterceptor {
public static final NeighborChangedInterceptor INSTANCE = new NeighborChangedInterceptor();
@RuntimeType
public void intercept(@This Object thisObj, @AllArguments Object[] args, @SuperCall Callable<Object> superMethod) {
ObjectHolder<BlockBehavior> holder = ((BehaviorHolder) thisObj).getBehaviorHolder();
try {
holder.value().neighborChanged(thisObj, args, superMethod);
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to run neighborChanged", e);
}
}
}
//
// public static class PickUpBlockInterceptor {
// public static final PickUpBlockInterceptor INSTANCE = new PickUpBlockInterceptor();

View File

@@ -6791,4 +6791,16 @@ public class Reflections {
}
}
public static final Class<?> clazz$Orientation =
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.level.redstone.Orientation",
"world.level.redstone.Orientation"
);
public static final Method method$BlockBehaviour$neighborChanged = requireNonNull(
VersionHelper.isOrAbove1_21_2() ?
ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Block, clazz$Orientation, boolean.class) :
Optional.ofNullable(ReflectionUtils.getDeclaredMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Block, clazz$BlockPos, boolean.class))
.orElse(ReflectionUtils.getMethod(clazz$BlockBehaviour, void.class, clazz$BlockState, clazz$Level, clazz$BlockPos, clazz$Block, clazz$BlockPos, boolean.class))
);
}

View File

@@ -1,7 +1,10 @@
package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
import net.momirealms.craftengine.bukkit.util.SoundUtils;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.Context;
@@ -15,7 +18,6 @@ import net.momirealms.craftengine.core.world.WorldHeight;
import net.momirealms.craftengine.core.world.particle.ParticleData;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.Registry;
import org.bukkit.SoundCategory;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.ExperienceOrb;