Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/main/java/net/coreprotect/listener/ListenerHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import net.coreprotect.listener.world.StructureGrowListener;
import net.coreprotect.paper.listener.BlockPreDispenseListener;
import net.coreprotect.paper.listener.CopperGolemChestListener;
import net.coreprotect.paper.listener.CopperGolemTargetListener;
import net.coreprotect.paper.listener.FlowerPotManipulateListener;
import net.coreprotect.paper.listener.LegacyTNTPrimeListener;

Expand All @@ -79,8 +80,17 @@ public ListenerHandler(CoreProtect plugin) {
}

try {
Class.forName("io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent"); // Paper 1.21.10+
pluginManager.registerEvents(new CopperGolemChestListener(plugin), plugin);
Class.forName("org.bukkit.entity.CopperGolem");
CopperGolemChestListener copperGolemListener = new CopperGolemChestListener(plugin);
pluginManager.registerEvents(copperGolemListener, plugin);

try {
Class.forName("io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent");
pluginManager.registerEvents(new CopperGolemTargetListener(copperGolemListener), plugin);
}
catch (Exception e) {
// Paper 26.1.x: GenericGameEvent fallback remains active.
}
}
catch (Exception e) {
// Ignore registration failures to remain compatible with older servers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import net.coreprotect.config.ConfigHandler;
import net.coreprotect.listener.player.InventoryChangeListener;
import net.coreprotect.thread.Scheduler;
import net.coreprotect.utility.HopperTransactionUtils;
import net.coreprotect.utility.ItemUtils;

public final class CopperGolemChestListener implements Listener {
Expand All @@ -54,6 +55,80 @@ public CopperGolemChestListener(CoreProtect plugin) {
this.plugin = plugin;
}

void captureTarget(CopperGolem golem, BlockState blockState) {
if (!(blockState instanceof InventoryHolder)) {
return;
}

Location containerLocation = blockState.getLocation();
if (containerLocation.getWorld() == null || !Config.getConfig(containerLocation.getWorld()).ITEM_TRANSACTIONS) {
return;
}

Material containerType = blockState.getType();
if (!isCopperChest(containerType) && containerType != Material.CHEST && containerType != Material.TRAPPED_CHEST) {
return;
}

InventoryHolder inventoryHolder = (InventoryHolder) blockState;
Inventory inventory = inventoryHolder.getInventory();
Location canonicalLocation = getCanonicalContainerLocation(containerLocation, inventory);
long now = System.currentTimeMillis();
cleanupOpenInteractions(now);
TransactionKey containerKey = TransactionKey.of(canonicalLocation);
UUID golemId = golem.getUniqueId();
OpenInteraction existing = openInteractions.get(golemId);
if (existing != null && existing.containerKey.equals(containerKey) && now - existing.openedAtMillis <= OPEN_INTERACTION_TIMEOUT_MILLIS) {
return;
}
if (existing != null) {
finalizeCapturedInteraction(golemId, golem, existing);
}
handleContainerOpen(golem, canonicalLocation, containerKey, containerType, inventoryHolder, now);
OpenInteraction captured = openInteractions.get(golemId);
if (captured != null && captured.containerKey.equals(containerKey)) {
scheduleTargetWatch(golemId, captured, 1);
}
}

private void scheduleTargetWatch(UUID golemId, OpenInteraction interaction, int attempt) {
Scheduler.scheduleSyncDelayedTask(plugin, () -> watchTarget(golemId, interaction, attempt), interaction.location, 1);
}

private void watchTarget(UUID golemId, OpenInteraction interaction, int attempt) {
if (openInteractions.get(golemId) != interaction) {
return;
}

Entity entity = plugin.getServer().getEntity(golemId);
if (entity instanceof CopperGolem && finalizeCapturedInteraction(golemId, (CopperGolem) entity, interaction)) {
return;
}

if (attempt < 80) {
scheduleTargetWatch(golemId, interaction, attempt + 1);
}
}

private boolean finalizeCapturedInteraction(UUID golemId, CopperGolem golem, OpenInteraction interaction) {
BlockState blockState = interaction.location.getBlock().getState();
if (!(blockState instanceof InventoryHolder)) {
return false;
}

ItemStack[] currentContents = ((InventoryHolder) blockState).getInventory().getContents();
if (!hasInventoryChanged(interaction.baselineState, currentContents) || !isAttributableToGolem(golem, interaction, currentContents)) {
return false;
}

if (openInteractions.remove(golemId, interaction)) {
openInteractionIndexByContainerKey.remove(interaction.containerKey, new OpenInteractionIndex(golemId, interaction.openedAtMillis));
recordForcedContainerState(interaction.location, currentContents);
InventoryChangeListener.inventoryTransaction(USERNAME, interaction.location, interaction.baselineState);
}
return true;
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onGenericGameEvent(GenericGameEvent event) {
if (event == null) {
Expand Down Expand Up @@ -522,7 +597,7 @@ private void recordForcedContainerState(Location location, ItemStack[] contents)
return;
}

String loggingContainerId = USERNAME + "." + location.getBlockX() + "." + location.getBlockY() + "." + location.getBlockZ();
String loggingContainerId = HopperTransactionUtils.getLoggingId(USERNAME, location);

List<ItemStack[]> forceList = ConfigHandler.forceContainer.get(loggingContainerId);
List<ItemStack[]> oldList = ConfigHandler.oldContainer.get(loggingContainerId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package net.coreprotect.paper.listener;

import org.bukkit.entity.CopperGolem;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;

import io.papermc.paper.event.entity.ItemTransportingEntityValidateTargetEvent;

/** Paper 26.2+ target-event bridge, kept separate for 26.1.x compatibility. */
public final class CopperGolemTargetListener implements Listener {

private final CopperGolemChestListener delegate;

public CopperGolemTargetListener(CopperGolemChestListener delegate) {
this.delegate = delegate;
}

@EventHandler(priority = EventPriority.MONITOR)
public void onValidateTarget(ItemTransportingEntityValidateTargetEvent event) {
if (event.isAllowed() && event.getEntity() instanceof CopperGolem) {
delegate.captureTarget((CopperGolem) event.getEntity(), event.getBlock().getState());
}
}
}