diff --git a/src/main/java/org/spongepowered/common/bridge/world/inventory/container/TrackedContainerBridge.java b/src/main/java/org/spongepowered/common/bridge/world/inventory/container/TrackedContainerBridge.java index 6de396ab1ad..f610e223c1e 100644 --- a/src/main/java/org/spongepowered/common/bridge/world/inventory/container/TrackedContainerBridge.java +++ b/src/main/java/org/spongepowered/common/bridge/world/inventory/container/TrackedContainerBridge.java @@ -28,5 +28,5 @@ public interface TrackedContainerBridge { boolean bridge$capturePossible(); - void bridge$detectAndSendChanges(boolean captureOnly); + void bridge$detectAndSendChanges(boolean capture, boolean synchronize); } diff --git a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java index 8c5278d5f80..09e1280fb41 100644 --- a/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java +++ b/src/main/java/org/spongepowered/common/event/tracking/phase/packet/PacketPhaseUtil.java @@ -125,6 +125,7 @@ public static void handleCursorRestore(final Player player, final Transaction context = PhaseTracker.SERVER.getPhaseContext(); TrackingUtil.processBlockCaptures(context); // ClickContainerEvent -> CraftEvent -> PreviewEvent // result is modified by the ClickMenuTransaction @@ -162,6 +163,18 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon } + @Inject(method = "broadcastFullState", at = @At("HEAD")) + private void impl$broadcastFullStateWithTransactions(final CallbackInfo ci) { + if (!PhaseTracker.SERVER.onSidedThread()) { + return; + } + this.bridge$detectAndSendChanges(false, false); + this.impl$broadcastDataSlots(false); + this.shadow$sendAllDataToRemote(); + this.impl$captureSuccess = true; // Detect mod overrides + ci.cancel(); + } + // broadcastChanges /** @@ -176,14 +189,14 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon if (!PhaseTracker.SERVER.onSidedThread()) { return; } - this.bridge$detectAndSendChanges(false); + this.bridge$detectAndSendChanges(false, true); this.impl$captureSuccess = true; // Detect mod overrides ci.cancel(); } @Override - public void bridge$detectAndSendChanges(final boolean captureOnly) { + public void bridge$detectAndSendChanges(final boolean capture, final boolean synchronize) { // Code-Flow changed from vanilla completely! final SpongeInventoryMenu menu = ((MenuBridge)this).bridge$getMenu(); @@ -212,7 +225,7 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon } else { this.impl$capture(i, newStack, oldStack); // Capture changes for inventory events - if (captureOnly) { + if (capture) { continue; } // Perform vanilla logic - updating inventory stack - notify listeners @@ -222,18 +235,22 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon for (final ContainerListener listener : this.containerListeners) { listener.slotChanged(((AbstractContainerMenu) (Object) this), i, oldStack); } - final ItemStack remoteStack = this.remoteSlots.get(i); - if (!ItemStack.matches(remoteStack, newStack)) { - this.remoteSlots.set(i, newStack.copy()); - if (this.synchronizer != null) { - this.synchronizer.sendSlotChange(((AbstractContainerMenu) (Object) this), i, newStack); + if (synchronize) { + final ItemStack remoteStack = this.remoteSlots.get(i); + if (!ItemStack.matches(remoteStack, newStack)) { + this.remoteSlots.set(i, newStack.copy()); + if (this.synchronizer != null) { + this.synchronizer.sendSlotChange(((AbstractContainerMenu) (Object) this), i, newStack); + } } } } } - this.shadow$synchronizeCarriedToRemote(); - this.impl$broadcastDataSlots(); + if (synchronize) { + this.shadow$synchronizeCarriedToRemote(); + this.impl$broadcastDataSlots(true); + } } private void impl$sendSlotContents(final Integer i, final ItemStack oldStack) { @@ -250,13 +267,15 @@ public abstract class AbstractContainerMenuMixin_Inventory implements TrackedCon } } - private void impl$broadcastDataSlots() { + private void impl$broadcastDataSlots(final boolean synchronize) { for(int j = 0; j < this.dataSlots.size(); ++j) { final DataSlot dataSlot = this.dataSlots.get(j); if (dataSlot.checkAndClearUpdateFlag()) { this.shadow$updateDataSlotListeners(j, dataSlot.get()); } - this.shadow$synchronizeDataSlotToRemote(j, dataSlot.get()); + if (synchronize) { + this.shadow$synchronizeDataSlotToRemote(j, dataSlot.get()); + } } } diff --git a/testplugins/src/main/java/org/spongepowered/test/interact/ClickContainerEventCancelledTest.java b/testplugins/src/main/java/org/spongepowered/test/interact/ClickContainerEventCancelledTest.java new file mode 100644 index 00000000000..b8013a68718 --- /dev/null +++ b/testplugins/src/main/java/org/spongepowered/test/interact/ClickContainerEventCancelledTest.java @@ -0,0 +1,78 @@ +/* + * This file is part of Sponge, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.test.interact; + +import com.google.inject.Inject; +import net.kyori.adventure.identity.Identity; +import net.kyori.adventure.text.Component; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.parameter.CommandContext; +import org.spongepowered.api.event.EventContextKeys; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.item.inventory.container.ClickContainerEvent; +import org.spongepowered.api.registry.RegistryTypes; +import org.spongepowered.plugin.PluginContainer; +import org.spongepowered.plugin.builtin.jvm.Plugin; +import org.spongepowered.test.LoadableModule; + +/** + * @author CanardNocturne + */ +@Plugin("clickcontainereventcancelledtest") +public class ClickContainerEventCancelledTest implements LoadableModule { + + private final PluginContainer plugin; + + @Inject + public ClickContainerEventCancelledTest(final PluginContainer plugin) { + this.plugin = plugin; + } + + @Override + public void enable(final CommandContext ctx) { + Sponge.eventManager().registerListeners(this.plugin, new ClickContainerEventCancelledTest.ClickContainerListener()); + } + + public static class ClickContainerListener { + @Listener + private void onInteractItem(final ClickContainerEvent.Primary event) { + event.setCancelled(true); + Sponge.game().systemSubject().sendMessage(Identity.nil(), Component.text("/*************")); + Sponge.game().systemSubject().sendMessage(Identity.nil(), Component.text().append(Component.text("/* Event: ")).append(Component.text(event.getClass().getSimpleName())).build()); + Sponge.game().systemSubject().sendMessage(Identity.nil(), + Component.text().append(Component.text("/* Hand: ")) + .append(Component.text(event.context().get(EventContextKeys.USED_HAND).map(h -> RegistryTypes.HAND_TYPE.keyFor(Sponge.game(), h).formatted()).orElse("UNKNOWN"))) + .build() + ); + Sponge.game().systemSubject().sendMessage(Identity.nil(), Component.text().append(Component.text("/ Cause: ")).append(Component.text(event.cause().all().toString())).build()); + Sponge.game().systemSubject().sendMessage(Identity.nil(), Component.text().append(Component.text("/ Context: ")).append(Component.text(event.context().toString())).build()); + Sponge.game().systemSubject().sendMessage(Identity.nil(), + Component.text().append(Component.text("/ Cancelled: ")).append(Component.text(event.isCancelled())).build()); + } + } + + + +} diff --git a/testplugins/src/main/resources/META-INF/sponge_plugins.json b/testplugins/src/main/resources/META-INF/sponge_plugins.json index 7432170f26b..48cc58a59c8 100644 --- a/testplugins/src/main/resources/META-INF/sponge_plugins.json +++ b/testplugins/src/main/resources/META-INF/sponge_plugins.json @@ -285,6 +285,12 @@ "name": "Display Entities Test", "entrypoint": "org.spongepowered.test.entity.DisplayEntityTest", "description": "Testing Display Entities" + }, + { + "id": "clickcontainereventcancelledtest", + "name": "Click Container Event Cancelled Test", + "entrypoint": "org.spongepowered.test.interact.ClickContainerEventCancelledTest", + "description": "Testing Cancelling Click Container Primary Event" } ] }