- Fix Counter infinite recursion

- Tags Get Packet
- Cooldowns Resetting on Dimension Change / Teleport
This commit is contained in:
KAMKEEL committed 2026-04-20 21:00:54 -04:00
1 parent 1a77728bcd
commit 4a7c7b09c7
4 files changed
+60 -13

No files matched your search

@@ -3,14 +3,21 @@ package kamkeel.npcs.controllers.data.ability.type;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import kamkeel.npcs.addon.DBCAddon;
import noppes.npcs.AbstractDataAbilities;
import noppes.npcs.controllers.AnimationController;
import kamkeel.npcs.controllers.data.ability.Ability;
import kamkeel.npcs.controllers.data.ability.enums.UserType;
import kamkeel.npcs.controllers.data.ability.gui.AbilityFieldDefs;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.DamageSource;
import noppes.npcs.controllers.data.Animation;
import noppes.npcs.controllers.data.PlayerData;
import noppes.npcs.api.ability.type.IAbilityCounter;
import noppes.npcs.client.gui.builder.FieldDef;
import noppes.npcs.entity.EntityNPCInterface;
import java.util.Arrays;
import java.util.List;
@@ -25,6 +32,11 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
private int counterAnimationId = -1;
private String counterAnimationName = "Ability_Guard_Counter";
// True only while this counter is synchronously dispatching its retaliation damage.
// Another counter that is hit by that retaliation inspects this flag on the source
// entity's active counter to avoid reacting and creating infinite mutual retaliation.
private transient boolean retaliating;
public AbilityCounter() {
this.typeId = "ability.cnpc.counter";
this.name = "Counter";
@@ -60,6 +72,35 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
// DEFEND HOOKS
// ═══════════════════════════════════════════════════════════════════
@Override
protected boolean isValidDamageSource(DamageSource source) {
if (!super.isValidDamageSource(source)) {
return false;
}
// Break counter-vs-counter recursion: if the hit is itself a retaliation
// from another counter that is mid-dispatch, don't counter it back.
Entity sourceEntity = source.getEntity();
AbstractDataAbilities abilities = getAbilitiesOf(sourceEntity);
if (abilities != null) {
Ability current = abilities.getCurrentAbility();
if (current instanceof AbilityCounter && ((AbilityCounter) current).retaliating) {
return false;
}
}
return true;
}
private static AbstractDataAbilities getAbilitiesOf(Entity entity) {
if (entity instanceof EntityPlayer) {
PlayerData pData = PlayerData.get((EntityPlayer) entity);
return pData != null ? pData.abilityData : null;
}
if (entity instanceof EntityNPCInterface) {
return ((EntityNPCInterface) entity).abilities;
}
return null;
}
@Override
protected float performDefend(EntityLivingBase attacker, float amount) {
// For PERCENT mode with DBC, use the full DBC-calculated damage, not vanilla base
@@ -78,13 +119,24 @@ public class AbilityCounter extends AbilityDefend implements IAbilityCounter {
// Counter-attack through the standard ability damage pipeline.
// DBCAbilityExtender handles DBC stat routing (ignore flags, scaling, etc.) automatically.
if (caster != null && attacker.isEntityAlive() && damage > 0) {
applyAbilityDamage(caster, attacker, damage, 0);
if (caster != null && attacker != null && attacker.isEntityAlive() && damage > 0) {
retaliating = true;
try {
applyAbilityDamage(caster, attacker, damage, 0);
} finally {
retaliating = false;
}
}
return 0;
}
@Override
public void reset() {
super.reset();
retaliating = false;
}
// ═══════════════════════════════════════════════════════════════════
// OVERRIDES
// ═══════════════════════════════════════════════════════════════════
@@ -73,7 +73,7 @@ public final class CategoryItemsRequestPacket extends AbstractPacket {
// Send tag map for the items
HashMap<String, HashSet<UUID>> tagMap = getTagMapByCat(type, categoryId);
if (tagMap != null && !tagMap.isEmpty()) {
if (tagMap != null) {
TagController.sendCategoryTagMap((EntityPlayerMP) player, tagMap);
}
}
@@ -6,8 +6,6 @@ import io.netty.buffer.ByteBuf;
import kamkeel.npcs.network.AbstractPacket;
import kamkeel.npcs.network.PacketChannel;
import kamkeel.npcs.network.PacketHandler;
import kamkeel.npcs.network.PacketUtil;
import kamkeel.npcs.network.enums.EnumItemPacketType;
import kamkeel.npcs.network.enums.EnumRequestPacket;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
@@ -38,9 +36,6 @@ public final class TagsGetPacket extends AbstractPacket {
@Override
public void receiveData(ByteBuf in, EntityPlayer player) throws IOException {
if (!PacketUtil.verifyItemPacket(packetName, player, EnumItemPacketType.WAND, EnumItemPacketType.CLONER))
return;
NoppesUtilServer.sendTagDataAll((EntityPlayerMP) player);
}
@@ -773,21 +773,21 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
}
/**
* Fully reset ability state when changing dimensions.
* Clears cooldowns so the player can immediately use abilities in the new dimension.
* Fully reset transient ability execution state when changing dimensions.
* Cooldowns are preserved so dimension changes cannot bypass them.
* Keeps active toggles (dimension change shouldn't reset ongoing effects).
*/
public void resetOnDimensionChange() {
trackingAbilityPosition = false;
onEntityReconstructed(true, false);
onEntityReconstructed(false, false);
}
/**
* Reset ability state when a large-distance teleport is detected during execution.
* Same behavior as dimension change — silent cleanup, clear cooldowns, keep toggles.
* Same behavior as dimension change: silent cleanup, preserve cooldowns, keep toggles.
*/
public void resetOnTeleport() {
onEntityReconstructed(true, false);
onEntityReconstructed(false, false);
}
/**