Skip to content

Commit

Permalink
refactor: move away from event-listeners to packets for adding/removi…
Browse files Browse the repository at this point in the history
…ng furniture hitboxes
  • Loading branch information
Boy0000 committed Sep 19, 2024
1 parent ff4b1a5 commit 4991268
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 64 deletions.
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -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" }
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<FurnitureUUID, MutableSet<BlockPos>>()
private val collisionHitboxPosMap = mutableMapOf<FurnitureBaseEntity, MutableSet<BlockPos>>()
private val interactionHitboxIds = mutableSetOf<FurnitureSubEntity>()
private val interactionHitboxPacketMap = mutableMapOf<FurnitureUUID, MutableSet<FurnitureSubEntityPacket>>()
private val outlineIds = mutableSetOf<FurnitureSubEntity>()
Expand All @@ -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.
Expand All @@ -77,8 +89,8 @@ object FurniturePacketHelpers {

val interactionHitboxes = furniture.toGeary().get<BlockyFurniture>()?.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<FurnitureSubEntityPacket>().apply {
interactionHitboxes.zip(entityIds).forEach { (hitbox, entityId) ->
Expand Down Expand Up @@ -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)
}

Expand All @@ -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()))
}

Expand All @@ -131,8 +148,8 @@ object FurniturePacketHelpers {
val interactionHitboxes = furniture.toGeary().get<BlockyFurniture>()?.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) {
Expand Down Expand Up @@ -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)
Expand All @@ -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()))
}
Expand All @@ -208,7 +232,7 @@ object FurniturePacketHelpers {
baseEntity.world.players.forEach {
removeCollisionHitboxPacket(baseEntity, it)
}
collisionHitboxPosMap.remove(baseEntity.uniqueId)
collisionHitboxPosMap.remove(FurnitureBaseEntity(baseEntity))
}

/**
Expand All @@ -222,6 +246,15 @@ object FurniturePacketHelpers {
player.sendMultiBlockChange(positions)
}

fun removeCollisionHitboxPacket(baseEntity: FurnitureId, player: Player) {
gearyMobs.bukkit2Geary[baseEntity]?.get<BlockyFurniture>() ?: 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
Expand Down Expand Up @@ -260,4 +293,13 @@ object FurniturePacketHelpers {

player.sendMultiBlockChange(collisionHitboxPositions)
}

fun removeLightPacket(baseEntity: Int, player: Player) {
gearyMobs.bukkit2Geary[baseEntity]?.get<BlockyFurniture>() ?: 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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -40,41 +53,51 @@ import org.bukkit.util.Vector

class BlockyFurnitureListener : Listener {

@EventHandler(priority = EventPriority.HIGH)
fun PlayerChunkLoadEvent.onLoadChunk() {
chunk.entities.filterIsInstance<ItemDisplay>().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<ItemDisplay>().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<ItemDisplay>().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<in ClientGamePacketListener> })
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
}
}

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 4991268

Please sign in to comment.