From 45cae9b0644e6e19f677786f35fe7a8bd5b8ff5c Mon Sep 17 00:00:00 2001 From: Kam Date: Sun, 12 Oct 2025 20:38:02 -0400 Subject: [PATCH] Harden slot switching against stale saves --- .../npcs/controllers/ProfileController.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/kamkeel/npcs/controllers/ProfileController.java b/src/main/java/kamkeel/npcs/controllers/ProfileController.java index 47de460df..73f16ebf6 100644 --- a/src/main/java/kamkeel/npcs/controllers/ProfileController.java +++ b/src/main/java/kamkeel/npcs/controllers/ProfileController.java @@ -518,9 +518,12 @@ public class ProfileController implements IProfileHandler { if (EventHooks.onProfileChange(handler, scriptPlayer, profile, newSlotId, prevSlot, false)) return ProfileOperation.error(MSG_CANCELLED); + // Persist the outgoing slot using its original id. Previously we relied on + // profile.currentSlotId inside saveSlotData, but scripts firing during the + // transition could change the active slot before the save completed and + // overwrite the destination slot with stale data. saveSlotData(profile.player, profile, prevSlot); - profile.currentSlotId = newSlotId; - loadSlotData(profile.player); + loadSlotData(profile.player, profile, newSlotId); EventHooks.onProfileChange(handler, scriptPlayer, profile, newSlotId, prevSlot, true); } else { @@ -651,11 +654,11 @@ public class ProfileController implements IProfileHandler { saveSlotData(player, profile, profile.getCurrentSlotId()); } - public void loadSlotData(EntityPlayer player) { - if (player == null || !activeProfiles.containsKey(player.getUniqueID())) + private void loadSlotData(EntityPlayer player, Profile profile, int slotId) { + if (player == null || profile == null) return; - Profile profile = activeProfiles.get(player.getUniqueID()); - ISlot slot = profile.getSlots().get(profile.getCurrentSlotId()); + + ISlot slot = profile.getSlots().get(slotId); if (slot == null) return; @@ -671,14 +674,28 @@ public class ProfileController implements IProfileHandler { profileData.save(player); } + // Only flip the active slot after the player's live data matches the + // requested slot. This way, any additional saveSlotData() calls triggered by + // scripts during the load can't persist stale information to the new slot. + profile.currentSlotId = slotId; + PlayerData pdata = PlayerData.get(player); - pdata.profileSlot = profile.getCurrentSlotId(); + pdata.profileSlot = slotId; pdata.save(); if (ConfigMain.AttributesEnabled) AttributeController.getTracker(player).recalcAttributes(player); } + public void loadSlotData(EntityPlayer player) { + if (player == null || !activeProfiles.containsKey(player.getUniqueID())) + return; + Profile profile = activeProfiles.get(player.getUniqueID()); + // Delay flipping profile.currentSlotId until after the data for the target slot + // has been restored to the player so other saves observe consistent state. + loadSlotData(player, profile, profile.getCurrentSlotId()); + } + public List getProfileInfo(EntityPlayer player, int slotId) { List infoList = new ArrayList<>(); Profile profile = getProfile(player);