Add Max to Energy Entities to prevent any long-term issues / errors

This commit is contained in:
Kam committed 2026-02-19 02:14:34 -05:00
1 parent e330b517a8
commit 6fdf9daf24
21 files changed
+102 -48

No files matched your search

@@ -47,6 +47,10 @@ public class EnergyLifespanData implements IEnergyLifespanData {
public void readNBT(NBTTagCompound nbt) {
maxDistance = nbt.getFloat("maxDistance");
maxLifetime = nbt.getInteger("maxLifetime");
// Sanitize: ensure minimum values so entities don't die instantly or live forever
if (Float.isNaN(maxDistance) || Float.isInfinite(maxDistance) || maxDistance <= 0) maxDistance = 150.0f;
if (maxLifetime <= 0) maxLifetime = 200;
}
public EnergyLifespanData copy() {
@@ -184,12 +184,12 @@ public class AbilityBeam extends AbilityEnergyProjectile<EntityAbilityBeam> impl
defs.add(FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback));
defs.add(FieldDef.section("ability.section.beam"));
defs.add(FieldDef.row(
FieldDef.floatField("ability.beamWidth", this::getBeamWidth, this::setBeamWidth),
FieldDef.floatField("ability.headSize", this::getHeadSize, this::setHeadSize)
FieldDef.floatField("ability.beamWidth", this::getBeamWidth, this::setBeamWidth).range(0.1f, 100.0f),
FieldDef.floatField("ability.headSize", this::getHeadSize, this::setHeadSize).range(0.1f, 100.0f)
));
defs.add(FieldDef.row(
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime)
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).range(1.0f, 500.0f),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
));
defs.add(FieldDef.section("ability.section.homing"));
defs.add(FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming).hover("ability.hover.homing"));
@@ -208,14 +208,14 @@ public class AbilityDisc extends AbilityEnergyProjectile<EntityAbilityDisc> impl
defs.add(FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback));
defs.add(FieldDef.section("ability.section.disc"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.radius", this::getDiscRadius, this::setDiscRadius),
FieldDef.floatField("gui.thickness", this::getDiscThickness, this::setDiscThickness)
FieldDef.floatField("gui.radius", this::getDiscRadius, this::setDiscRadius).range(0.1f, 100.0f),
FieldDef.floatField("gui.thickness", this::getDiscThickness, this::setDiscThickness).range(0.05f, 100.0f)
));
defs.add(FieldDef.boolField("ability.vertical", this::isVertical, this::setVertical)
.hover("ability.hover.vertical"));
defs.add(FieldDef.row(
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime)
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).range(1.0f, 500.0f),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
));
defs.add(FieldDef.section("ability.section.homing"));
defs.add(FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming).hover("ability.hover.homing"));
@@ -132,7 +132,7 @@ public class AbilityDome extends AbilityEnergyBarrier {
@Override
protected void addBarrierTypeDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.floatField("ability.domeRadius", this::getDomeRadius, this::setDomeRadius)
.range(1.0f, 30.0f));
.range(1.0f, 50.0f));
defs.add(FieldDef.boolField("ability.followCaster", this::isFollowCaster, this::setFollowCaster)
.hover("ability.hover.followCaster"));
}
@@ -247,7 +247,7 @@ public abstract class AbilityEnergyBarrier extends AbilityEnergy {
.visibleWhen(this::isUseHealth));
defs.add(FieldDef.boolField("ability.useDuration", this::isUseDuration, this::setUseDuration));
defs.add(FieldDef.intField("ability.duration", this::getBarrierDuration, this::setBarrierDuration)
.range(1, 6000).visibleWhen(this::isUseDuration));
.range(1, 12000).visibleWhen(this::isUseDuration));
defs.add(FieldDef.floatField("ability.defaultMultiplier", this::getDefaultMultiplier, this::setDefaultMultiplier));
// Knockback section
@@ -218,14 +218,14 @@ public class AbilityLaserShot extends AbilityEnergyProjectile<EntityAbilityLaser
));
defs.add(FieldDef.section("ability.section.beam"));
defs.add(FieldDef.row(
FieldDef.floatField("ability.laserWidth", this::getLaserWidth, this::setLaserWidth),
FieldDef.floatField("ability.expansionSpeed", this::getExpansionSpeed, this::setExpansionSpeed)
FieldDef.floatField("ability.laserWidth", this::getLaserWidth, this::setLaserWidth).range(0.1f, 100.0f),
FieldDef.floatField("ability.expansionSpeed", this::getExpansionSpeed, this::setExpansionSpeed).range(0.1f, 50.0f)
));
defs.add(FieldDef.row(
FieldDef.intField("ability.lingerTicks", this::getLingerTicks, this::setLingerTicks),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime)
FieldDef.intField("ability.lingerTicks", this::getLingerTicks, this::setLingerTicks).range(1, 200),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
));
defs.add(FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance));
defs.add(FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).range(1.0f, 500.0f));
defs.add(FieldDef.boolField("ability.dieOnImpact", this::isDieOnImpact, this::setDieOnImpact));
defs.add(FieldDef.section("ability.section.explosive"));
defs.add(FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"));
@@ -172,12 +172,12 @@ public class AbilityOrb extends AbilityEnergyProjectile<EntityAbilityOrb> implem
FieldDef.floatField("stats.speed", this::getOrbSpeed, this::setOrbSpeed)
));
defs.add(FieldDef.row(
FieldDef.floatField("stats.size", this::getOrbSize, this::setOrbSize),
FieldDef.floatField("stats.size", this::getOrbSize, this::setOrbSize).range(0.1f, 100.0f),
FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback)
));
defs.add(FieldDef.row(
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime)
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).range(1.0f, 500.0f),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
));
defs.add(FieldDef.section("ability.section.homing"));
defs.add(FieldDef.boolField("gui.enabled", this::isHoming, this::setHoming).hover("ability.hover.homing"));
@@ -145,8 +145,8 @@ public class AbilityShield extends AbilityEnergyBarrier {
@Override
protected void addBarrierTypeDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.row(
FieldDef.floatField("gui.width", this::getShieldWidth, this::setShieldWidth).range(0.5f, 10.0f),
FieldDef.floatField("gui.height", this::getShieldHeight, this::setShieldHeight).range(0.5f, 10.0f)
FieldDef.floatField("gui.width", this::getShieldWidth, this::setShieldWidth).range(0.5f, 100.0f),
FieldDef.floatField("gui.height", this::getShieldHeight, this::setShieldHeight).range(0.5f, 100.0f)
));
}
}
@@ -183,14 +183,14 @@ public class AbilitySlicer extends AbilityEnergyProjectile<EntityEnergySlicer> {
defs.add(FieldDef.floatField("ability.knockback", this::getKnockback, this::setKnockback));
defs.add(FieldDef.section("ability.section.slicer"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.width", this::getSliceWidth, this::setSliceWidth).range(0.5f, 20.0f),
FieldDef.floatField("gui.thickness", this::getSliceThickness, this::setSliceThickness).range(0.05f, 2.0f)
FieldDef.floatField("gui.width", this::getSliceWidth, this::setSliceWidth).range(0.5f, 100.0f),
FieldDef.floatField("gui.thickness", this::getSliceThickness, this::setSliceThickness).range(0.05f, 100.0f)
));
defs.add(FieldDef.boolField("ability.piercing", this::isPiercing, this::setPiercing)
.hover("ability.hover.piercing"));
defs.add(FieldDef.row(
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime)
FieldDef.floatField("ability.maxDistance", this::getMaxDistance, this::setMaxDistance).range(1.0f, 500.0f),
FieldDef.intField("ability.lifetime", this::getMaxLifetime, this::setMaxLifetime).range(1, 1200)
));
defs.add(FieldDef.section("ability.section.explosive"));
defs.add(FieldDef.boolField("gui.enabled", this::isExplosive, this::setExplosive).hover("ability.hover.explosive"));
@@ -260,14 +260,14 @@ public class AbilitySweeper extends AbilityEnergy implements IAbilitySweeper {
),
FieldDef.section("ability.section.beam"),
FieldDef.row(
FieldDef.floatField("gui.length", this::getBeamLength, this::setBeamLength),
FieldDef.floatField("gui.width", this::getBeamWidth, this::setBeamWidth)
FieldDef.floatField("gui.length", this::getBeamLength, this::setBeamLength).range(0.5f, 100.0f),
FieldDef.floatField("gui.width", this::getBeamWidth, this::setBeamWidth).range(0.1f, 100.0f)
),
FieldDef.row(
FieldDef.floatField("gui.height", this::getBeamHeight, this::setBeamHeight),
FieldDef.floatField("ability.sweepSpeed", this::getSweepSpeed, this::setSweepSpeed)
FieldDef.floatField("gui.height", this::getBeamHeight, this::setBeamHeight).range(0.1f, 100.0f),
FieldDef.floatField("ability.sweepSpeed", this::getSweepSpeed, this::setSweepSpeed).range(0.1f, 30.0f)
),
FieldDef.intField("ability.rotations", this::getNumberOfRotations, this::setNumberOfRotations),
FieldDef.intField("ability.rotations", this::getNumberOfRotations, this::setNumberOfRotations).range(1, 20),
FieldDef.row(
FieldDef.boolField("ability.piercing", this::isPiercing, this::setPiercing)
.hover("ability.hover.piercing"),
@@ -205,8 +205,8 @@ public class AbilityWall extends AbilityEnergyBarrier {
protected void addBarrierTypeDefinitions(List<FieldDef> defs) {
defs.add(FieldDef.section("ability.section.panel"));
defs.add(FieldDef.row(
FieldDef.floatField("gui.width", this::getPanelWidth, this::setPanelWidth).range(0.5f, 20.0f),
FieldDef.floatField("gui.height", this::getPanelHeight, this::setPanelHeight).range(0.5f, 20.0f)
FieldDef.floatField("gui.width", this::getPanelWidth, this::setPanelWidth).range(0.5f, 100.0f),
FieldDef.floatField("gui.height", this::getPanelHeight, this::setPanelHeight).range(0.5f, 100.0f)
));
defs.add(FieldDef.floatField("ability.heightOffset", this::getHeightOffset, this::setHeightOffset)
.min(Float.NEGATIVE_INFINITY));
@@ -694,8 +694,8 @@ public class EntityAbilityBeam extends EntityEnergyProjectile {
@Override
protected void readProjectileNBT(NBTTagCompound nbt) {
this.beamWidth = nbt.hasKey("BeamWidth") ? nbt.getFloat("BeamWidth") : 0.3f;
this.headSize = nbt.hasKey("HeadSize") ? nbt.getFloat("HeadSize") : 0.5f;
this.beamWidth = sanitize(nbt.hasKey("BeamWidth") ? nbt.getFloat("BeamWidth") : 0.3f, 0.3f, MAX_ENTITY_SIZE);
this.headSize = sanitize(nbt.hasKey("HeadSize") ? nbt.getFloat("HeadSize") : 0.5f, 0.5f, MAX_ENTITY_SIZE);
this.headOffsetX = nbt.hasKey("HeadOffsetX") ? nbt.getDouble("HeadOffsetX") : 0;
this.headOffsetY = nbt.hasKey("HeadOffsetY") ? nbt.getDouble("HeadOffsetY") : 0;
this.headOffsetZ = nbt.hasKey("HeadOffsetZ") ? nbt.getDouble("HeadOffsetZ") : 0;
@@ -38,6 +38,9 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
// Disc shape properties
private float discRadius = 1.0f; // Width of disc
private float discThickness = 0.2f; // Height of disc
// Boomerang owner-gone tracking
private int returnOwnerNullTicks = 0;
private boolean vertical = false; // false = horizontal (flat), true = vertical (thin edge forward)
// Charging state (disc-specific target sizes)
@@ -274,6 +277,7 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
if (returning) {
Entity owner = getOwnerEntity();
if (owner != null) {
returnOwnerNullTicks = 0;
double distToOwner = Math.sqrt(
(posX - owner.posX) * (posX - owner.posX) +
(posY - owner.posY) * (posY - owner.posY) +
@@ -281,8 +285,9 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
);
return distToOwner < 1.5;
}
// Owner gone but returning - keep going toward last known position
return false;
// Owner gone while returning - grace period then die
returnOwnerNullTicks++;
return returnOwnerNullTicks > 100;
}
// Not returning yet - don't die from distance (we'll trigger return above)
@@ -487,8 +492,8 @@ public class EntityAbilityDisc extends EntityEnergyProjectile {
protected void readProjectileNBT(NBTTagCompound nbt) {
this.boomerang = nbt.hasKey("Boomerang") && nbt.getBoolean("Boomerang");
this.boomerangDelay = nbt.hasKey("BoomerangDelay") ? nbt.getInteger("BoomerangDelay") : 40;
this.discRadius = nbt.hasKey("DiscRadius") ? nbt.getFloat("DiscRadius") : 1.0f;
this.discThickness = nbt.hasKey("DiscThickness") ? nbt.getFloat("DiscThickness") : 0.2f;
this.discRadius = sanitize(nbt.hasKey("DiscRadius") ? nbt.getFloat("DiscRadius") : 1.0f, 1.0f, MAX_ENTITY_SIZE);
this.discThickness = sanitize(nbt.hasKey("DiscThickness") ? nbt.getFloat("DiscThickness") : 0.2f, 0.2f, MAX_ENTITY_SIZE);
this.vertical = nbt.hasKey("Vertical") ? nbt.getBoolean("Vertical") : false;
this.returning = nbt.hasKey("Returning") && nbt.getBoolean("Returning");
// Charging state (common fields handled by base)
@@ -473,7 +473,7 @@ public class EntityAbilityLaser extends EntityEnergyProjectile {
@Override
protected void readProjectileNBT(NBTTagCompound nbt) {
this.laserWidth = nbt.hasKey("LaserWidth") ? nbt.getFloat("LaserWidth") : 0.2f;
this.laserWidth = sanitize(nbt.hasKey("LaserWidth") ? nbt.getFloat("LaserWidth") : 0.2f, 0.2f, MAX_ENTITY_SIZE);
this.expansionSpeed = nbt.hasKey("ExpansionSpeed") ? nbt.getFloat("ExpansionSpeed") : 2.0f;
this.lingerTicks = nbt.hasKey("LingerTicks") ? nbt.getInteger("LingerTicks") : 10;
this.dieOnImpact = nbt.getBoolean("DieOnImpact");
@@ -129,6 +129,12 @@ public class EntityAbilitySweeper extends EntityEnergyAbility {
return;
}
// Absolute hard lifetime cap (safety net)
if (ticksExisted > HARD_LIFETIME_CAP) {
this.setDead();
return;
}
Entity owner = getOwnerEntity();
if (owner != null && owner.isDead) {
this.setDead();
@@ -330,9 +336,9 @@ public class EntityAbilitySweeper extends EntityEnergyAbility {
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
readEnergyBaseNBT(nbt);
this.beamLength = nbt.getFloat("BeamLength");
this.beamWidth = nbt.getFloat("BeamWidth");
this.beamHeight = nbt.getFloat("BeamHeight");
this.beamLength = sanitize(nbt.getFloat("BeamLength"), 10.0f, MAX_ENTITY_SIZE);
this.beamWidth = sanitize(nbt.getFloat("BeamWidth"), 0.3f, MAX_ENTITY_SIZE);
this.beamHeight = sanitize(nbt.getFloat("BeamHeight"), 0.8f, MAX_ENTITY_SIZE);
this.sweepSpeed = nbt.getFloat("SweepSpeed");
this.numberOfRotations = nbt.getInteger("NumRotations");
this.completedRotations = nbt.getInteger("CompletedRotations");
@@ -18,6 +18,13 @@ import net.minecraft.world.World;
*/
public abstract class EntityEnergyAbility extends Entity implements IEntityAdditionalSpawnData {
// ==================== SAFETY CONSTANTS ====================
protected static final int CHARGE_TIMEOUT_GRACE = 60; // Ticks of grace after charge should have ended
protected static final int HARD_LIFETIME_CAP = 1200; // 60 seconds absolute max for projectiles/sweepers
protected static final int BARRIER_HARD_LIFETIME_CAP = 12000; // 10 minutes absolute max for barriers
protected static final float MAX_ENTITY_SIZE = 100.0f; // Max size/width/length for any energy entity
protected static final float MAX_ENTITY_RADIUS = 50.0f; // Max radius for dome barriers
// ==================== VISUAL PROPERTIES ====================
protected EnergyDisplayData displayData = new EnergyDisplayData();
protected EnergyLightningData lightningData = new EnergyLightningData();
@@ -272,6 +279,17 @@ public abstract class EntityEnergyAbility extends Entity implements IEntityAddit
return 0.0f;
}
// ==================== SAFETY HELPERS ====================
/**
* Sanitize a float value loaded from NBT or external sources.
* Guards against NaN, Infinity, negative values, and values exceeding a max.
*/
protected static float sanitize(float value, float fallback, float max) {
if (Float.isNaN(value) || Float.isInfinite(value) || value < 0) return fallback;
return Math.min(value, max);
}
// ==================== NBT HELPERS ====================
/**
@@ -174,6 +174,13 @@ public abstract class EntityEnergyBarrier extends EntityEnergyAbility {
return true;
}
// Absolute hard lifetime cap (safety net for barriers with no duration)
if (ticksAlive > BARRIER_HARD_LIFETIME_CAP) {
onBarrierDestroyed();
this.setDead();
return true;
}
// Reset hit flash
if (getHitFlash() > 0) {
setHitFlash((byte) (getHitFlash() - 1));
@@ -216,8 +216,8 @@ public class EntityEnergyDome extends EntityEnergyBarrier {
@Override
protected void readEntityFromNBT(NBTTagCompound nbt) {
readBarrierBaseNBT(nbt);
this.domeRadius = nbt.getFloat("DomeRadius");
this.targetDomeRadius = nbt.hasKey("TargetDomeRadius") ? nbt.getFloat("TargetDomeRadius") : domeRadius;
this.domeRadius = sanitize(nbt.getFloat("DomeRadius"), 5.0f, MAX_ENTITY_RADIUS);
this.targetDomeRadius = sanitize(nbt.hasKey("TargetDomeRadius") ? nbt.getFloat("TargetDomeRadius") : domeRadius, 5.0f, MAX_ENTITY_RADIUS);
this.followCaster = nbt.hasKey("FollowCaster") && nbt.getBoolean("FollowCaster");
}
@@ -365,8 +365,10 @@ public class EntityEnergyPanel extends EntityEnergyBarrier {
this.mode = (modeOrdinal >= 0 && modeOrdinal < PanelMode.values().length)
? PanelMode.values()[modeOrdinal] : PanelMode.PLACED;
panelData.readNBT(nbt);
this.targetPanelWidth = nbt.hasKey("TargetPanelWidth") ? nbt.getFloat("TargetPanelWidth") : panelData.panelWidth;
this.targetPanelHeight = nbt.hasKey("TargetPanelHeight") ? nbt.getFloat("TargetPanelHeight") : panelData.panelHeight;
panelData.panelWidth = Math.min(panelData.panelWidth, MAX_ENTITY_SIZE);
panelData.panelHeight = Math.min(panelData.panelHeight, MAX_ENTITY_SIZE);
this.targetPanelWidth = sanitize(nbt.hasKey("TargetPanelWidth") ? nbt.getFloat("TargetPanelWidth") : panelData.panelWidth, 3.0f, MAX_ENTITY_SIZE);
this.targetPanelHeight = sanitize(nbt.hasKey("TargetPanelHeight") ? nbt.getFloat("TargetPanelHeight") : panelData.panelHeight, 3.0f, MAX_ENTITY_SIZE);
}
@Override
@@ -219,6 +219,18 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
}
}
// Charging timeout - if charge phase exceeded with grace period, force kill
if (isCharging() && chargeTick > chargeDuration + CHARGE_TIMEOUT_GRACE) {
this.setDead();
return;
}
// Absolute hard lifetime cap (safety net for any stuck state)
if (ticksExisted > HARD_LIFETIME_CAP) {
this.setDead();
return;
}
// Set death time on first tick if not already set (handles chunk load/unload)
// Skip during charging phase - lifetime starts when the entity actually fires
if (deathWorldTime < 0 && worldObj != null && !isCharging()) {
@@ -911,7 +923,7 @@ public abstract class EntityEnergyProjectile extends EntityEnergyAbility {
protected void readBaseNBT(NBTTagCompound nbt) {
readEnergyBaseNBT(nbt);
this.size = nbt.getFloat("Size");
this.size = sanitize(nbt.getFloat("Size"), 1.0f, MAX_ENTITY_SIZE);
// Read effects list
this.effects.clear();
@@ -328,8 +328,8 @@ public class EntityEnergySlicer extends EntityEnergyProjectile {
@Override
protected void readProjectileNBT(NBTTagCompound nbt) {
readChargingNBT(nbt);
this.sliceWidth = nbt.hasKey("SliceWidth") ? nbt.getFloat("SliceWidth") : 3.0f;
this.sliceThickness = nbt.hasKey("SliceThickness") ? nbt.getFloat("SliceThickness") : 0.15f;
this.sliceWidth = sanitize(nbt.hasKey("SliceWidth") ? nbt.getFloat("SliceWidth") : 3.0f, 3.0f, MAX_ENTITY_SIZE);
this.sliceThickness = sanitize(nbt.hasKey("SliceThickness") ? nbt.getFloat("SliceThickness") : 0.15f, 0.15f, MAX_ENTITY_SIZE);
this.piercing = !nbt.hasKey("Piercing") || nbt.getBoolean("Piercing");
this.targetSliceWidth = nbt.hasKey("TargetSliceWidth") ? nbt.getFloat("TargetSliceWidth") : sliceWidth;
this.targetSliceThickness = nbt.hasKey("TargetSliceThickness") ? nbt.getFloat("TargetSliceThickness") : sliceThickness;