From 58a2918cff9c3e678c00744dd74add4de3e2fb95 Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Tue, 27 Oct 2026 13:37:23 +0100 Subject: [PATCH 1/2] fix: refresh crafted items again once the character is played The join sweep runs a tick after join, but RPCharacters only puts the character's inventory on when the player presses Play, so the sweep saw the empty selection room and worn or carried pieces kept old stats through relogs and restarts until touched. Sweep the inventory and ender chest again a tick after TLibs' CharacterSessionStartEvent (join and character switch). TLibs is bumped to 2.6.1, the version on all servers; the event needs 2.3.0 or newer. Co-Authored-By: Claude Opus 5.5 --- pom.xml | 2 +- .../managers/CraftRefreshListener.java | 12 +++++++- .../RefreshListenerCoverageTest.java | 28 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 36d1c83..bec1f95 100644 --- a/pom.xml +++ b/pom.xml @@ -13,7 +13,7 @@ UTF-8 full 2026-09-22T00:00:00Z - 2.0.0 + 2.6.1 diff --git a/src/main/java/net/tfminecraft/advancedcrafting/managers/CraftRefreshListener.java b/src/main/java/net/tfminecraft/advancedcrafting/managers/CraftRefreshListener.java index fa8c2a1..1c20151 100644 --- a/src/main/java/net/tfminecraft/advancedcrafting/managers/CraftRefreshListener.java +++ b/src/main/java/net/tfminecraft/advancedcrafting/managers/CraftRefreshListener.java @@ -21,6 +21,7 @@ import net.tfminecraft.tlibs.armour.ArmorEquipEvent; import net.tfminecraft.tlibs.armour.ArmorType; +import net.tfminecraft.tlibs.character.CharacterSessionStartEvent; import net.tfminecraft.advancedcrafting.AdvancedCrafting; import net.tfminecraft.advancedcrafting.utils.AcItemRefresher; @@ -88,7 +89,16 @@ public void onArmorEquip(ArmorEquipEvent event) { /** After a restart every player rejoins, so this brings their carried items up to date without anyone acting. */ @EventHandler(priority = EventPriority.MONITOR) public void onJoin(PlayerJoinEvent event) { - Player player = event.getPlayer(); + sweepLater(event.getPlayer()); + } + + /** A character's inventory is only put on when it is played, after the join sweep has already run. */ + @EventHandler(priority = EventPriority.MONITOR) + public void onSessionStart(CharacterSessionStartEvent event) { + sweepLater(event.getPlayer()); + } + + private void sweepLater(Player player) { Bukkit.getScheduler().runTask(AdvancedCrafting.plugin, () -> { if (!player.isOnline()) { return; diff --git a/src/test/java/net/tfminecraft/advancedcrafting/RefreshListenerCoverageTest.java b/src/test/java/net/tfminecraft/advancedcrafting/RefreshListenerCoverageTest.java index 69a5072..8e790ba 100644 --- a/src/test/java/net/tfminecraft/advancedcrafting/RefreshListenerCoverageTest.java +++ b/src/test/java/net/tfminecraft/advancedcrafting/RefreshListenerCoverageTest.java @@ -7,6 +7,7 @@ import net.tfminecraft.advancedcrafting.managers.*; import net.tfminecraft.advancedcrafting.utils.*; import net.tfminecraft.tlibs.armour.*; +import net.tfminecraft.tlibs.character.CharacterSessionStartEvent; import net.tfminecraft.tlibs.event.MMOItemRebuildEvent; import org.bukkit.Material; import org.bukkit.block.DoubleChest; @@ -158,6 +159,33 @@ void joinSweepRefreshesInventoryArmourOffhandAndEnderChestOneTickLater() throws verify(gone, never()).getInventory(); } + @Test + void characterSessionStartSweepsTheCharacterInventoryOneTickLater() throws Exception { + when(plugin.isEnabled()).thenReturn(true); + var p = server.addPlayer(); + var iron = ingredient("iron", ""); + iron.setRevision(1); + var stale = iron.build(); + iron.setRevision(2); + // The character's body is put on after the join sweep, so the join tick finds nothing to refresh. + var join = mock(PlayerJoinEvent.class); + when(join.getPlayer()).thenReturn(p); + var listener = new CraftRefreshListener(); + listener.onJoin(join); + server.getScheduler().performOneTick(); + p.getInventory().setChestplate(stale.clone()); + p.getInventory().setItem(5, stale.clone()); + p.getEnderChest().setItem(1, stale.clone()); + var start = mock(CharacterSessionStartEvent.class); + when(start.getPlayer()).thenReturn(p); + listener.onSessionStart(start); + assertEquals(1, AcItemTags.getStoredRevision(p.getInventory().getChestplate())); + server.getScheduler().performOneTick(); + assertEquals(2, AcItemTags.getStoredRevision(p.getInventory().getChestplate())); + assertEquals(2, AcItemTags.getStoredRevision(p.getInventory().getItem(5))); + assertEquals(2, AcItemTags.getStoredRevision(p.getEnderChest().getItem(1))); + } + @Test void aBrokenItemIsLoggedAndTheSweepCarriesOn() { when(plugin.isEnabled()).thenReturn(true); From 1c66e29b3841a1bdd4bc3dbca4fc1b78ad814356 Mon Sep 17 00:00:00 2001 From: XxFran10xX <318299142+XxFran10xX@users.noreply.github.com> Date: Tue, 27 Oct 2026 14:47:58 +0100 Subject: [PATCH 2/2] docs: state the TLibs 2.3.0 minimum for the session refresh Co-Authored-By: Claude Opus 5.5 --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 054b477..924d742 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,13 @@ finished item. it, never the count); a natural 20 always succeeds and a natural 1 always fails. The roll never changes the piece: 100% hits is always a Masterwork. +## Requirements + +Paper 1.21.10 with MMOCore, MMOItems, MythicLib and **TLibs 2.3.0 or newer** +(builds pin 2.6.1). The item refresh listens for TLibs' character session +event, so on an older TLibs its listener cannot register. `plugin.yml` cannot +declare a minimum version, so update TLibs before or together with this plugin. + ## Credits Originally authored by **Drefvelin**.