use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class PlaceRecipeTransaction method createInventoryEvent.
@Override
Optional<ClickContainerEvent> createInventoryEvent(final List<SlotTransaction> slotTransactions, final List<Entity> entities, final PhaseContext<@NonNull ?> context, final Cause cause) {
final SlotTransaction preview = this.getPreviewTransaction(this.craftingInventory.result(), slotTransactions);
final Transaction<ItemStackSnapshot> cursorTransaction = new Transaction<>(this.originalCursor, ItemStackUtil.snapshotOf(this.player.inventory.getCarried()));
ClickContainerEvent.Recipe event;
if (this.shift) {
event = SpongeEventFactory.createClickContainerEventRecipeAll(cause, (Container) this.menu, this.craftingInventory, cursorTransaction, preview, Optional.of((CraftingRecipe) this.recipe), Optional.empty(), slotTransactions);
} else {
event = SpongeEventFactory.createClickContainerEventRecipeSingle(cause, (Container) this.menu, this.craftingInventory, cursorTransaction, preview, Optional.of((CraftingRecipe) this.recipe), Optional.empty(), slotTransactions);
}
return Optional.of(event);
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class SelectTradeTransaction method createInventoryEvent.
@Override
Optional<ClickContainerEvent> createInventoryEvent(final List<SlotTransaction> slotTransactions, final List<Entity> entities, final PhaseContext<@NonNull ?> context, final Cause cause) {
final ItemStackSnapshot cursorItem = ItemStackUtil.snapshotOf(this.player.inventory.getCarried());
final Transaction<ItemStackSnapshot> cursorTransaction = new Transaction<>(cursorItem, cursorItem);
if (this.menu instanceof MerchantMenu) {
final MerchantOffer offer = ((MerchantMenu) this.menu).getOffers().get(this.tradeItem);
final ClickContainerEvent.SelectTrade event = SpongeEventFactory.createClickContainerEventSelectTrade(cause, (Container) this.menu, cursorTransaction, Optional.empty(), (TradeOffer) offer, slotTransactions, this.tradeItem);
return Optional.of(event);
}
SpongeCommon.logger().warn("SelectTradeTransaction without MerchantMenu");
return Optional.empty();
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class AbstractFurnaceBlockEntityMixin method impl$throwFuelEventIfOrShrink.
// @Formatter:on
// Shrink Fuel
@Redirect(method = "tick", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;shrink(I)V"))
private void impl$throwFuelEventIfOrShrink(final ItemStack itemStack, final int quantity) {
final Cause cause = PhaseTracker.getCauseStackManager().currentCause();
final ItemStackSnapshot fuel = ItemStackUtil.snapshotOf(itemStack);
final ItemStackSnapshot shrinkedFuel = ItemStackUtil.snapshotOf(ItemStackUtil.cloneDefensive(itemStack, itemStack.getCount() - 1));
final Transaction<ItemStackSnapshot> transaction = new Transaction<>(fuel, shrinkedFuel);
final AbstractCookingRecipe recipe = this.impl$getCurrentRecipe();
final CookingEvent.ConsumeFuel event = SpongeEventFactory.createCookingEventConsumeFuel(cause, (FurnaceBlockEntity) this, Optional.of(fuel), Optional.of((CookingRecipe) recipe), Collections.singletonList(transaction));
SpongeCommon.post(event);
if (event.isCancelled()) {
this.cookingTotalTime = 0;
return;
}
if (!transaction.isValid()) {
return;
}
if (transaction.custom().isPresent()) {
this.items.set(1, ItemStackUtil.fromSnapshotToNative(transaction.finalReplacement()));
} else {
// vanilla
itemStack.shrink(quantity);
}
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class BrewingStandBlockEntityMixin method impl$onConsumeFuel.
// @Formatter:on
@Inject(method = "tick", locals = LocalCapture.CAPTURE_FAILEXCEPTION, slice = @Slice(to = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/BrewingStandBlockEntity;isBrewable()Z")), at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/entity/BrewingStandBlockEntity;setChanged()V"))
private void impl$onConsumeFuel(CallbackInfo ci, ItemStack fuelStack) {
final Cause currentCause = Sponge.server().causeStackManager().currentCause();
fuelStack.grow(1);
final ItemStackSnapshot originalStack = ItemStackUtil.snapshotOf(fuelStack);
fuelStack.shrink(1);
final Transaction<ItemStackSnapshot> fuelTransaction = new Transaction<>(originalStack, ItemStackUtil.snapshotOf(fuelStack));
final ItemStackSnapshot ingredientStack = ItemStackUtil.snapshotOf(this.items.get(3));
final BrewingEvent.ConsumeFuel event = SpongeEventFactory.createBrewingEventConsumeFuel(currentCause, (BrewingStand) this, fuelTransaction, ingredientStack);
if (Sponge.eventManager().post(event)) {
fuelStack.grow(1);
this.fuel = 0;
} else if (event.fuel().custom().isPresent()) {
final ItemStackSnapshot finalFuel = event.fuel().finalReplacement();
this.items.set(4, ItemStackUtil.fromSnapshotToNative(finalFuel));
}
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class TrackingUtil method processBlockCaptures.
/**
* Processes the given list of {@link BlockSnapshot}s and creates and throws and processes
* the {@link ChangeBlockEvent}s as appropriately determined based on the {@link BlockChange}
* for each snapshot. If any transactions are invalid or events cancelled, this event
* returns {@code false} to signify a transaction was cancelled. This return value
* is used for portal creation.
*
* @param snapshots The snapshots to process
* @param state The phase state that is being processed, used to handle marking notifiers
* and block owners
* @param context The phase context, only used by the phase for handling processes.
* @return True if no events or transactions were cancelled
*/
@SuppressWarnings({ "unchecked" })
public static boolean processBlockCaptures(List<BlockSnapshot> snapshots, IPhaseState<?> state, PhaseContext<?> context) {
if (snapshots.isEmpty()) {
return false;
}
ImmutableList<Transaction<BlockSnapshot>>[] transactionArrays = new ImmutableList[EVENT_COUNT];
ImmutableList.Builder<Transaction<BlockSnapshot>>[] transactionBuilders = new ImmutableList.Builder[EVENT_COUNT];
for (int i = 0; i < EVENT_COUNT; i++) {
transactionBuilders[i] = new ImmutableList.Builder<>();
}
final List<ChangeBlockEvent> blockEvents = new ArrayList<>();
for (BlockSnapshot snapshot : snapshots) {
// This processes each snapshot to assign them to the correct event in the next area, with the
// correct builder array entry.
TRANSACTION_PROCESSOR.apply(transactionBuilders).accept(TRANSACTION_CREATION.apply(snapshot));
}
for (int i = 0; i < EVENT_COUNT; i++) {
// Build each event array
transactionArrays[i] = transactionBuilders[i].build();
}
// Clear captured snapshots after processing them
context.getCapturedBlocksOrEmptyList().clear();
final ChangeBlockEvent[] mainEvents = new ChangeBlockEvent[BlockChange.values().length];
// case in point for WorldTick event listeners since the players are captured non-deterministically
try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
if (context.getNotifier().isPresent()) {
Sponge.getCauseStackManager().addContext(EventContextKeys.NOTIFIER, context.getNotifier().get());
}
if (context.getOwner().isPresent()) {
Sponge.getCauseStackManager().addContext(EventContextKeys.OWNER, context.getOwner().get());
}
try {
state.associateAdditionalCauses(state, context);
} catch (Exception e) {
// TODO - this should be a thing to associate additional objects in the cause, or context, but for now it's just a simple
// try catch to avoid bombing on performing block changes.
}
// Creates the block events accordingly to the transaction arrays
// Needs to throw events
iterateChangeBlockEvents(transactionArrays, blockEvents, mainEvents);
// We create the post event and of course post it in the method, regardless whether any transactions are invalidated or not
final ChangeBlockEvent.Post postEvent = throwMultiEventsAndCreatePost(transactionArrays, blockEvents, mainEvents);
if (postEvent == null) {
// Means that we have had no actual block changes apparently?
return false;
}
final List<Transaction<BlockSnapshot>> invalid = new ArrayList<>();
boolean noCancelledTransactions = true;
// transactions of the preceeding block events)
for (ChangeBlockEvent blockEvent : blockEvents) {
// Need to only check if the event is cancelled, If it is, restore
if (blockEvent.isCancelled()) {
noCancelledTransactions = false;
// Don't restore the transactions just yet, since we're just marking them as invalid for now
for (Transaction<BlockSnapshot> transaction : Lists.reverse(blockEvent.getTransactions())) {
transaction.setValid(false);
}
}
}
// Finally check the post event
if (postEvent.isCancelled()) {
// Of course, if post is cancelled, just mark all transactions as invalid.
noCancelledTransactions = false;
for (Transaction<BlockSnapshot> transaction : postEvent.getTransactions()) {
transaction.setValid(false);
}
}
// Because after, we will restore all the invalid transactions in reverse order.
for (Transaction<BlockSnapshot> transaction : postEvent.getTransactions()) {
if (!transaction.isValid()) {
invalid.add(transaction);
// Cancel any block drops performed, avoids any item drops, regardless
final Location<World> location = transaction.getOriginal().getLocation().orElse(null);
if (location != null) {
final BlockPos pos = ((IMixinLocation) (Object) location).getBlockPos();
context.getBlockItemDropSupplier().removeAllIfNotEmpty(pos);
context.getBlockEntitySpawnSupplier().removeAllIfNotEmpty(pos);
context.getBlockEntitySpawnSupplier().removeAllIfNotEmpty(pos);
}
}
}
if (!invalid.isEmpty()) {
// We need to set this value and return it to signify that some transactions were cancelled
noCancelledTransactions = false;
// or the events were cancelled), again in reverse order of which they were received.
for (Transaction<BlockSnapshot> transaction : Lists.reverse(invalid)) {
transaction.getOriginal().restore(true, BlockChangeFlags.NONE);
if (state.tracksBlockSpecificDrops()) {
// Cancel any block drops or harvests for the block change.
// This prevents unnecessary spawns.
final Location<World> location = transaction.getOriginal().getLocation().orElse(null);
if (location != null) {
final BlockPos pos = ((IMixinLocation) (Object) location).getBlockPos();
context.getBlockDropSupplier().removeAllIfNotEmpty(pos);
}
}
}
}
return performBlockAdditions(postEvent.getTransactions(), state, context, noCancelledTransactions);
}
}
Aggregations