use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class ExplosionState method processBlockCaptures.
@SuppressWarnings({ "unchecked" })
private void processBlockCaptures(List<BlockSnapshot> snapshots, Explosion explosion, PhaseContext<?> context) {
if (snapshots.isEmpty()) {
return;
}
ImmutableList<Transaction<BlockSnapshot>>[] transactionArrays = new ImmutableList[TrackingUtil.EVENT_COUNT];
ImmutableList.Builder<Transaction<BlockSnapshot>>[] transactionBuilders = new ImmutableList.Builder[TrackingUtil.EVENT_COUNT];
for (int i = 0; i < TrackingUtil.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.
TrackingUtil.TRANSACTION_PROCESSOR.apply(transactionBuilders).accept(TrackingUtil.TRANSACTION_CREATION.apply(snapshot));
}
for (int i = 0; i < TrackingUtil.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()) {
try {
this.associateAdditionalCauses(this, 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);
// Copied from TrackingUtil#throwMultiEventsAndCreatePost
for (BlockChange blockChange : BlockChange.values()) {
final ChangeBlockEvent mainEvent = mainEvents[blockChange.ordinal()];
if (mainEvent != null) {
Sponge.getCauseStackManager().pushCause(mainEvent);
}
}
final ImmutableList<Transaction<BlockSnapshot>> transactions = transactionArrays[TrackingUtil.MULTI_CHANGE_INDEX];
final ExplosionEvent.Post postEvent = SpongeEventFactory.createExplosionEventPost(Sponge.getCauseStackManager().getCurrentCause(), explosion, transactions);
if (postEvent == null) {
// Means that we have had no actual block changes apparently?
return;
}
SpongeImpl.postEvent(postEvent);
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);
final Location<World> location = transaction.getOriginal().getLocation().orElse(null);
if (location != null) {
// Cancel any block drops performed, avoids any item drops, regardless
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 (this.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);
}
}
}
}
TrackingUtil.performBlockAdditions(postEvent.getTransactions(), this, context, noCancelledTransactions);
}
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class BasicInventoryPacketState method unwind.
@Override
public void unwind(InventoryPacketContext context) {
final EntityPlayerMP player = context.getPacketPlayer();
// The server will disable the player's crafting after receiving a
// client packet
// that did not pass validation (server click item != packet click item)
// The server then sends a SPacketConfirmTransaction and waits for a
// CPacketConfirmTransaction to re-enable crafting confirming that the
// client
// acknowledged the denied transaction.
// To detect when this happens, we turn off capturing so we can avoid
// firing
// invalid events.
// See MixinNetHandlerPlayServer processClickWindow redirect for rest of
// fix.
// --bloodmc
final IMixinContainer mixinContainer = ContainerUtil.toMixin(player.openContainer);
if (!mixinContainer.capturingInventory()) {
mixinContainer.getCapturedTransactions().clear();
return;
}
// TODO clear this shit out of the context
final CPacketClickWindow packetIn = context.getPacket();
final ItemStackSnapshot lastCursor = context.getCursor();
final ItemStackSnapshot newCursor = ItemStackUtil.snapshotOf(player.inventory.getItemStack());
final Transaction<ItemStackSnapshot> transaction = new Transaction<>(lastCursor, newCursor);
final net.minecraft.inventory.Container openContainer = player.openContainer;
final List<SlotTransaction> slotTransactions = mixinContainer.getCapturedTransactions();
final int usedButton = packetIn.getUsedButton();
final List<Entity> capturedItems = new ArrayList<>();
for (EntityItem entityItem : context.getCapturedItems()) {
capturedItems.add(EntityUtil.fromNative(entityItem));
}
try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
Sponge.getCauseStackManager().pushCause(openContainer);
Sponge.getCauseStackManager().pushCause(player);
final ClickInventoryEvent inventoryEvent;
inventoryEvent = this.createInventoryEvent(player, ContainerUtil.fromNative(openContainer), transaction, Lists.newArrayList(slotTransactions), capturedItems, usedButton);
// If this happens and we captured no entities, avoid firing events
if (mixinContainer.getCapturedTransactions().isEmpty() && capturedItems.isEmpty() && transaction.getOriginal().equals(transaction.getFinal())) {
mixinContainer.setCaptureInventory(false);
return;
}
if (inventoryEvent != null) {
// Don't fire inventory drop events when there are no entities
if (inventoryEvent instanceof AffectEntityEvent && ((AffectEntityEvent) inventoryEvent).getEntities().isEmpty()) {
slotTransactions.clear();
mixinContainer.setCaptureInventory(false);
return;
}
// packet has everything we want.
if (!(inventoryEvent instanceof ClickInventoryEvent.Drag)) {
PacketPhaseUtil.validateCapturedTransactions(packetIn.getSlotId(), openContainer, inventoryEvent.getTransactions());
}
SpongeImpl.postEvent(inventoryEvent);
if (inventoryEvent.isCancelled() || PacketPhaseUtil.allTransactionsInvalid(inventoryEvent.getTransactions())) {
if (inventoryEvent instanceof ClickInventoryEvent.Drop) {
capturedItems.clear();
}
// Restore cursor
if (inventoryEvent.isCancelled() || !inventoryEvent.getCursorTransaction().isValid()) {
PacketPhaseUtil.handleCustomCursor(player, inventoryEvent.getCursorTransaction().getOriginal());
} else if (inventoryEvent.getCursorTransaction().getCustom().isPresent()) {
PacketPhaseUtil.handleCustomCursor(player, inventoryEvent.getCursorTransaction().getFinal());
}
// Restore target slots
PacketPhaseUtil.handleSlotRestore(player, openContainer, inventoryEvent.getTransactions(), true);
} else {
PacketPhaseUtil.handleSlotRestore(player, openContainer, inventoryEvent.getTransactions(), false);
// Handle cursor
if (!inventoryEvent.getCursorTransaction().isValid()) {
PacketPhaseUtil.handleCustomCursor(player, inventoryEvent.getCursorTransaction().getOriginal());
} else if (inventoryEvent.getCursorTransaction().getCustom().isPresent()) {
PacketPhaseUtil.handleCustomCursor(player, inventoryEvent.getCursorTransaction().getFinal());
} else if (inventoryEvent instanceof ClickInventoryEvent.Drag) {
int increment;
increment = slotTransactions.stream().filter((t) -> !t.isValid()).mapToInt((t) -> t.getFinal().getQuantity()).sum();
final ItemStack cursor = inventoryEvent.getCursorTransaction().getFinal().createStack();
cursor.setQuantity(cursor.getQuantity() + increment);
PacketPhaseUtil.handleCustomCursor(player, cursor.createSnapshot());
} else if (inventoryEvent instanceof ClickInventoryEvent.Double && !(inventoryEvent instanceof ClickInventoryEvent.Shift)) {
int decrement;
decrement = slotTransactions.stream().filter((t) -> !t.isValid()).mapToInt((t) -> t.getOriginal().getQuantity()).sum();
final ItemStack cursor = inventoryEvent.getCursorTransaction().getFinal().createStack();
cursor.setQuantity(cursor.getQuantity() - decrement);
PacketPhaseUtil.handleCustomCursor(player, cursor.createSnapshot());
}
if (inventoryEvent instanceof SpawnEntityEvent) {
processSpawnedEntities(player, (SpawnEntityEvent) inventoryEvent);
} else if (!context.getCapturedEntitySupplier().isEmpty()) {
SpawnEntityEvent spawnEntityEvent = SpongeEventFactory.createSpawnEntityEvent(Sponge.getCauseStackManager().getCurrentCause(), context.getCapturedEntities());
SpongeImpl.postEvent(spawnEntityEvent);
if (!spawnEntityEvent.isCancelled()) {
processSpawnedEntities(player, spawnEntityEvent);
}
}
}
}
}
slotTransactions.clear();
mixinContainer.setCaptureInventory(false);
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class DropItemWithHotkeyState method unwind.
@Override
public void unwind(InventoryPacketContext context) {
final EntityPlayerMP player = context.getPacketPlayer();
// final ItemStack usedStack = context.getItemUsed();
// final ItemStackSnapshot usedSnapshot = ItemStackUtil.snapshotOf(usedStack);
final Entity spongePlayer = EntityUtil.fromNative(player);
try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
Sponge.getCauseStackManager().pushCause(spongePlayer);
Sponge.getCauseStackManager().addContext(EventContextKeys.SPAWN_TYPE, InternalSpawnTypes.DROPPED_ITEM);
context.getCapturedBlockSupplier().acceptAndClearIfNotEmpty(blocks -> TrackingUtil.processBlockCaptures(blocks, this, context));
context.getCapturedItemsSupplier().acceptAndClearIfNotEmpty(items -> {
final ArrayList<Entity> entities = new ArrayList<>();
for (EntityItem item : items) {
entities.add(EntityUtil.fromNative(item));
}
int usedButton = 0;
if (context.getPacket() instanceof CPacketPlayerDigging) {
final CPacketPlayerDigging packetIn = context.getPacket();
usedButton = packetIn.getAction() == CPacketPlayerDigging.Action.DROP_ITEM ? PacketPhase.PACKET_BUTTON_PRIMARY_ID : 1;
} else {
final CPacketClickWindow packetIn = context.getPacket();
usedButton = packetIn.getUsedButton();
}
Transaction<ItemStackSnapshot> cursorTrans = new Transaction<>(ItemStackSnapshot.NONE, ItemStackSnapshot.NONE);
final IMixinContainer mixinContainer = ContainerUtil.toMixin(player.openContainer);
List<SlotTransaction> slotTrans = mixinContainer.getCapturedTransactions();
ClickInventoryEvent.Drop dropItemEvent = ((DropItemWithHotkeyState) this).createInventoryEvent(player, ContainerUtil.fromNative(player.openContainer), cursorTrans, Lists.newArrayList(slotTrans), entities, usedButton);
SpongeImpl.postEvent(dropItemEvent);
if (dropItemEvent.isCancelled() || PacketPhaseUtil.allTransactionsInvalid(dropItemEvent.getTransactions())) {
((IMixinEntityPlayerMP) player).restorePacketItem(EnumHand.MAIN_HAND);
PacketPhaseUtil.handleSlotRestore(player, player.openContainer, dropItemEvent.getTransactions(), true);
} else {
processSpawnedEntities(player, dropItemEvent);
}
slotTrans.clear();
mixinContainer.setCaptureInventory(false);
});
context.getCapturedEntityDropSupplier().acceptIfNotEmpty(itemMapping -> {
});
final IMixinContainer mixinContainer = ContainerUtil.toMixin(player.openContainer);
mixinContainer.setCaptureInventory(false);
mixinContainer.getCapturedTransactions().clear();
}
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class OpenInventoryState method unwind.
@Override
public void unwind(InventoryPacketContext context) {
final EntityPlayerMP player = context.getPacketPlayer();
final ItemStackSnapshot lastCursor = context.getCursor();
final ItemStackSnapshot newCursor = ItemStackUtil.snapshotOf(player.inventory.getItemStack());
final Transaction<ItemStackSnapshot> cursorTransaction = new Transaction<>(lastCursor, newCursor);
try (CauseStackManager.StackFrame frame = Sponge.getCauseStackManager().pushCauseFrame()) {
Sponge.getCauseStackManager().pushCause(player);
final InteractInventoryEvent.Open event = SpongeEventFactory.createInteractInventoryEventOpen(Sponge.getCauseStackManager().getCurrentCause(), cursorTransaction, ContainerUtil.fromNative(player.openContainer));
SpongeImpl.postEvent(event);
if (event.isCancelled()) {
player.closeScreen();
} else {
// Custom cursor
if (!event.getCursorTransaction().isValid()) {
PacketPhaseUtil.handleCustomCursor(player, event.getCursorTransaction().getOriginal());
} else if (event.getCursorTransaction().getCustom().isPresent()) {
PacketPhaseUtil.handleCustomCursor(player, event.getCursorTransaction().getFinal());
}
}
}
}
use of org.spongepowered.api.data.Transaction in project SpongeCommon by SpongePowered.
the class SpongeCommonEventFactory method callInteractInventoryCloseEvent.
public static InteractInventoryEvent.Close callInteractInventoryCloseEvent(Container container, EntityPlayerMP player, ItemStackSnapshot lastCursor, ItemStackSnapshot newCursor, boolean clientSource) {
Transaction<ItemStackSnapshot> cursorTransaction = new Transaction<>(lastCursor, newCursor);
final InteractInventoryEvent.Close event = SpongeEventFactory.createInteractInventoryEventClose(Sponge.getCauseStackManager().getCurrentCause(), cursorTransaction, ContainerUtil.fromNative(container));
SpongeImpl.postEvent(event);
if (event.isCancelled()) {
if (clientSource && container.getSlot(0) != null) {
if (!(container instanceof ContainerPlayer)) {
// Inventory closed by client, reopen window and send
// container
player.openContainer = container;
final String guiId;
final Slot slot = container.getSlot(0);
final IInventory slotInventory = slot.inventory;
if (slotInventory instanceof IInteractionObject) {
guiId = ((IInteractionObject) slotInventory).getGuiID();
} else {
// expected fallback for unknown types
guiId = "minecraft:container";
}
slotInventory.openInventory(player);
player.connection.sendPacket(new SPacketOpenWindow(container.windowId, guiId, slotInventory.getDisplayName(), slotInventory.getSizeInventory()));
// resync data to client
player.sendContainerToPlayer(container);
} else {
// TODO: Maybe print a warning or throw an exception here?
// The player gui cannot be opened from the
// server so allowing this event to be cancellable when the
// GUI has been closed already would result
// in opening the wrong GUI window.
}
}
// Handle cursor
if (!event.getCursorTransaction().isValid()) {
handleCustomCursor(player, event.getCursorTransaction().getOriginal());
}
} else {
IMixinContainer mixinContainer = (IMixinContainer) player.openContainer;
mixinContainer.getCapturedTransactions().clear();
mixinContainer.setCaptureInventory(false);
// Handle cursor
if (!event.getCursorTransaction().isValid()) {
handleCustomCursor(player, event.getCursorTransaction().getOriginal());
} else if (event.getCursorTransaction().getCustom().isPresent()) {
handleCustomCursor(player, event.getCursorTransaction().getFinal());
}
if (!clientSource && player.openContainer != null && player.connection != null) {
player.closeScreen();
}
}
return event;
}
Aggregations