Skip to content

Commit

Permalink
feat: rework FurnitureOutline system
Browse files Browse the repository at this point in the history
  • Loading branch information
Boy0000 committed Mar 31, 2024
1 parent f377c81 commit 275d21a
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 64 deletions.
38 changes: 37 additions & 1 deletion src/main/kotlin/com/mineinabyss/blocky/BlockyConfig.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
package com.mineinabyss.blocky

import com.charleskorn.kaml.YamlComment
import com.mineinabyss.blocky.helpers.FurnitureOutlineType
import com.mineinabyss.idofront.items.editItemMeta
import com.mineinabyss.idofront.serialization.SerializableItemStack
import com.mineinabyss.idofront.serialization.toSerializable
import com.mineinabyss.idofront.textcomponents.miniMsg
import com.ticxo.modelengine.api.entity.Hitbox
import kotlinx.serialization.Serializable
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.syncher.EntityDataSerializers
import net.minecraft.network.syncher.SynchedEntityData
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.Blocks
import org.bukkit.Material
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack
import org.bukkit.entity.EntityType
import org.bukkit.inventory.ItemStack

@Serializable
Expand Down Expand Up @@ -38,7 +47,34 @@ data class BlockyConfig(
@Serializable data class BlockyCaveVineConfig(val isEnabled: Boolean = false)
@Serializable data class BlockySlabConfig(val isEnabled: Boolean = false)
@Serializable data class BlockyStairConfig(val isEnabled: Boolean = false)
@Serializable data class BlockyFurnitureConfig(val showHitboxOutline: Boolean = false, val worldEdit: Boolean = false)
@Serializable data class BlockyFurnitureConfig(
val hitboxOutlines: HitboxOutline = HitboxOutline(),
val worldEdit: Boolean = false
) {
fun showOutlines() = hitboxOutlines.type != FurnitureOutlineType.NONE
@Serializable
data class HitboxOutline(
val type: FurnitureOutlineType = FurnitureOutlineType.ITEM,
val item: SerializableItemStack = ItemStack(Material.GLASS).toSerializable()
) {
fun entityType(): net.minecraft.world.entity.EntityType<*>? {
return when (type) {
FurnitureOutlineType.ITEM -> net.minecraft.world.entity.EntityType.ITEM_DISPLAY
FurnitureOutlineType.BLOCK -> net.minecraft.world.entity.EntityType.BLOCK_DISPLAY
else -> null
}
}
fun outlineContent(): SynchedEntityData.DataValue<*>? {
return when (type) {
FurnitureOutlineType.ITEM ->
SynchedEntityData.DataValue(23, EntityDataSerializers.ITEM_STACK, CraftItemStack.asNMSCopy(item.toItemStack()))
FurnitureOutlineType.BLOCK ->
SynchedEntityData.DataValue(23, EntityDataSerializers.BLOCK_STATE, Block.byItem(CraftItemStack.asNMSCopy(item.toItemStack()).item).defaultBlockState())
else -> null
}
}
}
}
@Serializable data class DefaultBlockyMenu(
val title: String = "",
val height: Int = 5,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
package com.mineinabyss.blocky.components.core

import com.mineinabyss.blocky.helpers.GenericHelpers.toBlockCenterLocation
import com.mineinabyss.blocky.serializers.BrightnessSerializer
import com.mineinabyss.idofront.serialization.ColorSerializer
import com.mineinabyss.idofront.serialization.SerializableItemStack
import com.mineinabyss.idofront.serialization.Vector3fSerializer
import com.mineinabyss.idofront.serialization.toSerializable
import com.mineinabyss.idofront.serialization.*
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import org.bukkit.Location
import org.bukkit.Material
import org.bukkit.entity.Display.Billboard
import org.bukkit.entity.Display.Brightness
import org.bukkit.entity.ItemDisplay
import org.bukkit.entity.ItemDisplay.ItemDisplayTransform
import org.bukkit.inventory.ItemStack
import org.bukkit.util.BoundingBox
import org.bukkit.util.Vector
import org.joml.Vector3f
import kotlin.math.cos
import kotlin.math.round
Expand All @@ -30,11 +30,26 @@ data class BlockyFurniture(
@Serializable
@SerialName("blocky:interaction_hitbox")
data class InteractionHitbox(
val originOffset: BlockLocation = BlockLocation(),
val offset: @Serializable(VectorSerializer::class) Vector = Vector(),
val width: Float,
val height: Float,
val outline: SerializableItemStack = ItemStack(Material.GLASS).toSerializable()) {
fun toBoundingBox(location: Location) = BoundingBox.of(location, width.times(0.7), height.times(0.7), width.times(0.7))
fun location(furniture: ItemDisplay): Location {
return furniture.location.toBlockCenterLocation().add(offset(furniture.yaw))
}

fun offset(furnitureYaw: Float): Vector {
val angleRad = Math.toRadians(furnitureYaw.toDouble())


// Get the coordinates relative to the local y-axis
val x = cos(angleRad) * offset.x + sin(angleRad) * offset.z
val y = offset.y
val z = sin(angleRad) * offset.x + cos(angleRad) * offset.z

return Vector(x, y, z)
}
}

@JvmInline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ object FurnitureHelpers {
center: Location,
hitbox: Set<BlockyFurniture.InteractionHitbox>
): List<Location> =
hitbox.map { c -> c.originOffset.groundRotate(rotation).add(center) }
hitbox.map { i -> center.clone().add(i.offset(rotation)) }

fun rotation(yaw: Float, nullFurniture: BlockyFurniture?): Rotation {
val furniture = nullFurniture ?: BlockyFurniture()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.mineinabyss.blocky.helpers

enum class FurnitureOutlineType {
ITEM, BLOCK, NONE;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import com.comphenix.protocol.events.PacketContainer
import com.comphenix.protocol.wrappers.BlockPosition
import com.mineinabyss.blocky.api.BlockyFurnitures.isBlockyFurniture
import com.mineinabyss.blocky.api.BlockyFurnitures.isModelEngineFurniture
import com.mineinabyss.blocky.blocky
import com.mineinabyss.blocky.components.core.BlockyFurniture
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.toBlockCenterLocation
import com.mineinabyss.blocky.helpers.GenericHelpers.toEntity
import com.mineinabyss.geary.papermc.tracking.entities.toGeary
import com.mineinabyss.protocolburrito.dsl.sendTo
Expand All @@ -36,24 +36,25 @@ import java.util.*
* Typealias to make it clear that this is a UUID for a furniture entity.
*/
typealias FurnitureUUID = UUID
data class FurnitureInteractionHitboxIds(val furnitureUUID: FurnitureUUID, val entityIds: IntList) {
data class FurnitureSubEntity(val furnitureUUID: FurnitureUUID, val entityIds: IntList) {
val furniture get() = furnitureUUID.toEntity() as? ItemDisplay
}
data class FurnitureInteractionHitboxPacket(val entityId: Int, val addEntity: ClientboundAddEntityPacket, val metadata: ClientboundSetEntityDataPacket)
data class FurnitureSubEntityPacket(val entityId: Int, val addEntity: ClientboundAddEntityPacket, val metadata: ClientboundSetEntityDataPacket)
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<BlockPosition>>()
private val interactionHitboxIdMap = mutableSetOf<FurnitureInteractionHitboxIds>()
private val interactionHitboxPacketMap = mutableMapOf<FurnitureUUID, MutableSet<FurnitureInteractionHitboxPacket>>()
private val hitboxOutlineIdMap = mutableMapOf<FurnitureUUID, IntList>()
private val interactionHitboxIds = mutableSetOf<FurnitureSubEntity>()
private val interactionHitboxPacketMap = mutableMapOf<FurnitureUUID, MutableSet<FurnitureSubEntityPacket>>()
private val outlineIds = mutableSetOf<FurnitureSubEntity>()
private val outlinePacketMap = mutableMapOf<FurnitureUUID, MutableSet<FurnitureSubEntityPacket>>()
private val outlinePlayerMap = mutableMapOf<UUID, UUID>()

fun baseFurnitureFromInteractionHitbox(id: Int) =
interactionHitboxIdMap.firstOrNull { id in it.entityIds }?.furniture
interactionHitboxIds.firstOrNull { id in it.entityIds }?.furniture

fun baseFurnitureFromCollisionHitbox(pos: BlockPosition) =
collisionHitboxPosMap.entries.firstOrNull { pos in it.value }?.key?.toEntity() as? ItemDisplay
Expand All @@ -72,14 +73,13 @@ object FurniturePacketHelpers {
}

val interactionHitboxes = furniture.toGeary().get<BlockyFurniture>()?.interactionHitbox ?: return
val baseLoc = furniture.location.toBlockCenterLocation()
interactionHitboxPacketMap.computeIfAbsent(furniture.uniqueId) {
val entityIds = interactionHitboxIdMap.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: List(interactionHitboxes.size) { Entity.nextEntityId() }.apply {
interactionHitboxIdMap.add(FurnitureInteractionHitboxIds(furniture.uniqueId, IntList.of(*toIntArray())))
val entityIds = interactionHitboxIds.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: List(interactionHitboxes.size) { Entity.nextEntityId() }.apply {
interactionHitboxIds += FurnitureSubEntity(furniture.uniqueId, IntList.of(*toIntArray()))
}
mutableSetOf<FurnitureInteractionHitboxPacket>().apply {
mutableSetOf<FurnitureSubEntityPacket>().apply {
interactionHitboxes.zip(entityIds).forEach { (hitbox, entityId) ->
val loc = hitbox.originOffset.groundRotate(furniture.yaw).add(baseLoc)
val loc = hitbox.location(furniture)
val addEntityPacket = ClientboundAddEntityPacket(
entityId, UUID.randomUUID(),
loc.x, loc.y, loc.z, loc.pitch, loc.yaw,
Expand All @@ -93,7 +93,7 @@ object FurniturePacketHelpers {
)
)

add(FurnitureInteractionHitboxPacket(entityId, addEntityPacket, metadataPacket))
add(FurnitureSubEntityPacket(entityId, addEntityPacket, metadataPacket))
}
}
}.forEach {
Expand All @@ -110,7 +110,7 @@ object FurniturePacketHelpers {
furniture.world.players.forEach { player ->
removeInteractionHitboxPacket(furniture, player)
}
interactionHitboxIdMap.removeIf { it.furnitureUUID == furniture.uniqueId }
interactionHitboxIds.removeIf { it.furnitureUUID == furniture.uniqueId }
interactionHitboxPacketMap.remove(furniture.uniqueId)
}

Expand All @@ -119,7 +119,7 @@ object FurniturePacketHelpers {
* @param furniture The furniture to remove the interaction hitbox of.
*/
fun removeInteractionHitboxPacket(furniture: ItemDisplay, player: Player) {
val entityIds = interactionHitboxIdMap.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: return
val entityIds = interactionHitboxIds.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: return
PacketContainer.fromPacket(ClientboundRemoveEntitiesPacket(*entityIds.toIntArray())).sendTo(player)
}

Expand All @@ -129,25 +129,39 @@ object FurniturePacketHelpers {
outlinePlayerMap[player.uniqueId] = furniture.uniqueId

val interactionHitboxes = furniture.toGeary().get<BlockyFurniture>()?.interactionHitbox ?: return
val entityIds = hitboxOutlineIdMap.computeIfAbsent(furniture.uniqueId) { IntList.of(*IntArray(interactionHitboxes.size) { Entity.nextEntityId() }) }
val baseLoc = furniture.location.toBlockCenterLocation()

interactionHitboxes.zip(entityIds).forEach { (hitbox, entityId) ->
val loc = hitbox.originOffset.groundRotate(furniture.yaw).add(baseLoc).apply { y += hitbox.height / 2 }
val displayEntityPacket = ClientboundAddEntityPacket(
entityId, UUID.randomUUID(),
loc.x, loc.y, loc.z, loc.pitch, loc.yaw,
EntityType.ITEM_DISPLAY, 0, Vec3.ZERO, 0.0
)
PacketContainer.fromPacket(displayEntityPacket).sendTo(player)
val metadataPacket = ClientboundSetEntityDataPacket(
entityId, listOf(
SynchedEntityData.DataValue(12, EntityDataSerializers.VECTOR3, Vector3f(hitbox.width, hitbox.height, hitbox.width)),
SynchedEntityData.DataValue(23, EntityDataSerializers.ITEM_STACK, CraftItemStack.asNMSCopy(hitbox.outline.toItemStack())),
SynchedEntityData.DataValue(24, EntityDataSerializers.INT, furniture.itemDisplayTransform.ordinal)
)
)
PacketContainer.fromPacket(metadataPacket).sendTo(player)
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()))
}

outlinePacketMap.computeIfAbsent(furniture.uniqueId) {
mutableSetOf<FurnitureSubEntityPacket>().apply {
interactionHitboxes.zip(entityIds).forEach { (hitbox, entityId) ->
val loc = hitbox.location(furniture).add(0.0,hitbox.height / 2.0, 0.0).apply {
if (blocky.config.furniture.hitboxOutlines.type == FurnitureOutlineType.BLOCK)
toBlockLocation()
}
val addEntityPacket = ClientboundAddEntityPacket(
entityId, UUID.randomUUID(),
loc.x, loc.y, loc.z, 0.0f, 0.0f,
outlineType, 0, Vec3.ZERO, 0.0
)

val metadataPacket = ClientboundSetEntityDataPacket(
entityId, listOf(
outlineContent,
SynchedEntityData.DataValue(12, EntityDataSerializers.VECTOR3, Vector3f(hitbox.width, hitbox.height, hitbox.width)),
SynchedEntityData.DataValue(24, EntityDataSerializers.INT, furniture.itemDisplayTransform.ordinal)
)
)

add(FurnitureSubEntityPacket(entityId, addEntityPacket, metadataPacket))
}
}
}.forEach {
PacketContainer.fromPacket(it.addEntity).sendTo(player)
PacketContainer.fromPacket(it.metadata).sendTo(player)
}
}

Expand All @@ -158,15 +172,15 @@ object FurniturePacketHelpers {
}

fun removeHitboxOutlinePacket(furniture: ItemDisplay, player: Player) {
val displayEntityPacket = ClientboundRemoveEntitiesPacket(hitboxOutlineIdMap[furniture.uniqueId] ?: return)
val displayEntityPacket = ClientboundRemoveEntitiesPacket(outlineIds.firstOrNull { it.furnitureUUID == furniture.uniqueId }?.entityIds ?: return)
PacketContainer.fromPacket(displayEntityPacket).sendTo(player)
hitboxOutlineIdMap.remove(furniture.uniqueId)
outlineIds.removeIf { it.furnitureUUID == furniture.uniqueId }
outlinePlayerMap.remove(player.uniqueId)
}

fun removeHitboxOutlinePacket(player: Player) {
val furniture = outlinePlayerMap[player.uniqueId]?.toEntity() ?: return
val displayEntityPacket = ClientboundRemoveEntitiesPacket(hitboxOutlineIdMap[furniture.uniqueId] ?: return)
val entityIds = outlineIds.firstOrNull { it.furnitureUUID == (outlinePlayerMap[player.uniqueId] ?: return) }?.entityIds ?: return
val displayEntityPacket = ClientboundRemoveEntitiesPacket(entityIds)
PacketContainer.fromPacket(displayEntityPacket).sendTo(player)
outlinePlayerMap.remove(player.uniqueId)
}
Expand Down
Loading

0 comments on commit 275d21a

Please sign in to comment.