diff --git a/src/main/java/org/spongepowered/api/event/CompositeEvent.java b/src/main/java/org/spongepowered/api/event/CompositeEvent.java
new file mode 100644
index 0000000000..4ef7486393
--- /dev/null
+++ b/src/main/java/org/spongepowered/api/event/CompositeEvent.java
@@ -0,0 +1,51 @@
+package org.spongepowered.api.event;
+
+import org.spongepowered.api.util.annotation.eventgen.NoFactoryMethod;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A {@link CompositeEvent} is an {@link Event} that contains multiple
+ * side effectual {@link Event Events}, which may have their own side effects
+ * and may be {@link Cancellable}. In some cases, the interactions of this event
+ * may be cancellable as a whole, but are not guaranteed to revert all side
+ * effects on the {@link org.spongepowered.api.Game}. The {@link #children()} of
+ * this event are ordered in a "best-effort" basis, and may not be guaranteed
+ * to be in any particular order.
+ *
Using {@link #setCancelled(boolean)} will perform a best effort cancellation
+ * on each of the children events.
+ */
+@NoFactoryMethod
+public interface CompositeEvent extends Event, Cancellable {
+
+ E baseEvent();
+
+ List children();
+
+ default List extends E> event(Class type) {
+ return this.children().stream()
+ .filter(type::isInstance)
+ .map(type::cast)
+ .toList();
+ }
+
+ default void applyTo(Class type, Consumer super E> consumer) {
+ this.children().stream()
+ .filter(type::isInstance)
+ .map(type::cast)
+ .forEach(consumer);
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * Cancels this event and all related events captured {@link #children()}.
+ * Selectively, if individual events are wished to be cancelled,
+ * the individual events should be cancelled instead.
+ *
+ * @param cancel The new cancelled state
+ */
+ @Override
+ void setCancelled(boolean cancel);
+}
diff --git a/src/main/java/org/spongepowered/api/event/block/InteractBlockEvent.java b/src/main/java/org/spongepowered/api/event/block/InteractBlockEvent.java
index 161452c8bd..ee26311ba3 100644
--- a/src/main/java/org/spongepowered/api/event/block/InteractBlockEvent.java
+++ b/src/main/java/org/spongepowered/api/event/block/InteractBlockEvent.java
@@ -29,13 +29,18 @@
import org.spongepowered.api.block.BlockTypes;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.event.Cancellable;
+import org.spongepowered.api.event.CompositeEvent;
import org.spongepowered.api.event.action.InteractEvent;
+import org.spongepowered.api.event.entity.SpawnEntityEvent;
+import org.spongepowered.api.event.item.inventory.ChangeInventoryEvent;
import org.spongepowered.api.item.inventory.ItemStack;
import org.spongepowered.api.util.Direction;
import org.spongepowered.api.util.Tristate;
import org.spongepowered.api.world.server.ServerLocation;
import org.spongepowered.math.vector.Vector3d;
+import java.util.Optional;
+
/**
* Base event for all interactions involving a {@link BlockSnapshot} at a
* {@link ServerLocation}.
@@ -101,7 +106,7 @@ interface Finish extends Primary, Cancellable {
*
* This is usually right-click.
*/
- interface Secondary extends InteractBlockEvent, Cancellable {
+ interface Secondary extends InteractBlockEvent {
Tristate originalUseItemResult();
@@ -143,43 +148,47 @@ interface Secondary extends InteractBlockEvent, Cancellable {
*/
Tristate useBlockResult();
- /**
- * Sets whether the {@link Player#itemInHand} should be used.
- *
- *
- * - FALSE: The {@link ItemStack} will never be used.
- * - UNDEFINED: The {@link ItemStack} will be used if the block fails.
- *
- * - TRUE: The {@link ItemStack} will always be used.
- *
- *
- * Note: These results may differ depending on implementation.
- *
- * @param result Whether the {@link Player#itemInHand} should be used
- */
- void setUseItemResult(Tristate result);
-
- /**
- * Sets whether the interacted {@link BlockSnapshot} should be used.
- *
- *
- * - FALSE: {@link BlockSnapshot} will never be used.
- * - UNDEFINED: {@link BlockSnapshot} will be used as normal.
- * - TRUE: {@link BlockSnapshot} will always be used.
- *
- *
- * Note: These results may differ depending on implementation.
- *
- * @param result Whether the interacted {@link BlockSnapshot} should be
- * used
- */
- void setUseBlockResult(Tristate result);
-
/**
* Gets the point of interaction where the interaction occurred as a {@link Vector3d}.
*
* @return The interaction point
*/
Vector3d interactionPoint();
+
+ interface Pre extends Secondary, Cancellable {
+
+ /**
+ * Sets whether the {@link Player#itemInHand} should be used.
+ *
+ *
+ * - FALSE: The {@link ItemStack} will never be used.
+ * - UNDEFINED: The {@link ItemStack} will be used if the block fails.
+ *
+ * - TRUE: The {@link ItemStack} will always be used.
+ *
+ *
+ * Note: These results may differ depending on implementation.
+ *
+ * @param result Whether the {@link Player#itemInHand} should be used
+ */
+ void setUseItemResult(Tristate result);
+
+ /**
+ * Sets whether the interacted {@link BlockSnapshot} should be used.
+ *
+ *
+ * - FALSE: {@link BlockSnapshot} will never be used.
+ * - UNDEFINED: {@link BlockSnapshot} will be used as normal.
+ * - TRUE: {@link BlockSnapshot} will always be used.
+ *
+ *
+ * Note: These results may differ depending on implementation.
+ *
+ * @param result Whether the interacted {@link BlockSnapshot} should be
+ * used
+ */
+ void setUseBlockResult(Tristate result);
+ }
+
}
}
diff --git a/src/main/java/org/spongepowered/api/event/entity/SpawnEntityEvent.java b/src/main/java/org/spongepowered/api/event/entity/SpawnEntityEvent.java
index f459b856f7..81e4f12714 100644
--- a/src/main/java/org/spongepowered/api/event/entity/SpawnEntityEvent.java
+++ b/src/main/java/org/spongepowered/api/event/entity/SpawnEntityEvent.java
@@ -26,6 +26,7 @@
import org.spongepowered.api.entity.Entity;
import org.spongepowered.api.event.Cause;
+import org.spongepowered.api.event.impl.entity.AbstractAffectEntityEvent;
import org.spongepowered.api.event.impl.entity.AbstractSpawnEntityEvent;
import org.spongepowered.api.util.annotation.eventgen.GenerateFactoryMethod;
import org.spongepowered.api.util.annotation.eventgen.ImplementedBy;
@@ -51,7 +52,8 @@ public interface SpawnEntityEvent extends AffectEntityEvent {
* will result in no awareness to the client that the entity was being
* spawned and later cancelled.
*/
- interface Pre extends SpawnEntityEvent {}
+ @ImplementedBy(value = AbstractAffectEntityEvent.class, priority = 2)
+ interface Pre extends AffectEntityEvent {}
interface Custom extends SpawnEntityEvent {}
}