Skip to content

Commit

Permalink
fix: composite transactions respect event cancellation
Browse files Browse the repository at this point in the history
  • Loading branch information
gabizou committed Jan 12, 2025
1 parent 5e670a9 commit 9f3fa2a
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 484 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,24 @@
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.phys.BlockHitResult;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.spongepowered.api.block.BlockSnapshot;
import org.spongepowered.api.world.BlockChangeFlag;
import org.spongepowered.common.block.SpongeBlockSnapshot;
import org.spongepowered.common.bridge.server.level.ServerLevelBridge;
import org.spongepowered.common.bridge.world.level.LevelBridge;
import org.spongepowered.common.bridge.world.level.block.state.BlockStateBridge;
import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseBlockPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseItemAtPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseItemOnBlockPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseItemPipeline;
import org.spongepowered.common.event.tracking.context.transaction.effect.InteractionAtArgs;
import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemArgs;
import org.spongepowered.common.event.tracking.context.transaction.effect.UseItemAtArgs;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.WorldPipeline;

import java.util.Optional;
Expand All @@ -59,16 +57,14 @@
*/
public interface TrackedWorldBridge {

boolean bridge$forceSpawnEntity(Entity entity);

Optional<WorldPipeline.Builder> bridge$startBlockChange(BlockPos pos, BlockState state, int rawFlags);

/**
* Delegates to the {@link ServerLevel} to perform the lookup for a {@link LevelChunk}
* such that if the target {@link BlockPos} results in a {@code false} for
* {@link ServerLevel#isBlockLoaded(BlockPos)}, {@link BlockSnapshot#empty()}
* {@link ServerLevel#isLoaded(BlockPos)}, {@link BlockSnapshot#empty()}
* will be returned. Likewise, optimizes the creation of the snapshot by performing
* the {@link LevelChunk#getBlockState(BlockPos)} and {@link LevelChunk#getTileEntity(BlockPos, Chunk.CreateEntityType)}
* the {@link LevelChunk#getBlockState(BlockPos)} and {@link LevelChunk#getBlockEntity(BlockPos, LevelChunk.EntityCreationType)}
* lookup on the same chunk, avoiding an additional chunk lookup.
*
* <p>This should be used when the "known" {@link BlockState} for the target
Expand All @@ -83,42 +79,25 @@ public interface TrackedWorldBridge {
}

/**
* Creates a {@link BlockSnapshot} but performs an additional {@link LevelChunk#getTileEntity(BlockPos, Chunk.CreateEntityType)}
* Creates a {@link BlockSnapshot} but performs an additional {@link LevelChunk#getBlockEntity(BlockPos, LevelChunk.EntityCreationType)}
* lookup if the providing {@link BlockState#getBlock()} {@code instanceof} is
* {@code true} for being an {@link EntityBlock} or
* {@link BlockStateBridge#bridge$hasTileEntity()}, and associates
* the resulting snapshot of said Tile with the snapshot. This is useful for in-progress
* snapshot creation during transaction building for {@link TransactionalCaptureSupplier}.
*
* <p>If the {@link BlockEntity} is already known, and no lookups are needed, use
* {@link #bridge$createSnapshotWithEntity(BlockState, BlockPos, BlockChangeFlag, BlockEntity)} as it avoids
* any further chunk lookups.</p>
*
* @param state The block state
* @param pos The target position
* @param updateFlag The update flag
* @return The snapshot, never NONE
*/
SpongeBlockSnapshot bridge$createSnapshot(BlockState state, BlockPos pos, BlockChangeFlag updateFlag);

/**
* Similar to {@link #bridge$createSnapshot(BlockState, BlockPos, BlockChangeFlag)},
* but with the added avoidance of a {@link BlockEntity} lookup during the creation of the resulting
* {@link SpongeBlockSnapshot}.
*
* @param state The state
* @param pos The position
* @param updateFlag The update flag
* @param tileEntity The tile entity to serialize, if available
* @return The snapshot, never NONE
*/
SpongeBlockSnapshot bridge$createSnapshotWithEntity(BlockState state, BlockPos pos, BlockChangeFlag updateFlag, @Nullable BlockEntity tileEntity);

UseItemOnBlockPipeline bridge$startInteractionUseOnChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack);
InteractionPipeline<@NonNull InteractionAtArgs> bridge$startInteractionUseOnChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack);

UseBlockPipeline bridge$startInteractionChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack);
InteractionPipeline<@NonNull InteractionAtArgs> bridge$startInteractionChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, BlockHitResult blockRaytraceResultIn, BlockState blockstate, ItemStack copiedStack);

UseItemAtPipeline bridge$startItemInteractionChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, ItemStack copiedStack, BlockHitResult blockRaytraceResult, boolean creative);
InteractionPipeline<@NonNull UseItemAtArgs> bridge$startItemInteractionChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, ItemStack copiedStack, BlockHitResult blockRaytraceResult, boolean creative);

UseItemPipeline bridge$startItemInteractionUseChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, ItemStack copiedStack);
InteractionPipeline<@NonNull UseItemArgs> bridge$startItemInteractionUseChange(Level worldIn, ServerPlayer playerIn, InteractionHand handIn, ItemStack copiedStack);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import net.minecraft.world.InteractionResult;
import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor;
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseBlockPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline;

public final class InteractionItemEffect implements ProcessingSideEffect.Simple<UseBlockPipeline, InteractionAtArgs, InteractionResult> {
public final class InteractionItemEffect implements ProcessingSideEffect.Simple<InteractionPipeline<InteractionAtArgs>, InteractionAtArgs, InteractionResult> {

private static final class Holder {
static final InteractionItemEffect INSTANCE = new InteractionItemEffect();
Expand All @@ -41,7 +41,7 @@ public static InteractionItemEffect getInstance() {

@Override
public EffectResult<InteractionResult> processSideEffect(
UseBlockPipeline pipeline, InteractionResult oldState, InteractionAtArgs args
InteractionPipeline<InteractionAtArgs> pipeline, InteractionResult oldState, InteractionAtArgs args
) {
final var player = args.player();
final var world = args.world();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import net.minecraft.world.InteractionResult;
import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor;
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseItemOnBlockPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline;

public final class InteractionUseItemOnBlockEffect implements ProcessingSideEffect<UseItemOnBlockPipeline, InteractionResult, InteractionAtArgs, InteractionResult> {
public final class InteractionUseItemOnBlockEffect implements ProcessingSideEffect<InteractionPipeline<InteractionAtArgs>, InteractionResult, InteractionAtArgs, InteractionResult> {

private static final class Holder {
static final InteractionUseItemOnBlockEffect INSTANCE = new InteractionUseItemOnBlockEffect();
Expand All @@ -41,7 +41,7 @@ public static InteractionUseItemOnBlockEffect getInstance() {

@Override
public EffectResult<InteractionResult> processSideEffect(
UseItemOnBlockPipeline pipeline, InteractionResult oldState, InteractionAtArgs args
InteractionPipeline<InteractionAtArgs> pipeline, InteractionResult oldState, InteractionAtArgs args
) {
final var player = args.player();
final var hand = args.hand();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@
import net.minecraft.world.item.context.UseOnContext;
import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor;
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseItemAtPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline;

public class UseItemAtEffect implements ProcessingSideEffect.Simple<UseItemAtPipeline, UseItemAtArgs, InteractionResult> {
public class UseItemAtEffect implements ProcessingSideEffect.Simple<InteractionPipeline<UseItemAtArgs>, UseItemAtArgs, InteractionResult> {

private static final class Holder {
static final UseItemAtEffect INSTANCE = new UseItemAtEffect();
Expand All @@ -45,7 +45,7 @@ public static UseItemAtEffect getInstance() {

@Override
public EffectResult<InteractionResult> processSideEffect(
UseItemAtPipeline pipeline, InteractionResult oldState, UseItemAtArgs args
InteractionPipeline<UseItemAtArgs> pipeline, InteractionResult oldState, UseItemAtArgs args
) {
final var stack = args.copiedStack();
final InteractionResult result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
import net.minecraft.world.InteractionResult;
import org.spongepowered.common.event.tracking.context.transaction.EffectTransactor;
import org.spongepowered.common.event.tracking.context.transaction.inventory.PlayerInventoryTransaction;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.UseItemPipeline;
import org.spongepowered.common.event.tracking.context.transaction.pipeline.InteractionPipeline;

public class UseItemEffect implements ProcessingSideEffect.Simple<UseItemPipeline, UseItemArgs, InteractionResult> {
public class UseItemEffect implements ProcessingSideEffect.Simple<InteractionPipeline<UseItemArgs>, UseItemArgs, InteractionResult> {

private static final class Holder {
static final UseItemEffect INSTANCE = new UseItemEffect();
Expand All @@ -44,7 +44,7 @@ public static UseItemEffect getInstance() {

@Override
public EffectResult<InteractionResult> processSideEffect(
UseItemPipeline pipeline, InteractionResult oldState, UseItemArgs args
InteractionPipeline<UseItemArgs> pipeline, InteractionResult oldState, UseItemArgs args
) {
final var gameMode = args.gameMode();
final InteractionResult result = gameMode.useItem(args.player(), args.world(), args.copiedStack(), args.hand());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
package org.spongepowered.common.event.tracking.context.transaction.inventory;

import com.google.common.collect.ImmutableList;
import org.spongepowered.api.event.Cancellable;
import org.spongepowered.api.event.CauseStackManager;
import org.spongepowered.api.event.CompositeEvent;
import org.spongepowered.api.event.Event;
Expand All @@ -48,7 +47,7 @@ public void associateSideEffectEvents(E event, Stream<Event> elements) {
@Override
public void finalizeSideEffects(E post) {
// This finalizes the list to be immutable
((AbstractCompositeEvent) post).postInit();
((AbstractCompositeEvent<?>) post).postInit();
}

public void pushCause(CauseStackManager.StackFrame frame, E e) {
Expand All @@ -59,9 +58,12 @@ public void pushCause(CauseStackManager.StackFrame frame, E e) {
public boolean markCancelledTransactions(
final E event,
final ImmutableList<? extends GameTransaction<E>> gameTransactions) {
if (!event.isCancelled()) {
return false;
}
event.setCancelled(true);
gameTransactions.forEach(GameTransaction::markCancelled);
return false;
return true;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.spongepowered.common.event.tracking.context.transaction.pipeline;

import net.minecraft.world.InteractionResult;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.common.event.tracking.PhaseContext;
import org.spongepowered.common.event.tracking.context.transaction.ResultingTransactionBySideEffect;
import org.spongepowered.common.event.tracking.context.transaction.TransactionalCaptureSupplier;
import org.spongepowered.common.event.tracking.context.transaction.effect.ProcessingSideEffect;

import java.util.Objects;

public record InteractionPipeline<@NonNull A extends ProcessingSideEffect.Args>(
A args,
InteractionResult defaultResult,
ProcessingSideEffect<InteractionPipeline<A>, InteractionResult, A, InteractionResult> effect,
TransactionalCaptureSupplier transactor
) {

public InteractionResult processInteraction(final PhaseContext<?> context) {
var result = this.defaultResult;
final ResultingTransactionBySideEffect<InteractionPipeline<A>, InteractionResult, A, InteractionResult> resultingEffect = new ResultingTransactionBySideEffect<>(this.effect);
try (final var ignored = context.getTransactor().pushEffect(resultingEffect)) {
final var effectResult = this.effect.processSideEffect(this, result, this.args);
if (effectResult.hasResult) {
final @Nullable InteractionResult resultingState = effectResult.resultingState;
result = Objects.requireNonNullElse(resultingState, result);
}
}
return result;
}
}

This file was deleted.

Loading

0 comments on commit 9f3fa2a

Please sign in to comment.