Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package net.tfminecraft.magic.listener;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
Expand Down Expand Up @@ -184,12 +188,28 @@ public void onChunkLoad(ChunkLoadEvent event) {
ArtifactDisplayIndex.ensureChunk(event.getChunk());
}

/** Players with a lore refresh already queued for the next tick. */
private final Set<UUID> pendingLore = new HashSet<>();

/**
* Refreshes the inventory's aura and charge lore on the next tick, once per player however
* many items they picked up this tick. One refresh is the same as several: ArtifactLore.apply
* and ChargeLore.apply strip their own lore block and rebuild it from the item's data, so a
* second run in the same tick gives the same item. The queued mark is cleared by its own task
* even when SessionGuard skips the refresh (player gone or character switched), so the next
* pickup always queues again.
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPickup(EntityPickupItemEvent event) {
if (!(event.getEntity() instanceof Player player)) {
if (!(event.getEntity() instanceof Player player) || Magic.plugin == null) {
return;
}
UUID id = player.getUniqueId();
if (!pendingLore.add(id)) {
return;
}
SessionGuard.runLater(player, () -> VesselLore.updateInventory(player));
Magic.plugin.getServer().getScheduler().runTask(Magic.plugin, () -> pendingLore.remove(id));
}

@EventHandler
Expand Down
52 changes: 52 additions & 0 deletions src/test/java/net/tfminecraft/magic/ArtifactListenerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
listener.onPickup(new EntityPickupItemEvent(mock(Zombie.class), mock(Item.class), 0));
listener.onPickup(new EntityPickupItemEvent(player, mock(Item.class), 0));
server.getScheduler().performOneTick();
listener.onJoin(new PlayerJoinEvent(player, "joined"));

Check warning on line 119 in src/test/java/net/tfminecraft/magic/ArtifactListenerTest.java

View workflow job for this annotation

GitHub Actions / build

PlayerJoinEvent(org.bukkit.entity.Player,java.lang.String) in org.bukkit.event.player.PlayerJoinEvent has been deprecated and marked for removal
lore.verify(() -> VesselLore.updateInventory(player), times(2));
}
}
Expand Down Expand Up @@ -331,7 +331,7 @@
verify(event).setCancelled(true);
assertEquals("fillchest.done", player.nextMessage());
service.verify(() -> ArtifactFillChestService.consume(player.getUniqueId()));
listener.onQuit(new PlayerQuitEvent(player, "quit"));

Check warning on line 334 in src/test/java/net/tfminecraft/magic/ArtifactListenerTest.java

View workflow job for this annotation

GitHub Actions / build

PlayerQuitEvent(org.bukkit.entity.Player,java.lang.String) in org.bukkit.event.player.PlayerQuitEvent has been deprecated and marked for removal
service.verify(() -> ArtifactFillChestService.clear(player.getUniqueId()), times(2));
}
}
Expand All @@ -348,6 +348,58 @@
}
}

@Test
void manyPickupsInOneTickRefreshTheLoreOnceAndALaterPickupRefreshesAgain() {
var listener = new ArtifactListener();
var player = server.addPlayer();
var other = server.addPlayer();
try (var lore = mockStatic(VesselLore.class)) {
for (int i = 0; i < 5; i++) {
listener.onPickup(new EntityPickupItemEvent(player, mock(Item.class), 0));
}
listener.onPickup(new EntityPickupItemEvent(other, mock(Item.class), 0));
lore.verifyNoInteractions();
server.getScheduler().performOneTick();
lore.verify(() -> VesselLore.updateInventory(player));
lore.verify(() -> VesselLore.updateInventory(other));
listener.onPickup(new EntityPickupItemEvent(player, mock(Item.class), 0));
server.getScheduler().performOneTick();
lore.verify(() -> VesselLore.updateInventory(player), times(2));
lore.verify(() -> VesselLore.updateInventory(other));
}
}

@Test
void aSkippedRefreshStillLetsTheNextPickupQueueOne() {
var listener = new ArtifactListener();
var player = server.addPlayer();
var provider = new CharacterTestSupport();
provider.start(player, "Alice");
try (var lore = mockStatic(VesselLore.class)) {
listener.onPickup(new EntityPickupItemEvent(player, mock(Item.class), 0));
provider.start(player, "Bob");
server.getScheduler().performOneTick();
lore.verifyNoInteractions();
listener.onPickup(new EntityPickupItemEvent(player, mock(Item.class), 0));
server.getScheduler().performOneTick();
lore.verify(() -> VesselLore.updateInventory(player));
} finally {
provider.unregister();
}
}

@Test
void pickupsQueueNothingWhileThePluginIsNotRunning() {
var listener = new ArtifactListener();
var player = server.addPlayer();
try (var lore = mockStatic(VesselLore.class)) {
Magic.plugin = null;
listener.onPickup(new EntityPickupItemEvent(player, mock(Item.class), 0));
server.getScheduler().performOneTick();
lore.verifyNoInteractions();
}
}

@Test
void deferredShrineWorkKeepsTheWorldItemButDropsAnOfflineOrStaleActor() {
var listener = new ArtifactListener();
Expand Down
Loading