From 4991268bc59feb281073a8a6f1a202755058d7a8 Mon Sep 17 00:00:00 2001 From: Boy Date: Thu, 19 Sep 2024 16:33:47 +0200 Subject: [PATCH] refactor: move away from event-listeners to packets for adding/removing furniture hitboxes --- gradle/libs.versions.toml | 4 +- .../blocky/helpers/FurnitureHelpers.kt | 5 + .../blocky/helpers/FurniturePacketHelpers.kt | 74 +++++++++++--- .../listeners/BlockyFurnitureListener.kt | 99 ++++++++++--------- 4 files changed, 118 insertions(+), 64 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fc85abd4..d415bba1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -gearyPaper = "0.30.14" -guiy="0.12.4-dev.1" +gearyPaper = "0.30.18" +guiy="0.12.4" [libraries] geary-papermc = { module = "com.mineinabyss:geary-papermc", version.ref = "gearyPaper" } diff --git a/src/main/kotlin/com/mineinabyss/blocky/helpers/FurnitureHelpers.kt b/src/main/kotlin/com/mineinabyss/blocky/helpers/FurnitureHelpers.kt index e9570e48..7133d38a 100644 --- a/src/main/kotlin/com/mineinabyss/blocky/helpers/FurnitureHelpers.kt +++ b/src/main/kotlin/com/mineinabyss/blocky/helpers/FurnitureHelpers.kt @@ -1,5 +1,6 @@ package com.mineinabyss.blocky.helpers +import com.mineinabyss.blocky.blocky import com.mineinabyss.blocky.components.core.BlockyFurniture import com.mineinabyss.blocky.components.features.BlockyDrops import com.mineinabyss.blocky.components.features.furniture.BlockyAssociatedSeats @@ -17,6 +18,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import org.bukkit.Location +import org.bukkit.NamespacedKey import org.bukkit.Rotation import org.bukkit.block.Block import org.bukkit.block.BlockFace @@ -28,6 +30,9 @@ import org.bukkit.entity.Player import org.bukkit.inventory.ItemStack object FurnitureHelpers { + + val PACKET_KEY = NamespacedKey.fromString("furniture_packet_listener", blocky.plugin)!! + fun targetBlock(placedAgainst: Block, blockFace: BlockFace): Block? { return if (placedAgainst.isReplaceable) placedAgainst else placedAgainst.getRelative(blockFace).takeUnless { !it.type.isAir && it.isReplaceable } diff --git a/src/main/kotlin/com/mineinabyss/blocky/helpers/FurniturePacketHelpers.kt b/src/main/kotlin/com/mineinabyss/blocky/helpers/FurniturePacketHelpers.kt index 7f4f5a0d..d11c9813 100644 --- a/src/main/kotlin/com/mineinabyss/blocky/helpers/FurniturePacketHelpers.kt +++ b/src/main/kotlin/com/mineinabyss/blocky/helpers/FurniturePacketHelpers.kt @@ -10,8 +10,14 @@ import com.mineinabyss.blocky.components.features.BlockyLight import com.mineinabyss.blocky.components.features.furniture.BlockyModelEngine import com.mineinabyss.blocky.helpers.FurnitureHelpers.collisionHitboxPositions import com.mineinabyss.blocky.helpers.GenericHelpers.toEntity +import com.mineinabyss.geary.datatypes.GearyEntity +import com.mineinabyss.geary.modules.geary +import com.mineinabyss.geary.papermc.tracking.entities.BukkitEntity2Geary +import com.mineinabyss.geary.papermc.tracking.entities.gearyMobs import com.mineinabyss.geary.papermc.tracking.entities.toGeary +import com.mineinabyss.idofront.nms.aliases.toNMS import com.ticxo.modelengine.api.ModelEngineAPI +import io.papermc.paper.math.Position import it.unimi.dsi.fastutil.ints.IntList import net.minecraft.core.BlockPos import net.minecraft.network.protocol.game.ClientboundAddEntityPacket @@ -35,21 +41,27 @@ import java.util.* * Typealias to make it clear that this is a UUID for a furniture entity. */ typealias FurnitureUUID = UUID -data class FurnitureSubEntity(val furnitureUUID: FurnitureUUID, val entityIds: IntList) { - val furniture get() = furnitureUUID.toEntity() as? ItemDisplay +typealias FurnitureId = Int +data class FurnitureBaseEntity(val uuid: FurnitureUUID, val id: FurnitureId) { + constructor(entity: ItemDisplay) : this(entity.uniqueId, entity.entityId) } -data class FurnitureSubEntityPacket(val entityId: Int, val addEntity: ClientboundAddEntityPacket, val metadata: ClientboundSetEntityDataPacket) { + +data class FurnitureSubEntity(val baseEntity: FurnitureBaseEntity, val entityIds: IntList) { + val furniture get() = baseEntity.uuid.toEntity() as? ItemDisplay +} +data class FurnitureSubEntityPacket(val entityId: FurnitureId, val addEntity: ClientboundAddEntityPacket, val metadata: ClientboundSetEntityDataPacket) { fun bundlePacket(): ClientboundBundlePacket { return ClientboundBundlePacket(listOf(addEntity, metadata)) } } + object FurniturePacketHelpers { const val INTERACTION_WIDTH_ID = 8 const val INTERACTION_HEIGHT_ID = 9 const val ITEM_DISPLAY_ITEMSTACK_ID = 23 - private val collisionHitboxPosMap = mutableMapOf>() + private val collisionHitboxPosMap = mutableMapOf>() private val interactionHitboxIds = mutableSetOf() private val interactionHitboxPacketMap = mutableMapOf>() private val outlineIds = mutableSetOf() @@ -60,7 +72,7 @@ object FurniturePacketHelpers { interactionHitboxIds.firstOrNull { id in it.entityIds }?.furniture fun baseFurnitureFromCollisionHitbox(pos: BlockPos) = - collisionHitboxPosMap.entries.firstOrNull { pos in it.value }?.key?.toEntity() as? ItemDisplay + collisionHitboxPosMap.entries.firstOrNull { pos in it.value }?.key?.uuid?.toEntity() as? ItemDisplay /** * Sends a packet to show the interaction hitbox of the given furniture to the given player. @@ -77,8 +89,8 @@ object FurniturePacketHelpers { val interactionHitboxes = furniture.toGeary().get()?.interactionHitbox ?: return interactionHitboxPacketMap.computeIfAbsent(furniture.uniqueId) { - val entityIds = interactionHitboxIds.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: List(interactionHitboxes.size) { Entity.nextEntityId() }.apply { - interactionHitboxIds += FurnitureSubEntity(furniture.uniqueId, IntList.of(*toIntArray())) + val entityIds = interactionHitboxIds.firstOrNull { it.baseEntity.uuid == furniture.uniqueId }?.entityIds ?: List(interactionHitboxes.size) { Entity.nextEntityId() }.apply { + interactionHitboxIds += FurnitureSubEntity(FurnitureBaseEntity(furniture), IntList.of(*toIntArray())) } mutableSetOf().apply { interactionHitboxes.zip(entityIds).forEach { (hitbox, entityId) -> @@ -110,7 +122,7 @@ object FurniturePacketHelpers { furniture.world.players.forEach { player -> removeInteractionHitboxPacket(furniture, player) } - interactionHitboxIds.removeIf { it.furnitureUUID == furniture.uniqueId } + interactionHitboxIds.removeIf { it.baseEntity.uuid == furniture.uniqueId } interactionHitboxPacketMap.remove(furniture.uniqueId) } @@ -119,7 +131,12 @@ object FurniturePacketHelpers { * @param furniture The furniture to remove the interaction hitbox of. */ fun removeInteractionHitboxPacket(furniture: ItemDisplay, player: Player) { - val entityIds = interactionHitboxIds.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: return + val entityIds = interactionHitboxIds.firstOrNull { it.baseEntity.uuid == furniture.uniqueId }?.entityIds ?: return + (player as CraftPlayer).handle.connection.send(ClientboundRemoveEntitiesPacket(*entityIds.toIntArray())) + } + + fun removeInteractionHitboxPacket(furniture: Int, player: Player) { + val entityIds = interactionHitboxIds.firstOrNull { it.baseEntity.id == furniture }?.entityIds ?: return (player as CraftPlayer).handle.connection.send(ClientboundRemoveEntitiesPacket(*entityIds.toIntArray())) } @@ -131,8 +148,8 @@ object FurniturePacketHelpers { val interactionHitboxes = furniture.toGeary().get()?.interactionHitbox ?: return val outlineType = blocky.config.furniture.hitboxOutlines.entityType() ?: return val outlineContent = blocky.config.furniture.hitboxOutlines.outlineContent() ?: return - val entityIds = outlineIds.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: List(interactionHitboxes.size) { Entity.nextEntityId() }.apply { - outlineIds += FurnitureSubEntity(furniture.uniqueId, IntList.of(*toIntArray())) + val entityIds = outlineIds.firstOrNull { it.baseEntity.uuid == furniture.uniqueId }?.entityIds ?: List(interactionHitboxes.size) { Entity.nextEntityId() }.apply { + outlineIds += FurnitureSubEntity(FurnitureBaseEntity(furniture), IntList.of(*toIntArray())) } outlinePacketMap.computeIfAbsent(furniture.uniqueId) { @@ -169,14 +186,21 @@ object FurniturePacketHelpers { } fun removeHitboxOutlinePacket(furniture: ItemDisplay, player: Player) { - val displayEntityPacket = ClientboundRemoveEntitiesPacket(outlineIds.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: return) + val displayEntityPacket = ClientboundRemoveEntitiesPacket(outlineIds.firstOrNull { it.baseEntity.uuid == furniture.uniqueId }?.entityIds ?: return) + (player as CraftPlayer).handle.connection.send(displayEntityPacket) + outlineIds.removeIf { it.baseEntity.uuid == furniture.uniqueId } + outlinePlayerMap.remove(player.uniqueId) + } + + fun removeHitboxOutlinePacket(furniture: FurnitureId, player: Player) { + val displayEntityPacket = ClientboundRemoveEntitiesPacket(outlineIds.firstOrNull { it.baseEntity.id == furniture }?.entityIds ?: return) (player as CraftPlayer).handle.connection.send(displayEntityPacket) - outlineIds.removeIf { it.furnitureUUID == furniture.uniqueId } + outlineIds.removeIf { it.baseEntity.id == furniture } outlinePlayerMap.remove(player.uniqueId) } fun removeHitboxOutlinePacket(player: Player) { - val entityIds = outlineIds.firstOrNull { it.furnitureUUID == (outlinePlayerMap[player.uniqueId] ?: return) }?.entityIds ?: return + val entityIds = outlineIds.firstOrNull { it.baseEntity.uuid == (outlinePlayerMap[player.uniqueId] ?: return) }?.entityIds ?: return val displayEntityPacket = ClientboundRemoveEntitiesPacket(entityIds) (player as CraftPlayer).handle.connection.send(displayEntityPacket) outlinePlayerMap.remove(player.uniqueId) @@ -193,7 +217,7 @@ object FurniturePacketHelpers { .associateWith { Material.BARRIER.createBlockData() }.toMutableMap() player.sendMultiBlockChange(positions) positions.map { it.key.toBlock() }.forEach { - collisionHitboxPosMap.compute(baseEntity.uniqueId) { _, blockPos -> + collisionHitboxPosMap.compute(FurnitureBaseEntity(baseEntity)) { _, blockPos -> blockPos?.plus(BlockPos(it.blockX(), it.blockY(), it.blockZ()))?.toMutableSet() ?: mutableSetOf(BlockPos(it.blockX(), it.blockY(), it.blockZ())) } @@ -208,7 +232,7 @@ object FurniturePacketHelpers { baseEntity.world.players.forEach { removeCollisionHitboxPacket(baseEntity, it) } - collisionHitboxPosMap.remove(baseEntity.uniqueId) + collisionHitboxPosMap.remove(FurnitureBaseEntity(baseEntity)) } /** @@ -222,6 +246,15 @@ object FurniturePacketHelpers { player.sendMultiBlockChange(positions) } + fun removeCollisionHitboxPacket(baseEntity: FurnitureId, player: Player) { + gearyMobs.bukkit2Geary[baseEntity]?.get() ?: return + val positions = collisionHitboxPosMap.entries.firstOrNull { it.key.id == baseEntity }?.value?.map { + Position.block(it.x, it.y, it.z) + }?.associateWith { Material.AIR.createBlockData() }?.toMutableMap() ?: return + + player.sendMultiBlockChange(positions) + } + /** * Sends the light packets for this furniture to a specific player * @param baseEntity The furniture to send the light packets for @@ -260,4 +293,13 @@ object FurniturePacketHelpers { player.sendMultiBlockChange(collisionHitboxPositions) } + + fun removeLightPacket(baseEntity: Int, player: Player) { + gearyMobs.bukkit2Geary[baseEntity]?.get() ?: return + val positions = collisionHitboxPosMap.entries.firstOrNull { it.key.id == baseEntity }?.value?.map { + Position.block(it.x, it.y, it.z) + }?.associateWith { Material.AIR.createBlockData() }?.toMutableMap() ?: return + + player.sendMultiBlockChange(positions) + } } diff --git a/src/main/kotlin/com/mineinabyss/blocky/listeners/BlockyFurnitureListener.kt b/src/main/kotlin/com/mineinabyss/blocky/listeners/BlockyFurnitureListener.kt index 5bd2fc34..684c2e8e 100644 --- a/src/main/kotlin/com/mineinabyss/blocky/listeners/BlockyFurnitureListener.kt +++ b/src/main/kotlin/com/mineinabyss/blocky/listeners/BlockyFurnitureListener.kt @@ -13,16 +13,29 @@ import com.mineinabyss.blocky.components.core.BlockyFurniture import com.mineinabyss.blocky.components.features.BlockyPlacableOn import com.mineinabyss.blocky.components.features.furniture.BlockySeats import com.mineinabyss.blocky.helpers.* +import com.mineinabyss.blocky.helpers.GenericHelpers.toEntity import com.mineinabyss.geary.papermc.tracking.entities.toGearyOrNull import com.mineinabyss.geary.prefabs.PrefabKey +import com.mineinabyss.idofront.nms.PacketListener +import com.mineinabyss.idofront.nms.aliases.toBukkit +import com.mineinabyss.idofront.nms.aliases.toNMS +import com.mineinabyss.idofront.nms.interceptClientbound import com.mineinabyss.idofront.plugin.Plugins import com.mineinabyss.idofront.util.to import com.ticxo.modelengine.api.events.BaseEntityInteractEvent import io.papermc.paper.event.packet.PlayerChunkLoadEvent import io.papermc.paper.event.packet.PlayerChunkUnloadEvent import io.th0rgal.protectionlib.ProtectionLib +import net.minecraft.network.Connection +import net.minecraft.network.protocol.Packet +import net.minecraft.network.protocol.game.ClientGamePacketListener +import net.minecraft.network.protocol.game.ClientboundAddEntityPacket +import net.minecraft.network.protocol.game.ClientboundBundlePacket +import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket +import net.minecraft.server.packs.repository.Pack import org.bukkit.* import org.bukkit.block.BlockFace +import org.bukkit.craftbukkit.CraftServer import org.bukkit.entity.ArmorStand import org.bukkit.entity.ItemDisplay import org.bukkit.entity.Player @@ -40,41 +53,51 @@ import org.bukkit.util.Vector class BlockyFurnitureListener : Listener { - @EventHandler(priority = EventPriority.HIGH) - fun PlayerChunkLoadEvent.onLoadChunk() { - chunk.entities.filterIsInstance().forEach { - FurniturePacketHelpers.sendInteractionEntityPacket(it, player) - FurniturePacketHelpers.sendCollisionHitboxPacket(it, player) - FurniturePacketHelpers.sendLightPacket(it, player) + init { + if (Plugins.isEnabled("ModelEngine")) { + blocky.logger.s("ModelEngine detected, enabling ModelEngine-Furniture-Interaction Listener!") + Bukkit.getPluginManager().registerEvents(object : Listener { + @EventHandler + fun BaseEntityInteractEvent.onModelEngineInteract() { + val baseEntity = (baseEntity.original as? ItemDisplay)?.takeIf { it.isBlockyFurniture } ?: return + when { + action == BaseEntityInteractEvent.Action.ATTACK -> BlockyFurnitures.removeFurniture(baseEntity, player) + else -> BlockyFurnitureInteractEvent(baseEntity, player, slot, player.inventory.itemInMainHand, baseEntity.location.add(clickedPosition ?: Vector())).callEvent() + } + } + }, blocky.plugin) } - } - @EventHandler - fun PlayerChunkUnloadEvent.onUnloadChunk() { - chunk.entities.filterIsInstance().forEach { - FurniturePacketHelpers.removeInteractionHitboxPacket(it, player) - FurniturePacketHelpers.removeHitboxOutlinePacket(it, player) - FurniturePacketHelpers.removeCollisionHitboxPacket(it, player) - FurniturePacketHelpers.removeLightPacket(it, player) + PacketListener.unregisterListener(FurnitureHelpers.PACKET_KEY) + blocky.plugin.interceptClientbound(FurnitureHelpers.PACKET_KEY.asString()) { packet: Packet<*>, player: Player? -> + player?.let { handlePacket(packet, it) } ?: packet } } - @EventHandler(priority = EventPriority.LOWEST) - fun EntityRemoveFromWorldEvent.onRemoveFurniture() { - val entity = entity as? ItemDisplay ?: return - FurniturePacketHelpers.removeInteractionHitboxPacket(entity) - FurniturePacketHelpers.removeHitboxOutlinePacket(entity) - FurniturePacketHelpers.removeCollisionHitboxPacket(entity) - FurniturePacketHelpers.removeLightPacket(entity) - } - - @EventHandler - fun PlayerChangedWorldEvent.onChangeWorld() { - from.entities.filterIsInstance().forEach { - FurniturePacketHelpers.removeInteractionHitboxPacket(it, player) - FurniturePacketHelpers.removeHitboxOutlinePacket(it, player) - FurniturePacketHelpers.removeCollisionHitboxPacket(it, player) - FurniturePacketHelpers.removeLightPacket(it, player) + private fun handlePacket(packet: Packet<*>, player: Player): Packet<*>? { + return when (packet) { + is ClientboundBundlePacket -> ClientboundBundlePacket(packet.subPackets().map { handlePacket(it, player) as Packet }) + is ClientboundAddEntityPacket -> { + blocky.plugin.launch(blocky.plugin.minecraftDispatcher) { + val entity = packet.uuid.toEntity() as? ItemDisplay ?: return@launch + FurniturePacketHelpers.sendInteractionEntityPacket(entity, player) + FurniturePacketHelpers.sendCollisionHitboxPacket(entity, player) + FurniturePacketHelpers.sendLightPacket(entity, player) + } + packet + } + is ClientboundRemoveEntitiesPacket -> { + blocky.plugin.launch(blocky.plugin.minecraftDispatcher) { + packet.entityIds.forEach { entity -> + FurniturePacketHelpers.removeInteractionHitboxPacket(entity, player) + FurniturePacketHelpers.removeCollisionHitboxPacket(entity, player) + FurniturePacketHelpers.removeLightPacket(entity, player) + FurniturePacketHelpers.removeHitboxOutlinePacket(entity, player) + } + } + null + } + else -> packet } } @@ -148,22 +171,6 @@ class BlockyFurnitureListener : Listener { } } - init { - if (Plugins.isEnabled("ModelEngine")) { - blocky.logger.s("ModelEngine detected, enabling ModelEngine-Furniture-Interaction Listener!") - Bukkit.getPluginManager().registerEvents(object : Listener { - @EventHandler - fun BaseEntityInteractEvent.onModelEngineInteract() { - val baseEntity = (baseEntity.original as? ItemDisplay)?.takeIf { it.isBlockyFurniture } ?: return - when { - action == BaseEntityInteractEvent.Action.ATTACK -> BlockyFurnitures.removeFurniture(baseEntity, player) - else -> BlockyFurnitureInteractEvent(baseEntity, player, slot, player.inventory.itemInMainHand, baseEntity.location.add(clickedPosition ?: Vector())).callEvent() - } - } - }, blocky.plugin) - } - } - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) fun BlockyFurnitureInteractEvent.onSitting() { if (!ProtectionLib.canInteract(player, entity.location) || player.isSneaking) return