diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java index f336c1cbb..cf4cda67b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitCustomBlockStateWrapper.java @@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.block; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper; +import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.util.Key; @@ -30,6 +31,21 @@ public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper { }).orElse(null); } + @Override + public BlockStateWrapper withProperty(String propertyName, String propertyValue) { + Optional immutableBlockState = getImmutableBlockState(); + if (immutableBlockState.isPresent()) { + Property property = immutableBlockState.get().owner().value().getProperty(propertyName); + if (property != null) { + Comparable value = property.valueByName(propertyValue); + if (value != null) { + return ImmutableBlockState.with(immutableBlockState.get(), property, value).customBlockState(); + } + } + } + return this; + } + @Override public boolean hasProperty(String propertyName) { return getImmutableBlockState().map(state -> state.owner().value().getProperty(propertyName) != null).orElse(false); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java index 9a13a5d13..61c5d7172 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/BukkitVanillaBlockStateWrapper.java @@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.block; import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper; +import net.momirealms.craftengine.core.block.BlockRegistryMirror; +import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.StatePropertyAccessor; import net.momirealms.craftengine.core.util.Key; @@ -33,4 +35,11 @@ public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper { public String getAsString() { return BlockStateUtils.fromBlockData(super.blockState).getAsString(); } + + @Override + public BlockStateWrapper withProperty(String propertyName, String propertyValue) { + Object newState = this.accessor.withProperty(propertyName, propertyValue); + if (newState == super.blockState) return this; + return BlockRegistryMirror.byId(BlockStateUtils.blockStateToId(newState)); + } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java index b6a40cf8f..14cfb8f28 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/ChangeOverTimeBlockBehavior.java @@ -2,12 +2,14 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.core.block.*; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; -import net.momirealms.craftengine.core.util.Key; -import net.momirealms.craftengine.core.util.RandomUtils; -import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.util.*; +import net.momirealms.sparrow.nbt.CompoundTag; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Callable; @@ -15,23 +17,47 @@ import java.util.concurrent.Callable; public class ChangeOverTimeBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final float changeSpeed; - private final Key nextBlock; + private final String nextBlock; + private final LazyReference lazyState; + private final List excludedProperties; - public ChangeOverTimeBlockBehavior(CustomBlock customBlock, float changeSpeed, Key nextBlock) { + public ChangeOverTimeBlockBehavior(CustomBlock customBlock, float changeSpeed, String nextBlock, List excludedProperties) { super(customBlock); this.changeSpeed = changeSpeed; this.nextBlock = nextBlock; + this.excludedProperties = excludedProperties; + this.lazyState = LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(this.nextBlock)); + } + + public String nextBlock() { + return nextBlock; + } + + public BlockStateWrapper nextState() { + return this.lazyState.get(); + } + + public CompoundTag filter(CompoundTag properties) { + for (String property : this.excludedProperties) { + properties.remove(property); + } + return properties; } @Override - public void randomTick(Object thisBlock, Object[] args, Callable superMethod) throws ReflectiveOperationException { + public void randomTick(Object thisBlock, Object[] args, Callable superMethod) { if (RandomUtils.generateRandomFloat(0F, 1F) >= this.changeSpeed) return; - Optional nextState = BukkitBlockManager.instance().blockById(this.nextBlock) - .map(CustomBlock::defaultState) - .map(ImmutableBlockState::customBlockState) - .map(BlockStateWrapper::literalObject); - if (nextState.isEmpty()) return; - CraftBukkitReflections.method$CraftEventFactory$handleBlockFormEvent.invoke(null, args[1], args[2], nextState.get(), UpdateOption.UPDATE_ALL.flags()); + Object blockState = args[0]; + BlockStateUtils.getOptionalCustomBlockState(blockState).ifPresent(state -> { + BlockStateWrapper nextState = this.nextState(); + if (nextState == null) return; + nextState = nextState.withProperties(filter(state.propertiesNbt())); + try { + CraftBukkitReflections.method$CraftEventFactory$handleBlockFormEvent.invoke(null, args[1], args[2], nextState.literalObject(), UpdateOption.UPDATE_ALL.flags()); + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to call block form event", e); + } + }); } public static class Factory implements BlockBehaviorFactory { @@ -39,8 +65,9 @@ public class ChangeOverTimeBlockBehavior extends BukkitBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { float changeSpeed = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("change-speed", 0.05688889F), "change-speed"); - Key nextBlock = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.getOrDefault("next-block", "minecraft:air"), "warning.config.block.behavior.change_over_time.missing_next_block")); - return new ChangeOverTimeBlockBehavior(block, changeSpeed, nextBlock); + String nextBlock = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.getOrDefault("next-block", "minecraft:air"), "warning.config.block.behavior.change_over_time.missing_next_block"); + List excludedProperties = MiscUtils.getAsStringList(arguments.get("excluded-properties")); + return new ChangeOverTimeBlockBehavior(block, changeSpeed, nextBlock, excludedProperties); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java index a1b243ee9..bbe9837ae 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StrippableBlockBehavior.java @@ -1,40 +1,53 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.BlockStateWrapper; 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.parser.BlockStateParser; +import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.LazyReference; +import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.sparrow.nbt.CompoundTag; +import java.util.List; import java.util.Map; public class StrippableBlockBehavior extends BukkitBlockBehavior { public static final Factory FACTORY = new Factory(); private final String stripped; - private final LazyReference lazyState; + private final LazyReference lazyState; + private final List excludedProperties; - public StrippableBlockBehavior(CustomBlock block, String stripped) { + public StrippableBlockBehavior(CustomBlock block, String stripped, List excludedProperties) { super(block); this.stripped = stripped; - this.lazyState = LazyReference.lazyReference(() -> BlockStateParser.deserialize(this.stripped)); + this.lazyState = LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(this.stripped)); + this.excludedProperties = excludedProperties; } public String stripped() { return this.stripped; } - public ImmutableBlockState strippedState() { + public BlockStateWrapper strippedState() { return this.lazyState.get(); } + public CompoundTag filter(CompoundTag properties) { + for (String property : this.excludedProperties) { + properties.remove(property); + } + return properties; + } + public static class Factory implements BlockBehaviorFactory { @Override public BlockBehavior create(CustomBlock block, Map arguments) { String stripped = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("stripped"), "warning.config.block.behavior.strippable.missing_stripped"); - return new StrippableBlockBehavior(block, stripped); + List excludedProperties = MiscUtils.getAsStringList(arguments.get("excluded-properties")); + return new StrippableBlockBehavior(block, stripped, excludedProperties); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java index 882ba4be5..6d23f2ec7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/AxeItemBehavior.java @@ -70,21 +70,19 @@ public class AxeItemBehavior extends ItemBehavior { return InteractionResult.PASS; } - ImmutableBlockState newState = behaviorOptional.get().strippedState(); + BlockStateWrapper newState = behaviorOptional.get().strippedState(); if (newState == null) { CraftEngine.instance().logger().warn("stripped block " + behaviorOptional.get().stripped() + " does not exist"); return InteractionResult.FAIL; } - BlockStateWrapper - - newState = newState.with(customState.propertiesNbt()); + newState = newState.withProperties(behaviorOptional.get().filter(customState.propertiesNbt())); BukkitExistingBlock clicked = (BukkitExistingBlock) context.getLevel().getBlockAt(context.getClickedPos()); org.bukkit.entity.Player bukkitPlayer = null; if (player != null) { bukkitPlayer = ((org.bukkit.entity.Player) player.platformPlayer()); // Call bukkit event - EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.customBlockState().literalObject())); + EntityChangeBlockEvent event = new EntityChangeBlockEvent(bukkitPlayer, clicked.block(), BlockStateUtils.fromBlockData(newState.literalObject())); if (EventUtils.fireAndCheckCancel(event)) { return InteractionResult.FAIL; } @@ -95,7 +93,7 @@ public class AxeItemBehavior extends ItemBehavior { if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL; BlockPos pos = context.getClickedPos(); context.getLevel().playBlockSound(Vec3d.atCenterOf(pos), AXE_STRIP_SOUND, 1, 1); - FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.customBlockState().literalObject(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(context.getLevel().serverWorld(), LocationUtils.toBlockPos(pos), newState.literalObject(), UpdateOption.UPDATE_ALL_IMMEDIATE.flags()); clicked.block().getWorld().sendGameEvent(bukkitPlayer, GameEvent.BLOCK_CHANGE, new Vector(pos.x(), pos.y(), pos.z())); Material material = MaterialUtils.getMaterial(item.vanillaId()); if (bukkitPlayer != null) { @@ -123,7 +121,7 @@ public class AxeItemBehavior extends ItemBehavior { itemStack.damage(1, bukkitPlayer); } } - return InteractionResult.SUCCESS; + return InteractionResult.SUCCESS_AND_CANCEL; } public static class Factory implements ItemBehaviorFactory { diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java index 5ef5223b1..d8c97482b 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/BlockStateWrapper.java @@ -1,8 +1,11 @@ package net.momirealms.craftengine.core.block; import net.momirealms.craftengine.core.util.Key; +import net.momirealms.sparrow.nbt.*; import org.jetbrains.annotations.NotNull; +import java.util.Map; + public interface BlockStateWrapper extends Comparable { Object literalObject(); @@ -15,10 +18,27 @@ public interface BlockStateWrapper extends Comparable { boolean hasProperty(String propertyName); + BlockStateWrapper withProperty(String propertyName, String propertyValue); + String getAsString(); @Override default int compareTo(@NotNull BlockStateWrapper o) { return Integer.compare(registryId(), o.registryId()); } + + default BlockStateWrapper withProperties(CompoundTag properties) { + BlockStateWrapper result = this; + for (Map.Entry entry : properties.entrySet()) { + Tag value = entry.getValue(); + if (value instanceof StringTag stringTag) { + result = result.withProperty(entry.getKey(), stringTag.getAsString()); + } else if (value instanceof IntTag intTag) { + result = result.withProperty(entry.getKey(), String.valueOf(intTag.getAsInt())); + } else if (value instanceof ByteTag byteTag) { + result = result.withProperty(entry.getKey(), String.valueOf(byteTag.booleanValue())); + } + } + return result; + } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java index e2752a6c4..c46a69faf 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/ImmutableBlockState.java @@ -7,7 +7,6 @@ import net.momirealms.craftengine.core.block.entity.BlockEntityType; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement; import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig; import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker; -import net.momirealms.craftengine.core.block.properties.EnumProperty; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.item.Item; diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java b/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java index 3e1a46e3f..1277e0b1e 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/StatePropertyAccessor.java @@ -1,5 +1,7 @@ package net.momirealms.craftengine.core.block; +import org.jetbrains.annotations.NotNull; + import java.util.Collection; public interface StatePropertyAccessor { @@ -11,4 +13,7 @@ public interface StatePropertyAccessor { boolean hasProperty(String property); T getPropertyValue(String property); + + @NotNull + Object withProperty(String propertyName, String value); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java index 95b989567..00bf88224 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/IntegerProperty.java @@ -6,7 +6,6 @@ import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.sparrow.nbt.IntTag; import net.momirealms.sparrow.nbt.NumericTag; import net.momirealms.sparrow.nbt.Tag; -import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.Map; diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java index cacd02165..f89787fa1 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/Property.java @@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.util.Direction; import net.momirealms.craftengine.core.util.HorizontalDirection; import net.momirealms.sparrow.nbt.Tag; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -155,7 +156,7 @@ public abstract class Property> { } @Override - public String toString() { + public @NotNull String toString() { return this.property.name + "=" + this.property.valueName(this.value); } } diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java b/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java index 3d68ce99a..f9a556df3 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java +++ b/core/src/main/java/net/momirealms/craftengine/core/block/properties/StringProperty.java @@ -4,10 +4,8 @@ import com.google.common.collect.ImmutableMap; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.sparrow.nbt.StringTag; import net.momirealms.sparrow.nbt.Tag; -import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Optional; diff --git a/gradle.properties b/gradle.properties index 153a6411f..006b5a0c5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -50,7 +50,7 @@ byte_buddy_version=1.17.5 ahocorasick_version=0.6.3 snake_yaml_version=2.5 anti_grief_version=1.0.2 -nms_helper_version=1.0.103 +nms_helper_version=1.0.104 evalex_version=3.5.0 reactive_streams_version=1.0.4 amazon_awssdk_version=2.34.5