diff --git a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java index 087d08bc3..e1d51a0e9 100644 --- a/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java +++ b/bukkit/compatibility/src/main/java/net/momirealms/craftengine/bukkit/compatibility/BukkitCompatibilityManager.java @@ -13,6 +13,7 @@ import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEn import net.momirealms.craftengine.bukkit.compatibility.model.modelengine.ModelEngineUtils; import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicItemDropListener; import net.momirealms.craftengine.bukkit.compatibility.mythicmobs.MythicSkillHelper; +import net.momirealms.craftengine.bukkit.compatibility.packetevents.WrappedBlockStateHelper; import net.momirealms.craftengine.bukkit.compatibility.papi.PlaceholderAPIUtils; import net.momirealms.craftengine.bukkit.compatibility.permission.LuckPermsEventListeners; import net.momirealms.craftengine.bukkit.compatibility.quickshop.QuickShopItemExpressionHandler; @@ -141,6 +142,22 @@ public class BukkitCompatibilityManager implements CompatibilityManager[] BY_STRING = (Map[]) methodHandle$WrappedBlockState$BY_STRING$getter.invoke(); + Map[] BY_ID = (Map[]) methodHandle$WrappedBlockState$BY_ID$getter.invoke(); + Map[] INTO_STRING = (Map[]) methodHandle$WrappedBlockState$INTO_STRING$getter.invoke(); + Map[] INTO_ID = (Map[]) methodHandle$WrappedBlockState$INTO_ID$getter.invoke(); + Map[] DEFAULT_STATES = (Map[]) methodHandle$WrappedBlockState$DEFAULT_STATES$getter.invoke(); + Map stringWrappedBlockStateMap = BY_STRING[mappingsIndex]; + Map integerWrappedBlockStateMap = BY_ID[mappingsIndex]; + Map wrappedBlockStateStringMap = INTO_STRING[mappingsIndex]; + Map wrappedBlockStateIntegerMap = INTO_ID[mappingsIndex]; + Map stateTypeWrappedBlockStateMap = DEFAULT_STATES[mappingsIndex]; + Object typesBuilder = methodHandle$StateTypes$REGISTRY$getTypesBuilder.invoke(methodHandle$StateTypes$REGISTRY$getter.invoke()); + methodHandle$StateTypes$REGISTRY$load.invoke(typesBuilder); + for (int i = 0; i < Config.serverSideBlocks(); i++) { + String blockId = "craftengine:custom_" + i; + int id = BlockStateUtils.vanillaBlockStateCount() + i; + Object stateType = methodHandle$StateTypes$builder$build.invoke( + methodHandle$StateTypes$builder$setMaterial.invoke( + methodHandle$StateTypes$builder$isBlocking.invoke( + methodHandle$StateTypes$builder$name.invoke( + methodHandle$StateTypes$builder.invoke(), + blockId + ), true + ), instance$MaterialType$STONE + ) + ); + Object wrappedBlockState = methodHandle$WrappedBlockState$constructor.invoke(stateType, Collections.emptyMap(), id, mappingsIndex); + stringWrappedBlockStateMap.put(blockId, wrappedBlockState); + integerWrappedBlockStateMap.put(id, wrappedBlockState); + wrappedBlockStateStringMap.put(wrappedBlockState, blockId); + wrappedBlockStateIntegerMap.put(wrappedBlockState, id); + stateTypeWrappedBlockStateMap.put(stateType, wrappedBlockState); + } + methodHandle$StateTypes$REGISTRY$unloadFileMappings.invoke(typesBuilder); + } + + private static void init(@Nullable String packageName) throws Throwable { + packageName = (packageName != null ? packageName : "com{}github{}retrooper{}packetevents").replace("{}", "."); + Class clazz$WrappedBlockState = Class.forName(packageName + ".protocol.world.states.WrappedBlockState"); + Class clazz$PacketEvents = Class.forName(packageName + ".PacketEvents"); + Class clazz$PacketEventsAPI = Class.forName(packageName + ".PacketEventsAPI"); + Class clazz$ServerManager = Class.forName(packageName + ".manager.server.ServerManager"); + Class clazz$ServerVersion = Class.forName(packageName + ".manager.server.ServerVersion"); + Class clazz$ClientVersion = Class.forName(packageName + ".protocol.player.ClientVersion"); + Class clazz$StateType = Class.forName(packageName + ".protocol.world.states.type.StateType"); + Class clazz$StateTypes = Class.forName(packageName + ".protocol.world.states.type.StateTypes"); + Class clazz$StateTypes$Builder = Class.forName(packageName + ".protocol.world.states.type.StateTypes$Builder"); + Class clazz$MaterialType = Class.forName(packageName + ".protocol.world.MaterialType"); + Class clazz$VersionedRegistry = Class.forName(packageName + ".util.mappings.VersionedRegistry"); + Class clazz$TypesBuilder = Class.forName(packageName + ".util.mappings.TypesBuilder"); + MethodHandle methodHandle$PacketEvents$getAPI = ReflectionUtils.unreflectMethod(clazz$PacketEvents.getDeclaredMethod("getAPI")); + MethodHandle methodHandle$PacketEventsAPI$getServerManager = ReflectionUtils.unreflectMethod(clazz$PacketEventsAPI.getDeclaredMethod("getServerManager")); + MethodHandle methodHandle$ServerManager$getVersion = ReflectionUtils.unreflectMethod(clazz$ServerManager.getDeclaredMethod("getVersion")); + MethodHandle methodHandle$ServerVersion$toClientVersion = ReflectionUtils.unreflectMethod(clazz$ServerVersion.getDeclaredMethod("toClientVersion")); + methodHandle$WrappedBlockState$BY_STRING$getter = ReflectionUtils.unreflectGetter(clazz$WrappedBlockState.getDeclaredField("BY_STRING")); + methodHandle$WrappedBlockState$BY_ID$getter = ReflectionUtils.unreflectGetter(clazz$WrappedBlockState.getDeclaredField("BY_ID")); + methodHandle$WrappedBlockState$INTO_STRING$getter = ReflectionUtils.unreflectGetter(clazz$WrappedBlockState.getDeclaredField("INTO_STRING")); + methodHandle$WrappedBlockState$INTO_ID$getter = ReflectionUtils.unreflectGetter(clazz$WrappedBlockState.getDeclaredField("INTO_ID")); + methodHandle$WrappedBlockState$DEFAULT_STATES$getter = ReflectionUtils.unreflectGetter(clazz$WrappedBlockState.getDeclaredField("DEFAULT_STATES")); + methodHandle$WrappedBlockState$loadMappings = ReflectionUtils.unreflectMethod(clazz$WrappedBlockState.getDeclaredMethod("loadMappings", clazz$ClientVersion)); + methodHandle$WrappedBlockState$constructor = ReflectionUtils.unreflectConstructor(clazz$WrappedBlockState.getDeclaredConstructor(clazz$StateType, Map.class, int.class, byte.class)); + methodHandle$StateTypes$builder = ReflectionUtils.unreflectMethod(clazz$StateTypes.getDeclaredMethod("builder")); + methodHandle$StateTypes$builder$name = ReflectionUtils.unreflectMethod(clazz$StateTypes$Builder.getDeclaredMethod("name", String.class)); + methodHandle$StateTypes$builder$isBlocking = ReflectionUtils.unreflectMethod(clazz$StateTypes$Builder.getDeclaredMethod("isBlocking", boolean.class)); + methodHandle$StateTypes$builder$setMaterial = ReflectionUtils.unreflectMethod(clazz$StateTypes$Builder.getDeclaredMethod("setMaterial", clazz$MaterialType)); + methodHandle$StateTypes$builder$build = ReflectionUtils.unreflectMethod(clazz$StateTypes$Builder.getDeclaredMethod("build")); + methodHandle$StateTypes$REGISTRY$getter = ReflectionUtils.unreflectGetter(clazz$StateTypes.getDeclaredField("REGISTRY")); + methodHandle$StateTypes$REGISTRY$getTypesBuilder = ReflectionUtils.unreflectMethod(clazz$VersionedRegistry.getDeclaredMethod("getTypesBuilder")); + methodHandle$StateTypes$REGISTRY$load = ReflectionUtils.unreflectMethod(clazz$TypesBuilder.getDeclaredMethod("load")); + methodHandle$StateTypes$REGISTRY$unloadFileMappings = ReflectionUtils.unreflectMethod(clazz$TypesBuilder.getDeclaredMethod("unloadFileMappings")); + instance$MaterialType$STONE = clazz$MaterialType.getDeclaredField("STONE").get(null); + clientVersion = methodHandle$ServerVersion$toClientVersion.invoke(methodHandle$ServerManager$getVersion.invoke(methodHandle$PacketEventsAPI$getServerManager.invoke(methodHandle$PacketEvents$getAPI.invoke()))); + } +} diff --git a/bukkit/paper-loader/build.gradle.kts b/bukkit/paper-loader/build.gradle.kts index c533228d1..55924d350 100644 --- a/bukkit/paper-loader/build.gradle.kts +++ b/bukkit/paper-loader/build.gradle.kts @@ -78,6 +78,10 @@ paper { register("ViaVersion") { required = false } register("QuickShop-Hikari") { required = false } + // PacketEvents + register("GrimAC") { required = false } + register("packetevents") { required = false } + // Geyser register("Geyser-Spigot") { required = false } register("floodgate") { required = false } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java index 732aeb27e..ad8fb235b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/SlabBlockBehavior.java @@ -9,6 +9,7 @@ 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.behavior.CanBeReplacedBlockBehavior; import net.momirealms.craftengine.core.block.behavior.IsPathFindableBlockBehavior; import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.block.properties.type.SlabType; @@ -25,7 +26,7 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.Callable; -public class SlabBlockBehavior extends BukkitBlockBehavior implements IsPathFindableBlockBehavior { +public class SlabBlockBehavior extends BukkitBlockBehavior implements IsPathFindableBlockBehavior, CanBeReplacedBlockBehavior { public static final Factory FACTORY = new Factory(); private final Property typeProperty; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java index 933e2b3de..f569d013b 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/StackableBlockBehavior.java @@ -1,100 +1,87 @@ package net.momirealms.craftengine.bukkit.block.behavior; import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils; 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.UpdateOption; import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.behavior.CanBeReplacedBlockBehavior; import net.momirealms.craftengine.core.block.properties.IntegerProperty; -import net.momirealms.craftengine.core.entity.player.InteractionHand; -import net.momirealms.craftengine.core.entity.player.InteractionResult; -import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.item.Item; -import net.momirealms.craftengine.core.item.context.UseOnContext; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; -import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.util.ItemUtils; 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.BlockPos; -import net.momirealms.craftengine.core.world.Vec3d; -import net.momirealms.craftengine.core.world.World; -import org.bukkit.Location; -import org.bukkit.inventory.ItemStack; import java.util.List; import java.util.Map; -import java.util.Optional; -public class StackableBlockBehavior extends BukkitBlockBehavior { +public class StackableBlockBehavior extends BukkitBlockBehavior implements CanBeReplacedBlockBehavior { public static final Factory FACTORY = new Factory(); private final IntegerProperty amountProperty; private final List items; - private final SoundData stackSound; + private final String propertyName; - public StackableBlockBehavior(CustomBlock block, IntegerProperty amountProperty, List items, SoundData stackSound) { + public StackableBlockBehavior(CustomBlock block, IntegerProperty amountProperty, List items, String propertyName) { super(block); this.amountProperty = amountProperty; this.items = items; - this.stackSound = stackSound; + this.propertyName = propertyName; } @Override - @SuppressWarnings("unchecked") - public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { - Player player = context.getPlayer(); - if (player == null || player.isSecondaryUseActive()) { - return InteractionResult.PASS; + public boolean canBeReplaced(BlockPlaceContext context, ImmutableBlockState state) { + if (super.canBeReplaced(context, state)) { + return true; } - Item item = (Item) context.getItem(); + if (context.isSecondaryUseActive()) { + return false; + } + Item item = context.getItem(); if (ItemUtils.isEmpty(item)) { - return InteractionResult.PASS; + return false; } if (!this.items.contains(item.id())) { - return InteractionResult.PASS; + return false; } - BlockPos pos = context.getClickedPos(); - World world = context.getLevel(); - if (state.get(this.amountProperty) >= this.amountProperty.max) { - return InteractionResult.SUCCESS_AND_CANCEL; + Property property = state.owner().value().getProperty(this.propertyName); + if (property == null || property.valueClass() != Integer.class) { + return false; } - updateStackableBlock(state, pos, world, item, player, context.getHand()); - return InteractionResult.SUCCESS_AND_CANCEL; + return (Integer) state.get(property) < this.amountProperty.max; } - private void updateStackableBlock(ImmutableBlockState state, BlockPos pos, World world, Item item, Player player, InteractionHand hand) { - ImmutableBlockState nextStage = state.cycle(this.amountProperty); - Location location = new Location((org.bukkit.World) world.platformWorld(), pos.x(), pos.y(), pos.z()); - FastNMS.INSTANCE.method$LevelWriter$setBlock(world.serverWorld(), LocationUtils.toBlockPos(pos), nextStage.customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); - if (this.stackSound != null) { - world.playBlockSound(new Vec3d(location.getX(), location.getY(), location.getZ()), this.stackSound); + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + Object world = context.getLevel().serverWorld(); + Object pos = LocationUtils.toBlockPos(context.getClickedPos()); + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, pos)).orElse(null); + if (blockState == null) { + return state; } - if (!player.isCreativeMode()) { - item.count(item.count() - 1); + Property property = blockState.owner().value().getProperty(this.propertyName); + if (property == null || property.valueClass() != Integer.class) { + return state; } - player.swingHand(hand); + return blockState.cycle(property); } public static class Factory implements BlockBehaviorFactory { @Override - @SuppressWarnings("unchecked") public BlockBehavior create(CustomBlock block, Map arguments) { String propertyName = String.valueOf(arguments.getOrDefault("property", "amount")); IntegerProperty amount = (IntegerProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty(propertyName), () -> { throw new LocalizedResourceConfigException("warning.config.block.behavior.stackable.missing_property", propertyName); }); - Map sounds = (Map) arguments.get("sounds"); - SoundData stackSound = null; - if (sounds != null) { - stackSound = Optional.ofNullable(sounds.get("stack")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.FIXED_1)).orElse(null); - } Object itemsObj = ResourceConfigUtils.requireNonNullOrThrow(arguments.get("items"), "warning.config.block.behavior.stackable.missing_items"); List items = MiscUtils.getAsStringList(itemsObj).stream().map(Key::of).toList(); - return new StackableBlockBehavior(block, amount, items, stackSound); + return new StackableBlockBehavior(block, amount, items, propertyName); } } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java index 3ddde1aa7..30c54d2a7 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/UnsafeCompositeBlockBehavior.java @@ -15,7 +15,7 @@ import java.util.Optional; import java.util.concurrent.Callable; public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior - implements FallOnBlockBehavior, PlaceLiquidBlockBehavior, IsPathFindableBlockBehavior { + implements FallOnBlockBehavior, PlaceLiquidBlockBehavior, IsPathFindableBlockBehavior, CanBeReplacedBlockBehavior { private final AbstractBlockBehavior[] behaviors; public UnsafeCompositeBlockBehavior(CustomBlock customBlock, List behaviors) { @@ -258,8 +258,8 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior @Override public boolean canBeReplaced(BlockPlaceContext context, ImmutableBlockState state) { for (AbstractBlockBehavior behavior : this.behaviors) { - if (!behavior.canBeReplaced(context, state)) { - return false; + if (behavior instanceof CanBeReplacedBlockBehavior canBeReplacedBlockBehavior) { + return canBeReplacedBlockBehavior.canBeReplaced(context, state); } } return super.canBeReplaced(context, state); diff --git a/core/src/main/java/net/momirealms/craftengine/core/block/behavior/CanBeReplacedBlockBehavior.java b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/CanBeReplacedBlockBehavior.java new file mode 100644 index 000000000..5ca734775 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/block/behavior/CanBeReplacedBlockBehavior.java @@ -0,0 +1,9 @@ +package net.momirealms.craftengine.core.block.behavior; + +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; + +public interface CanBeReplacedBlockBehavior { + + boolean canBeReplaced(BlockPlaceContext context, ImmutableBlockState state); +}