diff --git a/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml b/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml index 8f4d7e973..8016ddf9c 100644 --- a/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml +++ b/bukkit/loader/src/main/resources/resources/default/configuration/plants.yml @@ -34,6 +34,9 @@ blocks: push-reaction: DESTROY behavior: type: bush_block + bottom-block-tags: + - minecraft:dirt + - minecraft:farmland loot: template: "default:loot_table/basic" arguments: diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index fc6fd3488..9cf2a38b6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -6,6 +6,7 @@ import net.momirealms.craftengine.shared.block.EmptyBlockBehavior; public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key BUSH_BLOCK = Key.from("craftengine:bush_block"); + public static final Key HANGING_BLOCK = Key.from("craftengine:hanging_block"); public static final Key FALLING_BLOCK = Key.from("craftengine:falling_block"); public static final Key LEAVES_BLOCK = Key.from("craftengine:leaves_block"); public static final Key STRIPPABLE_BLOCK = Key.from("craftengine:strippable_block"); @@ -18,6 +19,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); register(FALLING_BLOCK, FallingBlockBehavior.FACTORY); register(BUSH_BLOCK, BushBlockBehavior.FACTORY); + register(HANGING_BLOCK, HangingBlockBehavior.FACTORY); register(LEAVES_BLOCK, LeavesBlockBehavior.FACTORY); register(STRIPPABLE_BLOCK, StrippableBlockBehavior.FACTORY); register(SAPLING_BLOCK, SaplingBlockBehavior.FACTORY); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java index 6de0a63b8..19fa6e522 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BushBlockBehavior.java @@ -14,26 +14,29 @@ import net.momirealms.craftengine.core.item.Item; import net.momirealms.craftengine.core.loot.parameter.LootParameters; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; +import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.context.ContextHolder; import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.shared.block.BlockBehavior; -import org.bukkit.World; +import org.bukkit.*; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.Callable; public class BushBlockBehavior extends AbstractBlockBehavior { public static final Factory FACTORY = new Factory(); - protected static final Object DIRT_TAG = BlockTags.getOrCreate(Key.of("minecraft", "dirt")); - protected static final Object FARMLAND = BlockTags.getOrCreate(Key.of("minecraft", "farmland")); - public static final BushBlockBehavior INSTANCE = new BushBlockBehavior(List.of(DIRT_TAG, FARMLAND)); protected final List tagsCanSurviveOn; + protected final Set blocksCansSurviveOn; + protected final Set customBlocksCansSurviveOn; + protected final boolean any; - public BushBlockBehavior(List tagsCanSurviveOn) { + public BushBlockBehavior(List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { this.tagsCanSurviveOn = tagsCanSurviveOn; + this.blocksCansSurviveOn = blocksCansSurviveOn; + this.customBlocksCansSurviveOn = customBlocksCansSurviveOn; + this.any = this.tagsCanSurviveOn.isEmpty() && this.blocksCansSurviveOn.isEmpty() && this.customBlocksCansSurviveOn.isEmpty(); } @Override @@ -82,19 +85,41 @@ public class BushBlockBehavior extends AbstractBlockBehavior { } public static class Factory implements BlockBehaviorFactory { + @Override public BlockBehavior create(CustomBlock block, Map arguments) { - if (arguments.containsKey("bottom-block-tags")) { - return new BushBlockBehavior(MiscUtils.getAsStringList(arguments.get("bottom-block-tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList()); - } else if (arguments.containsKey("tags")) { - return new BushBlockBehavior(MiscUtils.getAsStringList(arguments.get("tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList()); - } else { - return INSTANCE; - } + Tuple, Set, Set> tuple = readTagsAndState(arguments); + return new BushBlockBehavior(tuple.left(), tuple.mid(), tuple.right()); } } - private boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + public static Tuple, Set, Set> readTagsAndState(Map arguments) { + List mcTags = new ArrayList<>(); + for (String tag : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-block-tags", List.of()))) { + mcTags.add(BlockTags.getOrCreate(Key.of(tag))); + } + Set mcBlocks = new HashSet<>(); + Set customBlocks = new HashSet<>(); + for (String blockStateStr : MiscUtils.getAsStringList(arguments.getOrDefault("bottom-blocks", List.of()))) { + int index = blockStateStr.indexOf('['); + Key blockType = index != -1 ? Key.from(blockStateStr.substring(0, index)) : Key.from(blockStateStr); + Material material = Registry.MATERIAL.get(new NamespacedKey(blockType.namespace(), blockType.value())); + if (material != null) { + if (index == -1) { + // vanilla + mcBlocks.addAll(BlockStateUtils.getAllVanillaBlockStates(blockType)); + } else { + mcBlocks.add(BlockStateUtils.blockDataToBlockState(Bukkit.createBlockData(blockStateStr))); + } + } else { + // custom maybe + customBlocks.add(blockStateStr); + } + } + return new Tuple<>(mcTags, mcBlocks, customBlocks); + } + + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { int y = Reflections.field$Vec3i$y.getInt(blockPos); int x = Reflections.field$Vec3i$x.getInt(blockPos); int z = Reflections.field$Vec3i$z.getInt(blockPos); @@ -104,11 +129,28 @@ public class BushBlockBehavior extends AbstractBlockBehavior { } protected boolean mayPlaceOn(Object belowState, Object world, Object belowPos) throws ReflectiveOperationException { + if (this.any) return true; for (Object tag : this.tagsCanSurviveOn) { if ((boolean) Reflections.method$BlockStateBase$hasTag.invoke(belowState, tag)) { return true; } } + int id = BlockStateUtils.blockStateToId(belowState); + if (BlockStateUtils.isVanillaBlock(id)) { + if (!this.blocksCansSurviveOn.isEmpty() && this.blocksCansSurviveOn.contains(belowState)) { + return true; + } + } else { + ImmutableBlockState previousState = BukkitBlockManager.instance().getImmutableBlockState(id); + if (previousState != null && !previousState.isEmpty()) { + if (this.customBlocksCansSurviveOn.contains(previousState.owner().value().id().toString())) { + return true; + } + if (this.customBlocksCansSurviveOn.contains(previousState.toString())) { + return true; + } + } + } return false; } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java new file mode 100644 index 000000000..034608fa6 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/HangingBlockBehavior.java @@ -0,0 +1,38 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.util.Reflections; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.util.Tuple; +import net.momirealms.craftengine.shared.block.BlockBehavior; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class HangingBlockBehavior extends BushBlockBehavior { + public static final Factory FACTORY = new Factory(); + + public HangingBlockBehavior(List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + } + + @Override + protected boolean canSurvive(Object thisBlock, Object state, Object world, Object blockPos) throws ReflectiveOperationException { + int y = Reflections.field$Vec3i$y.getInt(blockPos); + int x = Reflections.field$Vec3i$x.getInt(blockPos); + int z = Reflections.field$Vec3i$z.getInt(blockPos); + Object belowPos = Reflections.constructor$BlockPos.newInstance(x, y + 1, z); + Object belowState = Reflections.method$BlockGetter$getBlockState.invoke(world, belowPos); + return mayPlaceOn(belowState, world, belowPos); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + Tuple, Set, Set> tuple = readTagsAndState(arguments); + return new HangingBlockBehavior(tuple.left(), tuple.mid(), tuple.right()); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java index 1aca9d2bf..cb5934a2e 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/OnLiquidBlockBehavior.java @@ -9,14 +9,15 @@ import net.momirealms.craftengine.shared.block.BlockBehavior; import java.util.List; import java.util.Map; +import java.util.Set; public class OnLiquidBlockBehavior extends BushBlockBehavior { public static final Factory FACTORY = new Factory(); private final boolean onWater; private final boolean onLava; - public OnLiquidBlockBehavior(List tagsCanSurviveOn, boolean onWater, boolean onLava) { - super(tagsCanSurviveOn); + public OnLiquidBlockBehavior(boolean onWater, boolean onLava) { + super(List.of(), Set.of(), Set.of()); this.onWater = onWater; this.onLava = onLava; } @@ -33,7 +34,7 @@ public class OnLiquidBlockBehavior extends BushBlockBehavior { @Override public BlockBehavior create(CustomBlock block, Map arguments) { List liquidTypes = MiscUtils.getAsStringList(arguments.getOrDefault("liquid-type", List.of("water"))); - return new OnLiquidBlockBehavior(List.of(), liquidTypes.contains("water"), liquidTypes.contains("lava")); + return new OnLiquidBlockBehavior(liquidTypes.contains("water"), liquidTypes.contains("lava")); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java index 2998fa762..3f17a7320 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SaplingBlockBehavior.java @@ -2,7 +2,10 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; -import net.momirealms.craftengine.bukkit.util.*; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.FeatureUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +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.UpdateOption; @@ -12,6 +15,7 @@ import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MiscUtils; import net.momirealms.craftengine.core.util.RandomUtils; +import net.momirealms.craftengine.core.util.Tuple; import net.momirealms.craftengine.shared.block.BlockBehavior; import org.bukkit.Location; import org.bukkit.World; @@ -19,6 +23,7 @@ import org.bukkit.World; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.Callable; public class SaplingBlockBehavior extends BushBlockBehavior { @@ -27,8 +32,8 @@ public class SaplingBlockBehavior extends BushBlockBehavior { private final Property stageProperty; private final double boneMealSuccessChance; - public SaplingBlockBehavior(Key feature, Property stageProperty, List tagsCanSurviveOn, double boneMealSuccessChance) { - super(tagsCanSurviveOn); + public SaplingBlockBehavior(Key feature, Property stageProperty, List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn, double boneMealSuccessChance) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); this.feature = feature; this.stageProperty = stageProperty; this.boneMealSuccessChance = boneMealSuccessChance; @@ -121,13 +126,8 @@ public class SaplingBlockBehavior extends BushBlockBehavior { throw new IllegalArgumentException("stage property not set for sapling"); } double boneMealSuccessChance = MiscUtils.getAsDouble(arguments.getOrDefault("bone-meal-success-chance", 0.45)); - if (arguments.containsKey("bottom-block-tags")) { - return new SaplingBlockBehavior(Key.of(feature), stageProperty, MiscUtils.getAsStringList(arguments.get("bottom-block-tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList(), boneMealSuccessChance); - } else if (arguments.containsKey("tags")) { - return new SaplingBlockBehavior(Key.of(feature), stageProperty, MiscUtils.getAsStringList(arguments.get("tags")).stream().map(it -> BlockTags.getOrCreate(Key.of(it))).toList(), boneMealSuccessChance); - } else { - return new SaplingBlockBehavior(Key.of(feature), stageProperty, List.of(DIRT_TAG, FARMLAND), boneMealSuccessChance); - } + Tuple, Set, Set> tuple = readTagsAndState(arguments); + return new SaplingBlockBehavior(Key.of(feature), stageProperty, tuple.left(), tuple.mid(), tuple.right(), boneMealSuccessChance); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java new file mode 100644 index 000000000..615518c79 --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SugarCaneBlockBehavior.java @@ -0,0 +1,11 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import java.util.List; +import java.util.Set; + +public class SugarCaneBlockBehavior extends BushBlockBehavior { + + public SugarCaneBlockBehavior(List tagsCanSurviveOn, Set blocksCansSurviveOn, Set customBlocksCansSurviveOn) { + super(tagsCanSurviveOn, blocksCansSurviveOn, customBlocksCansSurviveOn); + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java index b477c47bc..9a47edb92 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/loot/BukkitVanillaLootManager.java @@ -118,7 +118,7 @@ public class BukkitVanillaLootManager implements VanillaLootManager, Listener { VanillaLoot vanillaLoot = this.blockLoots.computeIfAbsent(BlockStateUtils.blockStateToId(blockState), k -> new VanillaLoot(VanillaLoot.Type.BLOCK)); vanillaLoot.addLootTable(lootTable); } else { - for (Object blockState : BlockStateUtils.getAllBlockStates(Key.of(target))) { + for (Object blockState : BlockStateUtils.getAllVanillaBlockStates(Key.of(target))) { if (blockState == Reflections.instance$Blocks$AIR$defaultState) { this.plugin.logger().warn(path, "Failed to load " + id + ". Invalid target " + target); return; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java index f7fa9cddd..b3a773023 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/BukkitNetworkManager.java @@ -6,7 +6,6 @@ import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; -import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.network.impl.*; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java index 0cdb7db6e..52b298618 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/BlockStateUtils.java @@ -1,18 +1,23 @@ package net.momirealms.craftengine.bukkit.util; +import net.momirealms.craftengine.bukkit.block.BukkitBlockManager; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.core.block.BlockStateParser; +import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.PushReaction; import net.momirealms.craftengine.core.util.Instrument; import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.MapColor; import net.momirealms.craftengine.core.world.BlockPos; +import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; import org.bukkit.event.block.BlockPhysicsEvent; import java.util.IdentityHashMap; import java.util.List; +import java.util.Optional; public class BlockStateUtils { public static final IdentityHashMap CLIENT_SIDE_NOTE_BLOCKS = new IdentityHashMap<>(); @@ -27,8 +32,36 @@ public class BlockStateUtils { hasInit = true; } - @SuppressWarnings("unchecked") + public static List getAllBlockStates(String blockState) { + int index = blockState.indexOf('['); + if (index == -1) { + return getAllBlockStates(Key.of(blockState)); + } else { + String blockTypeString = blockState.substring(0, index); + Key block = Key.of(blockTypeString); + Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(block); + if (optionalCustomBlock.isPresent()) { + ImmutableBlockState state = BlockStateParser.deserialize(blockState); + if (state == null) { + return List.of(); + } else { + return List.of(state.customBlockState().handle()); + } + } else { + BlockData blockData = Bukkit.createBlockData(blockState); + return List.of(blockDataToBlockState(blockData)); + } + } + } + public static List getAllBlockStates(Key block) { + Optional optionalCustomBlock = BukkitBlockManager.instance().getBlock(block); + return optionalCustomBlock.map(customBlock -> customBlock.variantProvider().states().stream().map(it -> it.customBlockState().handle()).toList()) + .orElseGet(() -> getAllVanillaBlockStates(block)); + } + + @SuppressWarnings("unchecked") + public static List getAllVanillaBlockStates(Key block) { try { Object blockIns = Reflections.method$Registry$get.invoke(Reflections.instance$BuiltInRegistries$BLOCK, Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, block.namespace(), block.value())); Object definition = Reflections.field$Block$StateDefinition.get(blockIns); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java index f2aa345f1..31e6f79e8 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/network/NetworkManager.java @@ -3,8 +3,6 @@ package net.momirealms.craftengine.core.plugin.network; import io.netty.channel.Channel; import net.momirealms.craftengine.core.entity.player.Player; -import java.util.Collection; - public interface NetworkManager { String MOD_CHANNEL = "craftengine:payload";