From 34d253347350f3320044b850ac210e625046a81c Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 20:38:49 -0500 Subject: [PATCH 1/4] Revert "Better Script Session Token Handling" This reverts commit e4033481bcd77e791ee1915bb8181810f79d427f. --- .../java/kamkeel/npcs/network/PacketUtil.java | 25 +++---------------- .../java/noppes/npcs/ServerTickHandler.java | 4 --- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/main/java/kamkeel/npcs/network/PacketUtil.java b/src/main/java/kamkeel/npcs/network/PacketUtil.java index 94719ccb3..0db53b7fc 100644 --- a/src/main/java/kamkeel/npcs/network/PacketUtil.java +++ b/src/main/java/kamkeel/npcs/network/PacketUtil.java @@ -19,7 +19,6 @@ import noppes.npcs.controllers.data.IScriptUnit; import noppes.npcs.items.ItemNpcTool; import java.io.IOException; -import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -33,36 +32,20 @@ public class PacketUtil { // ==================== SCRIPT SESSION TOKEN SYSTEM ==================== // Prevents saving script data before the client has received it from the server. // Each GET generates a token sent to the client; each SAVE must echo it back. - // Multiple tokens per player are allowed so concurrent script editors don't invalidate each other. - // Tokens are consumed on successful verify to prevent unbounded growth. - private static final int MAX_TOKENS_PER_PLAYER = 10; - private static final Map> scriptSessionTokens = new ConcurrentHashMap<>(); + private static final Map scriptSessionTokens = new ConcurrentHashMap<>(); public static String createScriptSession(EntityPlayerMP player) { String token = Long.toHexString(ThreadLocalRandom.current().nextLong()); - Set tokens = scriptSessionTokens.computeIfAbsent(player.getUniqueID(), k -> new LinkedHashSet<>()); - synchronized (tokens) { - // Evict oldest tokens if at capacity - while (tokens.size() >= MAX_TOKENS_PER_PLAYER) { - Iterator it = tokens.iterator(); - it.next(); - it.remove(); - } - tokens.add(token); - } + scriptSessionTokens.put(player.getUniqueID(), token); return token; } public static boolean verifyScriptSession(EntityPlayer player, String token) { if (token == null || token.isEmpty()) return false; - Set tokens = scriptSessionTokens.get(player.getUniqueID()); - if (tokens == null) - return false; - synchronized (tokens) { - return tokens.remove(token); - } + String expected = scriptSessionTokens.get(player.getUniqueID()); + return expected != null && expected.equals(token); } public static void clearScriptSession(EntityPlayer player) { diff --git a/src/main/java/noppes/npcs/ServerTickHandler.java b/src/main/java/noppes/npcs/ServerTickHandler.java index 959cdddd5..12a0f29ce 100644 --- a/src/main/java/noppes/npcs/ServerTickHandler.java +++ b/src/main/java/noppes/npcs/ServerTickHandler.java @@ -6,7 +6,6 @@ import cpw.mods.fml.common.gameevent.TickEvent; import cpw.mods.fml.common.gameevent.TickEvent.Phase; import kamkeel.npcs.controllers.ProfileController; import kamkeel.npcs.controllers.SyncController; -import kamkeel.npcs.network.PacketUtil; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.world.WorldServer; import noppes.npcs.controllers.AuctionController; @@ -79,8 +78,5 @@ public class ServerTickHandler { // Save and unload the player's profile data on logout ProfileController.Instance.logout(event.player); - - // Clear script session tokens - PacketUtil.clearScriptSession(event.player); } } From 12cf4b9a8567138ef33fd484ab927ae83c0d4f66 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 20:45:33 -0500 Subject: [PATCH 2/4] Revert Script Token --- .../java/kamkeel/npcs/network/PacketUtil.java | 46 ++----------------- .../request/script/AbilityScriptPacket.java | 5 +- .../request/script/BlockScriptPacket.java | 12 +---- .../script/ChainedAbilityScriptPacket.java | 5 +- .../request/script/EffectScriptPacket.java | 3 +- .../request/script/EventScriptPacket.java | 3 +- .../request/script/ForgeScriptPacket.java | 3 +- .../request/script/GlobalNPCScriptPacket.java | 3 +- .../request/script/NPCScriptPacket.java | 10 +--- .../request/script/PlayerScriptPacket.java | 3 +- .../request/script/RecipeScriptPacket.java | 3 +- .../request/script/item/ItemScriptPacket.java | 9 ---- .../script/item/LinkedItemScriptPacket.java | 3 +- .../npcs/blocks/tiles/TileScripted.java | 7 +-- .../noppes/npcs/client/gui/GuiScript.java | 7 +-- .../client/gui/script/GuiScriptInterface.java | 11 ++--- .../data/IScriptHandlerPacket.java | 15 +----- .../controllers/data/SingleScriptHandler.java | 16 ++----- .../npcs/scripted/item/ScriptCustomItem.java | 7 +-- 19 files changed, 31 insertions(+), 140 deletions(-) diff --git a/src/main/java/kamkeel/npcs/network/PacketUtil.java b/src/main/java/kamkeel/npcs/network/PacketUtil.java index 0db53b7fc..9235dc1f7 100644 --- a/src/main/java/kamkeel/npcs/network/PacketUtil.java +++ b/src/main/java/kamkeel/npcs/network/PacketUtil.java @@ -21,37 +21,10 @@ import noppes.npcs.items.ItemNpcTool; import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ThreadLocalRandom; public class PacketUtil { - // ==================== SCRIPT SESSION TOKEN SYSTEM ==================== - // Prevents saving script data before the client has received it from the server. - // Each GET generates a token sent to the client; each SAVE must echo it back. - - private static final Map scriptSessionTokens = new ConcurrentHashMap<>(); - - public static String createScriptSession(EntityPlayerMP player) { - String token = Long.toHexString(ThreadLocalRandom.current().nextLong()); - scriptSessionTokens.put(player.getUniqueID(), token); - return token; - } - - public static boolean verifyScriptSession(EntityPlayer player, String token) { - if (token == null || token.isEmpty()) - return false; - String expected = scriptSessionTokens.get(player.getUniqueID()); - return expected != null && expected.equals(token); - } - - public static void clearScriptSession(EntityPlayer player) { - scriptSessionTokens.remove(player.getUniqueID()); - } - public static boolean verifyItemPacket(String name, EnumItemPacketType type, EntityPlayer player) { if (player == null) return false; @@ -243,14 +216,11 @@ public class PacketUtil { } public static void getScripts(IScriptHandler data, EntityPlayerMP player) { - String token = createScriptSession(player); - NBTTagCompound compound = new NBTTagCompound(); compound.setBoolean("ScriptEnabled", data.getEnabled()); compound.setString("ScriptLanguage", data.getLanguage()); compound.setTag("Languages", ScriptController.Instance.nbtLanguages()); compound.setTag("ScriptConsole", NBTTags.NBTLongStringMap(data.getConsoleText())); - compound.setString("ScriptSessionToken", token); GuiDataPacket.sendGuiData(player, compound); List containers = data.getScripts(); for (int i = 0; i < containers.size(); i++) { @@ -267,18 +237,9 @@ public class PacketUtil { GuiDataPacket.sendGuiData(player, loadComplete); } - public static boolean saveScripts(IScriptHandler data, ByteBuf buffer, EntityPlayer player) throws IOException { + public static void saveScripts(IScriptHandler data, ByteBuf buffer) throws IOException { int tab = buffer.readInt(); int totalScripts = buffer.readInt(); - NBTTagCompound compound = ByteBufUtils.readNBT(buffer); - - // Verify script session token to prevent saving unloaded defaults - String token = compound.getString("ScriptSessionToken"); - if (!verifyScriptSession(player, token)) { - LogWriter.error(String.format("Rejected script save from %s: session not verified (data not loaded)", player.getCommandSenderName())); - return false; - } - if (totalScripts == 0) { data.getScripts().clear(); } @@ -289,10 +250,12 @@ public class PacketUtil { } else while (data.getScripts().size() < totalScripts) { data.getScripts().add(new ScriptContainer(data)); } + NBTTagCompound tabCompound = ByteBufUtils.readNBT(buffer); // Use factory method to create correct script unit type based on NBT - IScriptUnit script = IScriptUnit.createFromNBT(compound, data); + IScriptUnit script = IScriptUnit.createFromNBT(tabCompound, data); data.getScripts().set(tab, script); } else { + NBTTagCompound compound = ByteBufUtils.readNBT(buffer); data.setLanguage(compound.getString("ScriptLanguage")); if (!ScriptController.Instance.languages.containsKey(data.getLanguage())) { if (!ScriptController.Instance.languages.isEmpty()) { @@ -303,6 +266,5 @@ public class PacketUtil { } data.setEnabled(compound.getBoolean("ScriptEnabled")); } - return true; } } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/AbilityScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/AbilityScriptPacket.java index ada1989c3..53802cc88 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/AbilityScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/AbilityScriptPacket.java @@ -98,7 +98,7 @@ public final class AbilityScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { - data.saveScript(in, player); + data.saveScript(in); } return; } @@ -107,8 +107,7 @@ public final class AbilityScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { - if (!data.saveScript(in, player)) - return; + data.saveScript(in); // Persist to disk AbilityController.Instance.saveCustomAbility(ability); if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/BlockScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/BlockScriptPacket.java index ac2303628..ce94c98f9 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/BlockScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/BlockScriptPacket.java @@ -7,7 +7,6 @@ import kamkeel.npcs.network.AbstractPacket; import kamkeel.npcs.network.PacketChannel; import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.PacketHandler; -import kamkeel.npcs.network.PacketUtil; import kamkeel.npcs.network.enums.EnumRequestPacket; import kamkeel.npcs.network.packets.data.large.GuiDataPacket; import kamkeel.npcs.util.ByteBufUtils; @@ -16,7 +15,6 @@ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import noppes.npcs.CustomNpcsPermissions; -import noppes.npcs.LogWriter; import noppes.npcs.blocks.tiles.TileScripted; import noppes.npcs.config.ConfigScript; import noppes.npcs.controllers.ScriptController; @@ -83,10 +81,8 @@ public final class BlockScriptPacket extends AbstractPacket { TileEntity tile = player.worldObj.getTileEntity(in.readInt(), in.readInt(), in.readInt()); if (!(tile instanceof TileScripted)) return; - String token = PacketUtil.createScriptSession((EntityPlayerMP) player); NBTTagCompound compound = ((TileScripted) tile).getNBT(new NBTTagCompound()); compound.setTag("Languages", ScriptController.Instance.nbtLanguages()); - compound.setString("ScriptSessionToken", token); GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); NBTTagCompound loadComplete = new NBTTagCompound(); @@ -99,14 +95,8 @@ public final class BlockScriptPacket extends AbstractPacket { TileEntity tile = player.worldObj.getTileEntity(in.readInt(), in.readInt(), in.readInt()); if (!(tile instanceof TileScripted)) return; - NBTTagCompound compound = ByteBufUtils.readNBT(in); - String token = compound.getString("ScriptSessionToken"); - if (!PacketUtil.verifyScriptSession(player, token)) { - LogWriter.error(String.format("Rejected block script save from %s: session not verified", player.getCommandSenderName())); - return; - } TileScripted script = (TileScripted) tile; - script.setNBT(compound); + script.setNBT(ByteBufUtils.readNBT(in)); script.lastInited = -1; } } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/ChainedAbilityScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/ChainedAbilityScriptPacket.java index 99142681d..4b1dd5504 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/ChainedAbilityScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/ChainedAbilityScriptPacket.java @@ -98,7 +98,7 @@ public final class ChainedAbilityScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { - data.saveScript(in, player); + data.saveScript(in); } return; } @@ -107,8 +107,7 @@ public final class ChainedAbilityScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { - if (!data.saveScript(in, player)) - return; + data.saveScript(in); // Persist to disk AbilityController.Instance.saveChainedAbility(chain); if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/EffectScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/EffectScriptPacket.java index fc5dded0e..0570078df 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/EffectScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/EffectScriptPacket.java @@ -90,8 +90,7 @@ public final class EffectScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { - if (!data.saveScript(in, player)) - return; + data.saveScript(in); if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { LogWriter.script(String.format("[%s] (Player) %s SAVED EFFECT %s [%s]", "EFFECT SCRIPTS", player.getCommandSenderName(), effect.getName(), effect.id)); } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/EventScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/EventScriptPacket.java index 2d63c5bbd..69f660a2c 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/EventScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/EventScriptPacket.java @@ -89,8 +89,7 @@ public final class EventScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { PacketUtil.getScripts(data, (EntityPlayerMP) player); } else { - if (!PacketUtil.saveScripts(data, in, player)) - return; + PacketUtil.saveScripts(data, in); npc.updateAI = true; npc.script.hasInited = false; if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/ForgeScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/ForgeScriptPacket.java index e99abb702..86584a766 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/ForgeScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/ForgeScriptPacket.java @@ -84,8 +84,7 @@ public final class ForgeScriptPacket extends AbstractPacket { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { int tab = in.getInt(in.readerIndex()); - if (!PacketUtil.saveScripts(data, in, player)) - return; + PacketUtil.saveScripts(data, in); ScriptController.Instance.lastForgeUpdate = System.currentTimeMillis(); if (tab == -1) ScriptController.Instance.saveForgeScriptsSync(); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/GlobalNPCScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/GlobalNPCScriptPacket.java index 9f1237638..ae9e44a3d 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/GlobalNPCScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/GlobalNPCScriptPacket.java @@ -84,8 +84,7 @@ public final class GlobalNPCScriptPacket extends AbstractPacket { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { int tab = in.getInt(in.readerIndex()); - if (!PacketUtil.saveScripts(data, in, player)) - return; + PacketUtil.saveScripts(data, in); ScriptController.Instance.lastGlobalNpcUpdate = System.currentTimeMillis(); if (tab == -1) ScriptController.Instance.saveGlobalScriptsSync(); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/NPCScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/NPCScriptPacket.java index 35633cb07..7d5e0d3ed 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/NPCScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/NPCScriptPacket.java @@ -81,19 +81,11 @@ public final class NPCScriptPacket extends AbstractPacket { Action requestedAction = Action.values()[in.readInt()]; if (requestedAction == Action.GET) { - String token = PacketUtil.createScriptSession((EntityPlayerMP) player); NBTTagCompound compound = npc.script.writeToNBT(new NBTTagCompound()); compound.setTag("Languages", ScriptController.Instance.nbtLanguages()); - compound.setString("ScriptSessionToken", token); GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); } else { - NBTTagCompound compound = ByteBufUtils.readNBT(in); - String token = compound.getString("ScriptSessionToken"); - if (!PacketUtil.verifyScriptSession(player, token)) { - LogWriter.error(String.format("Rejected NPC script save from %s: session not verified", player.getCommandSenderName())); - return; - } - npc.script.readFromNBT(compound); + npc.script.readFromNBT(ByteBufUtils.readNBT(in)); npc.updateAI = true; npc.script.hasInited = false; if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/PlayerScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/PlayerScriptPacket.java index 30d999815..242e7d092 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/PlayerScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/PlayerScriptPacket.java @@ -84,8 +84,7 @@ public final class PlayerScriptPacket extends AbstractPacket { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { int tab = in.getInt(in.readerIndex()); - if (!PacketUtil.saveScripts(data, in, player)) - return; + PacketUtil.saveScripts(data, in); ScriptController.Instance.lastPlayerUpdate = System.currentTimeMillis(); if (tab == -1) ScriptController.Instance.savePlayerScriptsSync(); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/RecipeScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/RecipeScriptPacket.java index 36ab65201..3627631eb 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/RecipeScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/RecipeScriptPacket.java @@ -102,8 +102,7 @@ public final class RecipeScriptPacket extends AbstractPacket { if (action == Action.GET) { PacketUtil.getScripts(script, player); } else { - if (!script.saveScript(in, player)) - return; + script.saveScript(in); if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { // TODO: Log Recipe Script Saving } diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/item/ItemScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/item/ItemScriptPacket.java index a1ae83610..7d21242bd 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/item/ItemScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/item/ItemScriptPacket.java @@ -7,7 +7,6 @@ import kamkeel.npcs.network.AbstractPacket; import kamkeel.npcs.network.PacketChannel; import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.PacketHandler; -import kamkeel.npcs.network.PacketUtil; import kamkeel.npcs.network.enums.EnumRequestPacket; import kamkeel.npcs.network.packets.data.large.GuiDataPacket; import kamkeel.npcs.util.ByteBufUtils; @@ -15,7 +14,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.CustomNpcsPermissions; -import noppes.npcs.LogWriter; import noppes.npcs.config.ConfigScript; import noppes.npcs.controllers.ScriptController; import noppes.npcs.scripted.NpcAPI; @@ -74,10 +72,8 @@ public final class ItemScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { ScriptCustomItem iw = (ScriptCustomItem) NpcAPI.Instance().getIItemStack(player.getHeldItem()); iw.loadScriptData(); - String token = PacketUtil.createScriptSession((EntityPlayerMP) player); NBTTagCompound compound = iw.getMCNbt(); compound.setTag("Languages", ScriptController.Instance.nbtLanguages()); - compound.setString("ScriptSessionToken", token); GuiDataPacket.sendGuiData((EntityPlayerMP) player, compound); NBTTagCompound loadComplete = new NBTTagCompound(); @@ -89,11 +85,6 @@ public final class ItemScriptPacket extends AbstractPacket { } NBTTagCompound compound = ByteBufUtils.readNBT(in); - String token = compound.getString("ScriptSessionToken"); - if (!PacketUtil.verifyScriptSession(player, token)) { - LogWriter.error(String.format("Rejected item script save from %s: session not verified", player.getCommandSenderName())); - return; - } ScriptCustomItem wrapper = (ScriptCustomItem) NpcAPI.Instance().getIItemStack(player.getHeldItem()); wrapper.setMCNbt(compound); wrapper.saveScriptData(); diff --git a/src/main/java/kamkeel/npcs/network/packets/request/script/item/LinkedItemScriptPacket.java b/src/main/java/kamkeel/npcs/network/packets/request/script/item/LinkedItemScriptPacket.java index a711aace2..faa14f4d1 100644 --- a/src/main/java/kamkeel/npcs/network/packets/request/script/item/LinkedItemScriptPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/request/script/item/LinkedItemScriptPacket.java @@ -90,8 +90,7 @@ public final class LinkedItemScriptPacket extends AbstractPacket { if (requestedAction == Action.GET) { PacketUtil.getScripts((IScriptHandler) data, (EntityPlayerMP) player); } else { - if (!data.saveScript(in, player)) - return; + data.saveScript(in); if (ConfigDebug.PlayerLogging && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { LogWriter.script(String.format("[%s] (Player) %s SAVED LINKED ITEM [%s]", "LINKED ITEM SCRIPTS", player.getCommandSenderName(), linkedItem.getName())); } diff --git a/src/main/java/noppes/npcs/blocks/tiles/TileScripted.java b/src/main/java/noppes/npcs/blocks/tiles/TileScripted.java index 22e0d4f2f..64dbb767b 100644 --- a/src/main/java/noppes/npcs/blocks/tiles/TileScripted.java +++ b/src/main/java/noppes/npcs/blocks/tiles/TileScripted.java @@ -188,11 +188,8 @@ public class TileScripted extends TileEntity implements IScriptBlockHandler, ISc } @Override - public void sync(String sessionToken) { - NBTTagCompound compound = getNBT(new NBTTagCompound()); - if (sessionToken != null && !sessionToken.isEmpty()) - compound.setString("ScriptSessionToken", sessionToken); - sendSavePacket(-1, getScripts().size(), compound); + public void sync() { + sendSavePacket(-1, getScripts().size(), getNBT(new NBTTagCompound())); } @Override diff --git a/src/main/java/noppes/npcs/client/gui/GuiScript.java b/src/main/java/noppes/npcs/client/gui/GuiScript.java index caa8c74da..7420d4387 100644 --- a/src/main/java/noppes/npcs/client/gui/GuiScript.java +++ b/src/main/java/noppes/npcs/client/gui/GuiScript.java @@ -343,8 +343,6 @@ public class GuiScript extends GuiScriptInterface { @Override public void setGuiData(NBTTagCompound compound) { script.readFromNBT(compound); - if (compound.hasKey("ScriptSessionToken")) - scriptSessionToken = compound.getString("ScriptSessionToken"); loadLanguagesData(compound); loaded = true; } @@ -353,10 +351,7 @@ public class GuiScript extends GuiScriptInterface { public void save() { if (loaded) { setScript(); - NBTTagCompound compound = script.writeToNBT(new NBTTagCompound()); - if (scriptSessionToken != null) - compound.setString("ScriptSessionToken", scriptSessionToken); - NPCScriptPacket.Save(compound); + NPCScriptPacket.Save(script.writeToNBT(new NBTTagCompound())); } } diff --git a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java index 012a6cd5f..3d91446c9 100644 --- a/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java +++ b/src/main/java/noppes/npcs/client/gui/script/GuiScriptInterface.java @@ -49,9 +49,8 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb public Map> languages = new HashMap(); protected int scriptLimit = 1; public List hookList = new ArrayList(); - protected volatile boolean loaded = false; + protected boolean loaded = false; protected boolean serverDataReceived = false; - protected String scriptSessionToken = null; protected Map textAreas = new HashMap<>(); protected GuiScreen parent; @@ -756,8 +755,6 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb return; } if (result.kind == IScriptHandlerPacket.GuiDataKind.METADATA) { - if (compound.hasKey("ScriptSessionToken")) - scriptSessionToken = compound.getString("ScriptSessionToken"); loadLanguagesData(compound); return; } @@ -768,8 +765,6 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb return; } - if (compound.hasKey("ScriptSessionToken")) - scriptSessionToken = compound.getString("ScriptSessionToken"); loadLanguagesData(compound); } @@ -819,7 +814,9 @@ public class GuiScriptInterface extends GuiNPCInterface implements GuiYesNoCallb this.setScript(); if (handler instanceof IScriptHandlerPacket) - ((IScriptHandlerPacket) handler).sync(scriptSessionToken); + ((IScriptHandlerPacket) handler).sync(); + + } public void textUpdate(String text) { diff --git a/src/main/java/noppes/npcs/controllers/data/IScriptHandlerPacket.java b/src/main/java/noppes/npcs/controllers/data/IScriptHandlerPacket.java index 0ff4fcb7e..88ff5fb94 100644 --- a/src/main/java/noppes/npcs/controllers/data/IScriptHandlerPacket.java +++ b/src/main/java/noppes/npcs/controllers/data/IScriptHandlerPacket.java @@ -71,29 +71,16 @@ public interface IScriptHandlerPacket extends IScriptHandler { * GuiScriptInterface is responsible for updating the active {@link IScriptUnit} with current editor text first. */ default void sync() { - sync(null); - } - - /** - * Emit save packets with a session token for server-side verification. - * The token prevents saving unloaded default data due to race conditions. - */ - default void sync(String sessionToken) { List containers = getScripts(); for (int i = 0; i < containers.size(); i++) { IScriptUnit container = containers.get(i); - NBTTagCompound nbt = container.writeToNBT(new NBTTagCompound()); - if (sessionToken != null && !sessionToken.isEmpty()) - nbt.setString("ScriptSessionToken", sessionToken); - sendSavePacket(i, containers.size(), nbt); + sendSavePacket(i, containers.size(), container.writeToNBT(new NBTTagCompound())); } NBTTagCompound scriptData = new NBTTagCompound(); scriptData.setString("ScriptLanguage", getLanguage()); scriptData.setBoolean("ScriptEnabled", getEnabled()); scriptData.setTag("ScriptConsole", NBTTags.NBTLongStringMap(getConsoleText())); - if (sessionToken != null && !sessionToken.isEmpty()) - scriptData.setString("ScriptSessionToken", sessionToken); sendSavePacket(-1, containers.size(), scriptData); } } diff --git a/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java b/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java index 9844a02a3..854e77941 100644 --- a/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java +++ b/src/main/java/noppes/npcs/controllers/data/SingleScriptHandler.java @@ -2,9 +2,7 @@ package noppes.npcs.controllers.data; import cpw.mods.fml.common.eventhandler.Event; import io.netty.buffer.ByteBuf; -import kamkeel.npcs.network.PacketUtil; import kamkeel.npcs.util.ByteBufUtils; -import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.common.util.Constants; import noppes.npcs.CustomNpcs; @@ -119,16 +117,9 @@ public abstract class SingleScriptHandler extends ScriptHandler { * - tab == 0: Script content NBT follows * - tab != 0 (typically -1): Script metadata (language, enabled) follows */ - public boolean saveScript(ByteBuf buffer, EntityPlayer player) throws IOException { + public void saveScript(ByteBuf buffer) throws IOException { int tab = buffer.readInt(); int totalScripts = buffer.readInt(); - NBTTagCompound compound = ByteBufUtils.readNBT(buffer); - - // Verify session token - String token = compound.getString("ScriptSessionToken"); - if (!PacketUtil.verifyScriptSession(player, token)) { - return false; - } if (totalScripts == 0) { this.container = null; @@ -136,14 +127,15 @@ public abstract class SingleScriptHandler extends ScriptHandler { if (tab == 0) { // Script content - this.container = IScriptUnit.createFromNBT(compound, this); + NBTTagCompound tabCompound = ByteBufUtils.readNBT(buffer); + this.container = IScriptUnit.createFromNBT(tabCompound, this); } else { // Metadata (language, enabled) + NBTTagCompound compound = ByteBufUtils.readNBT(buffer); this.setLanguage(compound.getString("ScriptLanguage")); normalizeLanguage(); this.setEnabled(compound.getBoolean("ScriptEnabled")); } - return true; } /** diff --git a/src/main/java/noppes/npcs/scripted/item/ScriptCustomItem.java b/src/main/java/noppes/npcs/scripted/item/ScriptCustomItem.java index b89a241dd..e3641570e 100644 --- a/src/main/java/noppes/npcs/scripted/item/ScriptCustomItem.java +++ b/src/main/java/noppes/npcs/scripted/item/ScriptCustomItem.java @@ -99,11 +99,8 @@ public class ScriptCustomItem extends ScriptCustomizableItem implements IItemCus } @Override - public void sync(String sessionToken) { - NBTTagCompound compound = getMCNbt(); - if (sessionToken != null && !sessionToken.isEmpty()) - compound.setString("ScriptSessionToken", sessionToken); - sendSavePacket(-1, getScripts().size(), compound); + public void sync() { + sendSavePacket(-1, getScripts().size(), getMCNbt()); } public int getType() { From 8c8815dc45d4aac231e32ca9bed4210ff3e71dcb Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 20:56:06 -0500 Subject: [PATCH 3/4] Various Changes / Health Preservation to Request Spawn Data --- .../packets/data/RequestProperSpawnData.java | 50 +++++-------------- .../noppes/npcs/client/ClientTickHandler.java | 2 - .../npcs/entity/EntityNPCInterface.java | 19 ++++++- 3 files changed, 31 insertions(+), 40 deletions(-) diff --git a/src/main/java/kamkeel/npcs/network/packets/data/RequestProperSpawnData.java b/src/main/java/kamkeel/npcs/network/packets/data/RequestProperSpawnData.java index e747bf62c..fce6c13fa 100644 --- a/src/main/java/kamkeel/npcs/network/packets/data/RequestProperSpawnData.java +++ b/src/main/java/kamkeel/npcs/network/packets/data/RequestProperSpawnData.java @@ -9,7 +9,6 @@ import kamkeel.npcs.network.PacketClient; import kamkeel.npcs.network.PacketHandler; import kamkeel.npcs.network.enums.EnumPlayerPacket; import kamkeel.npcs.network.packets.data.npc.UpdateNpcPacket; -import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; @@ -17,16 +16,10 @@ import net.minecraft.nbt.NBTTagCompound; import noppes.npcs.entity.EntityNPCInterface; import java.io.IOException; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; public class RequestProperSpawnData extends AbstractPacket { public static final String packetName = "Player|SpawnData"; - private static final int BATCH_LIMIT = 5; - - private static final Set entitiesToFix = new HashSet<>(); - private static long lastBatchAttemptMillis = 0L; + private static final long REQUEST_COOLDOWN_MILLIS = 1000L; private int entityId; public RequestProperSpawnData() {} @@ -37,8 +30,7 @@ public class RequestProperSpawnData extends AbstractPacket { @SideOnly(Side.CLIENT) public static void clear() { - entitiesToFix.clear(); - lastBatchAttemptMillis = 0L; + // No backlog queue to clear. } @Override @@ -81,40 +73,24 @@ public class RequestProperSpawnData extends AbstractPacket { if (entityID <= 0) return; - if (npc.clientFixAttempts++ < 3) { - PacketClient.sendClient(new RequestProperSpawnData(entityID)); - } else { - entitiesToFix.add(entityID); - } + long now = System.currentTimeMillis(); + if (now - npc.clientFixLastRequestMillis < REQUEST_COOLDOWN_MILLIS) + return; + if (npc.clientFixAttempts >= 3) + return; + + npc.clientFixLastRequestMillis = now; + npc.clientFixAttempts++; + PacketClient.sendClient(new RequestProperSpawnData(entityID)); } @SideOnly(Side.CLIENT) public static boolean canDoBatchUpdate() { - return System.currentTimeMillis() - lastBatchAttemptMillis >= 5000; + return false; } @SideOnly(Side.CLIENT) public static void handleBacklog() { - Minecraft mc = Minecraft.getMinecraft(); - if (mc == null || mc.theWorld == null || mc.thePlayer == null) - return; - - if (entitiesToFix.isEmpty()) - return; - // Remove entity checks if they're no longer loaded - entitiesToFix.removeIf(value -> !(mc.theWorld.getEntityByID(value) instanceof EntityNPCInterface)); - - Iterator it = entitiesToFix.iterator(); - int count = 0; - - while (it.hasNext() && count < BATCH_LIMIT) { - PacketClient.sendClient(new RequestProperSpawnData(it.next())); - it.remove(); - count++; - } - - if (count != 0) { - lastBatchAttemptMillis = System.currentTimeMillis(); - } + // No batch backlog. Requests are immediate and throttled in reportMissingData(). } } diff --git a/src/main/java/noppes/npcs/client/ClientTickHandler.java b/src/main/java/noppes/npcs/client/ClientTickHandler.java index c0b501c4d..1f6e1bf34 100644 --- a/src/main/java/noppes/npcs/client/ClientTickHandler.java +++ b/src/main/java/noppes/npcs/client/ClientTickHandler.java @@ -193,8 +193,6 @@ public class ClientTickHandler { return; if (mc.theWorld.getTotalWorldTime() % 20 == 0) { // Update every second - if (RequestProperSpawnData.canDoBatchUpdate()) - RequestProperSpawnData.handleBacklog(); updateCompassMarks(); } } diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 6045317d8..3eed36105 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -236,6 +236,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti // For Client-Side Use Only public int clientFixAttempts = 0; + public long clientFixLastRequestMillis = 0L; public EntityNPCInterface(World world) { super(world); @@ -483,7 +484,14 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti } if (wasKilled != isKilled() && wasKilled) { - reset(); + if (!worldObj.isRemote) { + reset(); + } else { + // Client-side full reset can create health oscillation when spawn data is being repaired. + deathTime = 0; + currentAnimation = EnumAnimation.NONE; + updateHitbox(); + } } wasKilled = isKilled(); @@ -1838,6 +1846,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti NBTTagCompound compound = new NBTTagCompound(); display.writeToNBT(compound); compound.setDouble("MaxHealth", stats.maxHealth); + compound.setFloat("CurrentHealth", getHealth()); compound.setTag("Armor", NBTTags.nbtItemStackList(inventory.getArmor())); compound.setTag("Weapons", NBTTags.nbtItemStackList(inventory.getWeapons())); compound.setInteger("Speed", ais.getWalkingSpeed()); @@ -1892,6 +1901,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti return; } + float preservedHealth = getHealth(); stats.maxHealth = compound.getDouble("MaxHealth"); ais.setWalkingSpeed(compound.getInteger("Speed")); stats.hideKilledBody = compound.getBoolean("DeadBody"); @@ -1916,6 +1926,12 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti ais.bodyOffsetY = compound.getFloat("OffsetY"); this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(stats.maxHealth); + if (compound.hasKey("CurrentHealth", Constants.NBT.TAG_FLOAT)) { + setHealth(MathHelper.clamp_float(compound.getFloat("CurrentHealth"), 0.0F, getMaxHealth())); + } else { + // Keep previous client health if packet came from an older sender format. + setHealth(MathHelper.clamp_float(preservedHealth, 0.0F, getMaxHealth())); + } inventory.setArmor(NBTTags.getItemStackList(compound.getTagList("Armor", 10))); inventory.setWeapons(NBTTags.getItemStackList(compound.getTagList("Weapons", 10))); advanced.setRole(compound.getInteger("Role")); @@ -1990,6 +2006,7 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti return true; } clientFixAttempts = 0; + clientFixLastRequestMillis = 0L; return false; } From 82f9ecaf4a0e439c1ac7626e8a22c88e5f4d1ba5 Mon Sep 17 00:00:00 2001 From: Vidal Date: Thu, 5 Mar 2026 01:45:58 -0300 Subject: [PATCH 4/4] Submodules --- gradle-plugins | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugins b/gradle-plugins index dc0350128..19dc9a836 160000 --- a/gradle-plugins +++ b/gradle-plugins @@ -1 +1 @@ -Subproject commit dc0350128ae8bc81ddde6615816b29f35de2959c +Subproject commit 19dc9a836148062fa744b9a9ca87391f84274b44