diff --git a/src/main/kotlin/net/hareworks/hcu/items/listeners/EventListener.kt b/src/main/kotlin/net/hareworks/hcu/items/listeners/EventListener.kt index 10d645f..59fcb69 100644 --- a/src/main/kotlin/net/hareworks/hcu/items/listeners/EventListener.kt +++ b/src/main/kotlin/net/hareworks/hcu/items/listeners/EventListener.kt @@ -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