feat: Introduce dispatchToComponentsWithDependencyCheck to conditionally dispatch events based on item data components and event handling status.

This commit is contained in:
Kariya 2025-12-12 12:41:26 +00:00
parent b5a51fad96
commit e5d89e41b6

View File

@ -94,7 +94,13 @@ class EventListener(private val plugin: Plugin) : Listener {
for (item in items) {
// AbstractItem interface does not define onToggleSneak.
dispatchToComponents(item) { it.onToggleSneak(player, item, event) }
val handled = dispatchToComponentsWithDependencyCheck(
item = item,
isEventHandled = { event.isCancelled }
) { component ->
component.onToggleSneak(player, item, event)
}
if (handled) break
}
}
@ -104,7 +110,13 @@ class EventListener(private val plugin: Plugin) : Listener {
if (entity is Player) {
val items = getEquipmentItems(entity)
for (item in items) {
dispatchToComponents(item) { it.onToggleGlide(entity, item, event) }
val handled = dispatchToComponentsWithDependencyCheck(
item = item,
isEventHandled = { event.isCancelled }
) { component ->
component.onToggleGlide(entity, item, event)
}
if (handled) break
}
}
}
@ -138,6 +150,52 @@ class EventListener(private val plugin: Plugin) : Listener {
action(abstractItem)
}
/**
* Dispatches to components on an item, but only if the item satisfies
* the component's dataComponentDependencies.
*
* This prevents multiple components with the same trigger conditions
* (e.g., GLIDER) from firing simultaneously when only one item
* actually has the required data component active.
*
* @param item The item to dispatch to
* @param isEventHandled Lambda to check if the event has been handled (e.g., cancelled)
* @param action The action to perform on each eligible component
* @return true if the event was handled by any component, false otherwise
*/
private fun dispatchToComponentsWithDependencyCheck(
item: ItemStack,
isEventHandled: () -> Boolean,
action: (CustomComponent) -> Unit
): Boolean {
if (item.type.isAir) return false
val meta = item.itemMeta ?: return false
val pdc = meta.persistentDataContainer
for (key in pdc.keys) {
// Stop if event is already handled by a previous component
if (isEventHandled()) return true
val component = ComponentRegistry.get(key) ?: continue
// Check if this item has all required data component dependencies
// This ensures the component only triggers on items that
// actually have the necessary data components active
val hasAllDependencies = component.dataComponentDependencies.all { dep ->
item.hasData(dep)
}
if (!hasAllDependencies) continue
action(component)
}
return isEventHandled()
}
/**
* Dispatches to all components on an item without checking dependencies.
* Use this for events that don't rely on data component triggers.
*/
private fun dispatchToComponents(item: ItemStack, action: (CustomComponent) -> Unit) {
if (item.type.isAir) return
val meta = item.itemMeta ?: return