From b72e6e3466295c07974ebbd10eeba2096ead436f Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 02:02:54 -0500 Subject: [PATCH 1/6] Update EffectScriptPacket.java --- .../npcs/network/packets/request/script/EffectScriptPacket.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 705e191be..fc5dded0e 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 @@ -93,7 +93,7 @@ public final class EffectScriptPacket extends AbstractPacket { if (!data.saveScript(in, player)) return; 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())); + LogWriter.script(String.format("[%s] (Player) %s SAVED EFFECT %s [%s]", "EFFECT SCRIPTS", player.getCommandSenderName(), effect.getName(), effect.id)); } } } From bd9d56943ebc9c8fcaf822fc10dd78f01c8a1435 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 04:32:54 -0500 Subject: [PATCH 2/6] Fix Push Through wall Teleport Logic for Barriers --- .../npcs/entity/EntityEnergyBarrier.java | 33 +++++++++++++++++ .../kamkeel/npcs/entity/EntityEnergyDome.java | 33 ++++++++++------- .../npcs/entity/EntityEnergyPanel.java | 36 ++++++++++++------- 3 files changed, 78 insertions(+), 24 deletions(-) diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java index 9111e7dd8..b6e190fe7 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyBarrier.java @@ -9,6 +9,7 @@ import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.DamageSource; import net.minecraft.world.World; import noppes.npcs.CustomNpcs; @@ -672,8 +673,11 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility { /** * Teleport an entity to a position. Uses network handler for players * to ensure reliable client synchronization. + * Skips teleport if destination intersects solid blocks. */ protected void teleportEntity(EntityLivingBase ent, double x, double y, double z) { + if (!isPositionClear(ent, x, y, z)) return; + if (ent instanceof EntityPlayerMP) { EntityPlayerMP player = (EntityPlayerMP) ent; player.playerNetServerHandler.setPlayerLocation(x, y, z, player.rotationYaw, player.rotationPitch); @@ -682,6 +686,35 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility { } } + /** + * Returns true if placing the entity at (x, y, z) would not intersect solid blocks. + */ + protected boolean isPositionClear(EntityLivingBase ent, double x, double y, double z) { + double halfW = ent.width * 0.5; + AxisAlignedBB box = AxisAlignedBB.getBoundingBox( + x - halfW, y, z - halfW, + x + halfW, y + ent.height, z + halfW + ); + return worldObj.getCollidingBoundingBoxes(ent, box).isEmpty(); + } + + /** + * Returns true if the entity can be pushed in the given direction without hitting solid blocks. + * Probes a small distance (0.3 blocks) ahead in the push direction. + */ + protected boolean canPushInDirection(EntityLivingBase ent, double dirX, double dirY, double dirZ) { + double probe = 0.3; + double halfW = ent.width * 0.5; + double px = ent.posX + dirX * probe; + double py = ent.posY + dirY * probe; + double pz = ent.posZ + dirZ * probe; + AxisAlignedBB box = AxisAlignedBB.getBoundingBox( + px - halfW, py, pz - halfW, + px + halfW, py + ent.height, pz + halfW + ); + return worldObj.getCollidingBoundingBoxes(ent, box).isEmpty(); + } + // ==================== GETTERS ==================== public EnergyBarrierData getBarrierData() { diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java index 535d1cdff..2c0277a65 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyDome.java @@ -371,8 +371,10 @@ public class EntityEnergyDome extends EntityEnergyBarrier { ent.motionY -= radialVel * ny; ent.motionZ -= radialVel * nz; double pushForce = wasInside ? -0.15 : 0.15; - ent.motionX += nx * pushForce; - ent.motionZ += nz * pushForce; + if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) { + ent.motionX += nx * pushForce; + ent.motionZ += nz * pushForce; + } ent.velocityChanged = true; } else { // Preemptive: near surface and moving toward it — cancel radial velocity @@ -385,8 +387,10 @@ public class EntityEnergyDome extends EntityEnergyBarrier { ent.motionY -= radialVel * ny; ent.motionZ -= radialVel * nz; double pushForce = isInside ? -0.05 : 0.05; - ent.motionX += nx * pushForce; - ent.motionZ += nz * pushForce; + if (canPushInDirection(ent, nx * pushForce, 0, nz * pushForce)) { + ent.motionX += nx * pushForce; + ent.motionZ += nz * pushForce; + } ent.velocityChanged = true; } } @@ -400,16 +404,21 @@ public class EntityEnergyDome extends EntityEnergyBarrier { double proximity = 1.0 - (surfaceDist / knockbackMargin); double force = proximity * strength * 0.06; + double pushX, pushY, pushZ; if (isInside) { - // Push toward center (away from surface) - ent.motionX += -nx * force; - ent.motionY += -ny * force * 0.5; - ent.motionZ += -nz * force; + pushX = -nx * force; + pushY = -ny * force * 0.5; + pushZ = -nz * force; } else { - // Push outward (away from surface) - ent.motionX += nx * force; - ent.motionY += ny * force * 0.5; - ent.motionZ += nz * force; + pushX = nx * force; + pushY = ny * force * 0.5; + pushZ = nz * force; + } + + if (canPushInDirection(ent, pushX, pushY, pushZ)) { + ent.motionX += pushX; + ent.motionY += pushY; + ent.motionZ += pushZ; } ent.velocityChanged = true; } diff --git a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java index eab43440f..ec77f8248 100644 --- a/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java +++ b/src/main/java/kamkeel/npcs/entity/EntityEnergyPanel.java @@ -257,12 +257,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier { double dz = target.posZ - posZ; double len = Math.sqrt(dx * dx + dz * dz); if (len > 0) { - target.addVelocity( - (dx / len) * panelData.launchKnockback * 0.5, - 0.1, - (dz / len) * panelData.launchKnockback * 0.5 - ); - target.velocityChanged = true; + double kbX = (dx / len) * panelData.launchKnockback * 0.5; + double kbZ = (dz / len) * panelData.launchKnockback * 0.5; + if (canPushInDirection(target, kbX, 0.1, kbZ)) { + target.addVelocity(kbX, 0.1, kbZ); + target.velocityChanged = true; + } } } } @@ -580,8 +580,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier { // Cancel normal velocity and add corrective push ent.motionX -= normalVel * normalX; ent.motionZ -= normalVel * normalZ; - ent.motionX += normalX * prevSide * 0.15; - ent.motionZ += normalZ * prevSide * 0.15; + double pushX = normalX * prevSide * 0.15; + double pushZ = normalZ * prevSide * 0.15; + if (canPushInDirection(ent, pushX, 0, pushZ)) { + ent.motionX += pushX; + ent.motionZ += pushZ; + } ent.velocityChanged = true; } else { // Preemptive: near the plane and moving toward it — cancel normal velocity @@ -592,8 +596,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier { if (movingToward) { ent.motionX -= normalVel * normalX; ent.motionZ -= normalVel * normalZ; - ent.motionX += normalX * side * 0.05; - ent.motionZ += normalZ * side * 0.05; + double pushX = normalX * side * 0.05; + double pushZ = normalZ * side * 0.05; + if (canPushInDirection(ent, pushX, 0, pushZ)) { + ent.motionX += pushX; + ent.motionZ += pushZ; + } ent.velocityChanged = true; } } @@ -606,8 +614,12 @@ public class EntityEnergyPanel extends EntityEnergyBarrier { if (absDist < searchExtension) { double proximity = 1.0 - (absDist / searchExtension); double force = proximity * strength * 0.06; - ent.motionX += normalX * side * force; - ent.motionZ += normalZ * side * force; + double pushX = normalX * side * force; + double pushZ = normalZ * side * force; + if (canPushInDirection(ent, pushX, 0, pushZ)) { + ent.motionX += pushX; + ent.motionZ += pushZ; + } ent.velocityChanged = true; } } From d1b32ea8a1bd2ad53e2deb9e4d78379d5221d2a0 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 15:59:41 -0500 Subject: [PATCH 3/6] Fix Timer not being able to be called from a Spawning NPC [Revert if not working] --- .../npcs/controllers/ScriptContainer.java | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/noppes/npcs/controllers/ScriptContainer.java b/src/main/java/noppes/npcs/controllers/ScriptContainer.java index e8dbe22b1..7cd691c49 100644 --- a/src/main/java/noppes/npcs/controllers/ScriptContainer.java +++ b/src/main/java/noppes/npcs/controllers/ScriptContainer.java @@ -14,6 +14,7 @@ import noppes.npcs.scripted.NpcAPI; import javax.script.Bindings; import javax.script.Compilable; import javax.script.CompiledScript; +import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; import java.io.PrintWriter; @@ -272,20 +273,18 @@ public class ScriptContainer implements IScriptUnit { unknownFunctions.add(type); } } else { - if (!this.cachedFunctions.containsKey(type)) { - ScriptObjectMirror func; - if (instanceBindings != null) { + if (instanceBindings != null) { + if (!this.cachedFunctions.containsKey(type)) { Object val = instanceBindings.get(type); - func = val instanceof ScriptObjectMirror ? (ScriptObjectMirror) val : null; - } else { - ScriptObjectMirror global = (ScriptObjectMirror) engine.getBindings(ScriptContext.ENGINE_SCOPE); - func = (ScriptObjectMirror) global.get(type); + ScriptObjectMirror func = val instanceof ScriptObjectMirror ? (ScriptObjectMirror) val : null; + this.cachedFunctions.put(type, func); } - this.cachedFunctions.put(type, func); - } - ScriptObjectMirror func = this.cachedFunctions.get(type); - if (func != null) { - func.call(null, event); + ScriptObjectMirror func = this.cachedFunctions.get(type); + if (func != null) { + func.call(null, event); + } + } else { + ((Invocable) engine).invokeFunction(type, event); } } } catch (NoSuchMethodException e) { From a981060e9e02cf0d27c0282d4ad135066711fc34 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 16:05:09 -0500 Subject: [PATCH 4/6] Crazy Logging for Health Spam Debugging --- .../npcs/entity/EntityNPCInterface.java | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java index 6045317d8..8326cacf9 100644 --- a/src/main/java/noppes/npcs/entity/EntityNPCInterface.java +++ b/src/main/java/noppes/npcs/entity/EntityNPCInterface.java @@ -85,6 +85,7 @@ import noppes.npcs.DataInventory; import noppes.npcs.DataStats; import noppes.npcs.EventHooks; import noppes.npcs.IChatMessages; +import noppes.npcs.LogWriter; import noppes.npcs.NBTTags; import noppes.npcs.NoppesUtilPlayer; import noppes.npcs.NoppesUtilServer; @@ -482,8 +483,42 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti abilities.tick(); } + // DEBUG: Log client-side health state every tick to diagnose DI green health spam + if (worldObj.isRemote && ticksExisted % 20 == 0) { + float h = getHealth(); + float mh = (float) getMaxHealth(); + int flags = dataWatcher.getWatchableObjectInt(15); + boolean killed = isKilled(); + LogWriter.info("[CLIENT-HEALTH-DEBUG] NPC=" + display.name + + " id=" + getEntityId() + + " health=" + h + + " maxHealth=" + mh + + " flags=" + Integer.toBinaryString(flags) + + " isKilled=" + killed + + " wasKilled=" + wasKilled + + " isDead=" + isDead + + " deathTime=" + deathTime + + " statsMaxHP=" + stats.maxHealth + + " tick=" + ticksExisted); + } + if (wasKilled != isKilled() && wasKilled) { - reset(); + if (!worldObj.isRemote) { + reset(); + } else { + // Client: only reset visual state. Health/flags are synced via DataWatcher. + // Full reset() on client can cause health oscillation when maxHealth is desynced. + LogWriter.info("[CLIENT-RESET-DEBUG] NPC=" + display.name + + " id=" + getEntityId() + + " health=" + getHealth() + + " maxHealth=" + (float) getMaxHealth() + + " wasKilled=" + wasKilled + + " isKilled=" + isKilled() + + " tick=" + ticksExisted); + deathTime = 0; + currentAnimation = EnumAnimation.NONE; + updateHitbox(); + } } wasKilled = isKilled(); @@ -1881,6 +1916,10 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti readSpawnData(ByteBufUtils.readNBT(buf)); } catch (IOException e) { if (this.worldObj != null && this.worldObj.isRemote) { + LogWriter.info("[SPAWN-DATA-DEBUG] ByteBuf readSpawnData FAILED for id=" + getEntityId() + + " health=" + getHealth() + + " maxHealth=" + (float) getMaxHealth() + + " statsMaxHP=" + stats.maxHealth); RequestProperSpawnData.reportMissingData(this); } } @@ -1888,8 +1927,17 @@ public abstract class EntityNPCInterface extends EntityCreature implements IEnti public void readSpawnData(NBTTagCompound compound) { if (this.worldObj != null && this.worldObj.isRemote) { - if(requestClientFix(compound)) + if(requestClientFix(compound)) { + LogWriter.info("[SPAWN-DATA-DEBUG] requestClientFix returned true (bad compound) for id=" + getEntityId() + + " health=" + getHealth() + + " maxHealth=" + (float) getMaxHealth() + + " statsMaxHP=" + stats.maxHealth); return; + } + LogWriter.info("[SPAWN-DATA-DEBUG] readSpawnData SUCCESS for id=" + getEntityId() + + " health=" + getHealth() + + " oldMaxHP=" + (float) getMaxHealth() + + " newMaxHP=" + compound.getDouble("MaxHealth")); } stats.maxHealth = compound.getDouble("MaxHealth"); From d3346111c731443a7ab6455bfc33564b5a031ef7 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 16:19:42 -0500 Subject: [PATCH 5/6] Animation Resync on Teleport --- .../controllers/data/PlayerAbilityData.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java index 1330b34fa..b6aa3f4d0 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerAbilityData.java @@ -8,6 +8,8 @@ import kamkeel.npcs.controllers.data.ability.data.ChainedAbility; import kamkeel.npcs.controllers.data.ability.data.IAbilityAction; import kamkeel.npcs.controllers.data.ability.data.entry.AbilityToggleEntry; import kamkeel.npcs.controllers.data.telegraph.TelegraphInstance; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.packets.data.UpdateAnimationsPacket; import kamkeel.npcs.network.packets.data.ability.AbilityCooldownSyncPacket; import kamkeel.npcs.network.packets.data.ability.PlayerAbilityStatePacket; import kamkeel.npcs.network.packets.data.ability.PlayerAbilitySyncPacket; @@ -177,6 +179,22 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA protected void clearAnimationData() { playerData.animationData.setAnimation(null); playerData.animationData.updateClient(); + + // Directly send animation clear to the owning player. updateClient() uses + // getEntitiesWithinAABB to find recipients, but during teleport the player + // entity hasn't migrated to the new chunk yet — so the AABB search centered + // on the new position won't find them, and the clear packet is never delivered. + // This guarantees the player's client always receives the clear. + EntityPlayer player = playerData.player; + if (player instanceof EntityPlayerMP) { + NBTTagCompound data = new NBTTagCompound(); + data.setBoolean("AllowAnimation", playerData.animationData.enabled()); + PacketHandler.Instance.sendToPlayer( + new UpdateAnimationsPacket(data, player.getCommandSenderName()), + (EntityPlayerMP) player + ); + } + playingAbilityAnimation = false; } From 5a879cd6a56ac67a869b66a80ff59cc291e6f111 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 4 Mar 2026 16:21:22 -0500 Subject: [PATCH 6/6] Beta 13.1 --- build.gradle | 2 +- src/main/java/noppes/npcs/CustomNpcs.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index c5ac8e73f..bcdbd58f1 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ plugins { id 'idea' } -version = "1.11-beta13" +version = "1.11-beta13.1" group= "kamkeel.customnpc-plus" archivesBaseName = "CustomNPC-Plus" diff --git a/src/main/java/noppes/npcs/CustomNpcs.java b/src/main/java/noppes/npcs/CustomNpcs.java index c3c6b1646..8fa190ecc 100644 --- a/src/main/java/noppes/npcs/CustomNpcs.java +++ b/src/main/java/noppes/npcs/CustomNpcs.java @@ -124,7 +124,7 @@ import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; -@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-beta13") +@Mod(modid = "customnpcs", name = "CustomNPC+", version = "1.11-beta13.1") public class CustomNpcs { @SidedProxy(clientSide = "noppes.npcs.client.ClientProxy", serverSide = "noppes.npcs.CommonProxy")