Fractal Softworks Forum

Starsector => Mods => Modding => Topic started by: xenoargh on May 29, 2017, 04:57:03 PM

Title: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 29, 2017, 04:57:03 PM
...it returns TRUE on the Title Screen, in the Missions, etc., etc.

Pretty sure that's a bug.

But I've got a question, related to this.

Is there a method to determine, in EveryFrameCombatScripts, if the game's currently running that initial Economy sim (you know, starting with Jan 1)? 

Because running EveryFrameCombatScripts there is causing some sort of endless-loop hang pretty consistently, running here with 64-bit Java.  Or it's exposing a bug in there, I don't know.

And the CombatEngine doesn't report isPaused() being true, etc., etc., when that's happening, so I don't see a clear way to detect that state and halt the scripts then.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 29, 2017, 05:07:36 PM
I'm confused - why would you be calling this from *combat* scripts? They would never run during the new game advance period (i.e. the jan 1st -> march 1st interval) since there is never any non-autoresolved combat/
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 10:15:29 AM
The game's still running EveryFrameCombatScript() during this period, because of the "attraction" ships in the Title screen.  I've repeatedly confirmed that, not only is it running, the game is not returning isPaused(), etc., so I can't get the script to halt.  It's causing some sort of issue with the core gamecode during that period, that I can't see (looks like a timer exceeds <some value> from here) that then causes the application to terminate.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 10:19:57 AM
I've tested a bunch of ways to find a valid method to halt that stuff during this period, but to no avail thus far.  I'm really not sure why other EveryFrameCombatScripts aren't causing issues (or they are, but nobody's been reporting them), but it's happening with all of mine.

Here's some of the code, just to see what it's trying to do:

Code
package data.scripts.plugins;

import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.input.InputEventAPI;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class FormShieldPlugin implements EveryFrameCombatPlugin
{
private final float shieldDamage = 0.25f;
private final float shieldDamageShot = 0.05f;
    private CombatEngineAPI engine;

private HashSet<ShipAPI> reflectingMap = new HashSet<>();
private HashSet<ShipAPI> damagedMap = new HashSet<>();

private List<BeamAPI> beamList = new ArrayList<>();
private List<DamagingProjectileAPI> projList = new ArrayList<>();

public static final Color JITTER_UNDER_COLOR = new Color(0,200,255,155);
public static final Color JITTER_COLOR = new Color(0,200,255,32);
  
    
    @Override
    public void advance(float amount,List<InputEventAPI> events)
    {
        if (engine.isPaused()) return;
        for (ShipAPI ship : engine.getShips()) {
List hullmods = ship.getVariant().getSortedMods();
if(hullmods.contains("shields_formshield")){
if (!reflectingMap.contains(ship))
{
reflectingMap.add(ship);
MutableShipStatsAPI stats = ship.getMutableStats();
stats.getEmpDamageTakenMult().modifyFlatAfterMult(null, -1f);
stats.getArmorDamageTakenMult().modifyFlatAfterMult(null, -1f);
stats.getHullDamageTakenMult().modifyFlatAfterMult(null, -1f);
stats.getWeaponDamageTakenMult().modifyFlatAfterMult(null, -1f);
stats.getEngineDamageTakenMult().modifyFlatAfterMult(null, -1f);
}
if(reflectingMap.contains(ship) && ship.getFluxTracker().isOverloadedOrVenting() || ship.isHulk()){
MutableShipStatsAPI stats = ship.getMutableStats();
stats.getEmpDamageTakenMult().modifyFlatAfterMult(null, 1f);
stats.getArmorDamageTakenMult().modifyFlatAfterMult(null, 1f);
stats.getHullDamageTakenMult().modifyFlatAfterMult(null, 1f);
stats.getWeaponDamageTakenMult().modifyFlatAfterMult(null, 1f);
stats.getEngineDamageTakenMult().modifyFlatAfterMult(null, 1f);
reflectingMap.remove(ship);
}
            }
        }

beamList = engine.getBeams();
        projList = engine.getProjectiles();

        if(!beamList.isEmpty()){    
for (BeamAPI thisBeam : beamList)
{
CombatEntityAPI thisBeamTarget = thisBeam.getDamageTarget();
if(thisBeamTarget != null)
{    
if(thisBeamTarget instanceof ShipAPI)
{
ShipAPI shipTarget = (ShipAPI) thisBeamTarget;
if(reflectingMap.contains(shipTarget))
{
float dps = 0f;
if(thisBeam.getWeapon().isBurstBeam()){
dps = thisBeam.getWeapon().getDerivedStats().getBurstDamage() * shieldDamage;
dps = thisBeam.getWeapon().getDerivedStats().getBurstFireDuration() * dps;
} else {
dps = thisBeam.getWeapon().getDerivedStats().getDps() * shieldDamage * amount;
}
shipTarget.getFluxTracker().increaseFlux(dps, true);
damagedMap.add(shipTarget);
}
}
}    
}
}

if(!projList.isEmpty()){
for (DamagingProjectileAPI thisProj : projList)
{
if(thisProj.didDamage())
{    
CombatEntityAPI thisProjTarget = thisProj.getDamageTarget();
if(thisProjTarget instanceof ShipAPI){
ShipAPI shipTarget = (ShipAPI) thisProjTarget;
if(reflectingMap.contains(shipTarget))
{
float dps = thisProj.getWeapon().getDerivedStats().getDamagePerShot() * shieldDamageShot;
shipTarget.getFluxTracker().increaseFlux(dps, true);
damagedMap.add(shipTarget);
}
}
}    
}  
}

for(ShipAPI damagedShip : damagedMap){
damagedShip.setJitterUnder(this, JITTER_UNDER_COLOR, 3f, 4, 0f, 5f);
damagedShip.setJitter(this, JITTER_COLOR, 3f, 4, 0f, 5f);
}
damagedMap.clear();
    }

    @Override
    public void init(CombatEngineAPI eng) {
        this.engine = eng;
        reflectingMap.clear();
beamList.clear();
projList.clear();
    }

@Override
public void renderInWorldCoords(ViewportAPI vapi) {
}

@Override
public void renderInUICoords(ViewportAPI vapi) {

}
}

Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 10:28:25 AM
The game's still running EveryFrameCombatScript() during this period, because of the "attraction" ships in the Title screen.

It's 100% not doing that. It couldn't possibly. Because I'm paranoid, I also just tested to make sure, and it indeed doesn't.

To clarify: it's running those scripts in the title screen, but NOT once the "new campaign game creation" progress bar starts up.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 10:34:28 AM
Uh, I hate to put it this way, but something's still running then; I can watch the FX script doing <something> during that period (it's also the best way to see this crash occur- get a bunch of ships fighting on the screen, hit "start game").

So... maybe it's during the drawing portion that this occurs?

But, more importantly, ships and weapons and all that are getting drawn while that code is running; so obviously a lot of things are still happening.  How can I detect that you've entered that special state and halt all operations?
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 10:42:11 AM
The rendering is not running either. The reason you still see the last frame before you hit "start new game" is the screen buffer isn't cleared, and the progress bar stuff is just rendered on top of it. Occasionally, you'll see the title screen stuff in the background shifting between two frames, this is due to different frames being double buffered, but again, no actual combat rendering going on at that point.

However: it will render two frames in a row *right before* the progress bar etc starts, without an intervening advance() call, so if there's anything in your code that relies on render()/advance() always being precisely interleaved, that could cause a problem.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 10:44:49 AM
Here's the code from the current release of FX mod, for comparison's sake.  Note all the ways I'm trying to return during Advance() here.

Code
package data.scripts.plugins;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.CombatAsteroidAPI;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;

import data.scripts.fx_Particle;

import static data.scripts.fx_SharedLib.circularVelocity;
import static data.scripts.fx_SharedLib.getBeamColors;
import static data.scripts.fx_SharedLib.getProjectileColors;
import static data.scripts.fx_SharedLib.isWithinShieldArc;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import java.util.List;
import java.util.Map;

import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.opengl.GL11;
import static org.lazywizard.lazylib.MathUtils.getRandomNumberInRange;


public class FX_Plugin implements EveryFrameCombatPlugin
{
CombatEngineAPI engine;
private Vector2f centerPoint;
private float frameTime;
private final float longDist = 3000f * 3000f;

//Default Sprite
private final SpriteAPI eSprite = Global.getSettings().getSprite("fx", "fx_muzzleflash_001");

//FX Sprites
private final SpriteAPI fx_muzzleflash_001 = Global.getSettings().getSprite("fx", "fx_muzzleflash_001");
private final SpriteAPI fx_muzzleflash_002 = Global.getSettings().getSprite("fx", "fx_muzzleflash_002");
private final SpriteAPI fx_muzzleflash_003 = Global.getSettings().getSprite("fx", "fx_muzzleflash_003");

private final SpriteAPI fx_shieldflash_001 = Global.getSettings().getSprite("fx", "fx_shieldflash_001");
private final SpriteAPI fx_shieldflash_002 = Global.getSettings().getSprite("fx", "fx_shieldflash_002");
private final SpriteAPI fx_shieldflash_003 = Global.getSettings().getSprite("fx", "fx_shieldflash_003");

private final SpriteAPI fx_laserflash_001 = Global.getSettings().getSprite("fx", "fx_laserflash_001");
private final SpriteAPI fx_laserflash_002 = Global.getSettings().getSprite("fx", "fx_laserflash_002");
private final SpriteAPI fx_laserflash_003 = Global.getSettings().getSprite("fx", "fx_laserflash_003");

private final SpriteAPI fx_smoke_tendril_001 = Global.getSettings().getSprite("fx", "fx_smoke_tendril_001");
private final SpriteAPI fx_smoke_tendril_002 = Global.getSettings().getSprite("fx", "fx_smoke_tendril_002");
private final SpriteAPI fx_smoke_tendril_003 = Global.getSettings().getSprite("fx", "fx_smoke_tendril_003");

private final SpriteAPI fx_generic_pulse = Global.getSettings().getSprite("fx", "fx_generic_pulse");

public static List<fx_Particle> particleList = new ArrayList<>();

public static Map projCoreColorMap = new HashMap();
public static Map projFringeColorMap = new HashMap();

//public static List<ShipAPI> engineList = new ArrayList<>();
public static HashSet<DamagingProjectileAPI> deadShotList = new HashSet<>(100);
//public static Map engineMap = new HashMap();

public static HashSet<MissileAPI> seenMissiles = new HashSet<>(100);

    @Override
public void init(CombatEngineAPI engine) {
this.engine = engine;
clearMe();
}

public void clearMe(){
particleList.clear();
deadShotList.clear();
seenMissiles.clear();
}
        
    @Override
public void advance(float amount, List events)
{
// Obvious exploits are obvious
if (engine == null){ clearMe(); return;}
if(engine.isPaused()){ frameTime = 0f; return;}
if(engine.isInFastTimeAdvance() || engine.isUIShowingDialog()){ clearMe(); return;}
frameTime = amount;
if(engine.getTimeMult().getModifiedValue() != 1f){
frameTime /= engine.getTimeMult().getModifiedValue();
}

        // get the missiles in this frame  
        HashSet<MissileAPI> activeMissiles = new HashSet<>();  
        activeMissiles.addAll(engine.getMissiles());  
  
        // the difference between the missiles in the last frame to this frame are those that *could* have done damage  
        //seenMissiles.removeAll(activeMissiles);  
        for(MissileAPI missile : seenMissiles){  
if(deadShotList.contains(missile)) continue;
            if(missile.didDamage()){  
int damageType;
switch (missile.getDamageType()) {
case ENERGY:
damageType = 0;
break;
case HIGH_EXPLOSIVE:
damageType = 1;
break;
case KINETIC:
damageType = 2;
break;
case FRAGMENTATION:
damageType = 3;
break;
default:
damageType = 0;
break;
}
                int power = Math.min(200,Math.max(1,Math.round(missile.getWeapon().getDerivedStats().getDamagePerShot() / 5)));
doProjectileExplosion(missile,null,power,power > 3,damageType);
deadShotList.add(missile);
            }  
        }  
  
        // set our missiles for the next frame  
        seenMissiles = activeMissiles;

for(BeamAPI beam : engine.getBeams()){
//Generates a bit of "spark" when the Beam strikes something.
if(beam.didDamageThisFrame() && beam.getBrightness() > 0.99f){
float angle = VectorUtils.getAngle(beam.getFrom(), beam.getTo());
int power = Math.min(5,Math.max(1,Math.round(beam.getWeapon().getDerivedStats().getDps() / 75)));
boolean didEffect = false;
CombatEntityAPI target = beam.getDamageTarget();
if(target instanceof ShipAPI){
ShipAPI tShip = (ShipAPI) target;
if(tShip.getShield() != null){
if(tShip.getShield().isOn() && isWithinShieldArc(tShip,beam.getTo())){
didEffect = true;

if(beam.getWeapon() == null) continue;
if(beam.getWeapon().getShip() == null) continue;
if(beam.getWeapon().isBurstBeam() || getRandomNumberInRange(0f,10f) > 9f){
power = Math.min(400,Math.max(1,Math.round(beam.getWeapon().getDerivedStats().getDamagePerShot())));
float newAngle = VectorUtils.getAngle(tShip.getShield().getLocation(),beam.getTo()) + getRandomNumberInRange(-45f, 45f);
float vel = getRandomNumberInRange(0, 3f);
float size = getRandomNumberInRange(50f + (float) power,75f + (float) power);
float growth = getRandomNumberInRange(0.995f,1.0f);
float ratio = 1.0f;
float lifeTime = getRandomNumberInRange(0.1f,0.15f);
float fadeTime = getRandomNumberInRange(0.1f,0.15f);
Color projColor;
Color projColorFade;
if(!projCoreColorMap.containsKey(beam.getWeapon().getId())){
Color[] bColors = getBeamColors(beam);
projColor = bColors[0];
projColorFade = bColors[1];
} else {
projColor = (Color) projCoreColorMap.get(beam.getWeapon().getId());
projColorFade = (Color) projFringeColorMap.get(beam.getWeapon().getId());
}
int muzzleflash = getRandomNumberInRange(1,3);
Vector2f velocity = circularVelocity(newAngle,vel);
Vector2f targetVel = beam.getWeapon().getShip().getVelocity();
velocity = velocity.translate(targetVel.getX(), targetVel.getY());
newParticle(beam.getTo(),velocity, size, ratio, newAngle, true, true, "fx_shieldflash_00"+muzzleflash,projColor,projColorFade,lifeTime,fadeTime,growth);
}
}
}
}

if(target instanceof MissileAPI || target instanceof CombatAsteroidAPI || !didEffect){
for(int i = 0; i < power; i++){
if(getRandomNumberInRange(0f,10f) > 5f){
float newAngle = MathUtils.clampAngle(angle -180f + MathUtils.getRandomNumberInRange(-20f, 20f));
float vel = getRandomNumberInRange(200f + (power * 100f), 400f + (power * 100f));
float size = getRandomNumberInRange(10f + (float) power * 3f,20f + (float) power * 3f);
float growth = getRandomNumberInRange(0.995f,1.0f);
float ratio = 1.0f;
float lifeTime = getRandomNumberInRange(0.1f,0.3f);
float fadeTime = getRandomNumberInRange(0.1f,0.3f);
Vector2f velocity = circularVelocity(newAngle,vel);
Vector2f targetVel = beam.getDamageTarget().getVelocity();
velocity = velocity.translate(targetVel.getX(), targetVel.getY());
newParticle(beam.getTo(),velocity, size, ratio, newAngle, true, true, "fx_generic_pulse",new Color(255,255,0,255),new Color(128,32,0,255),lifeTime,fadeTime,growth);
}
}
}
}

//Generates a "muzzle flash" for a Beam that's active
if(beam.getBrightness() > 0.5f && getRandomNumberInRange(0f,10f) > 5f){
if(beam.getWeapon() == null) continue;
if(beam.getWeapon().getShip() == null) continue;
int power = Math.min(200,Math.max(1,Math.round(beam.getWeapon().getDerivedStats().getDamagePerShot() / 15)));
float newAngle = VectorUtils.getAngle(beam.getFrom(), beam.getTo());
float vel = getRandomNumberInRange(10, 15f);
float size = getRandomNumberInRange(100f + (float) power,125f + (float) power);
float growth = getRandomNumberInRange(0.8f,0.9f);
float ratio = 1.0f;
float lifeTime = getRandomNumberInRange(0.1f,0.15f);
float fadeTime = getRandomNumberInRange(0.1f,0.15f);
Color projColor;
Color projColorFade;
if(!projCoreColorMap.containsKey(beam.getWeapon().getId())){
Color[] bColors = getBeamColors(beam);
projColor = bColors[0];
projColorFade = bColors[1];
} else {
projColor = (Color) projCoreColorMap.get(beam.getWeapon().getId());
projColorFade = (Color) projFringeColorMap.get(beam.getWeapon().getId());
}
int muzzleflash = getRandomNumberInRange(1,3);
Vector2f velocity = circularVelocity(newAngle,vel);
Vector2f targetVel = beam.getWeapon().getShip().getVelocity();
velocity = velocity.translate(targetVel.getX(), targetVel.getY());
newParticle(beam.getFrom(),velocity, size, ratio, newAngle, true, true, "fx_laserflash_00"+muzzleflash,projColor,projColorFade,lifeTime,fadeTime,growth);
}
}

//Kills dead particles; fast boolean checker
for(int i = 0; i < particleList.size(); i++){
if(particleList.get(i).isDead() == true){
particleList.remove(i);
i--;
}
}

//Handles explosions and muzzle-flashes for projectiles.
for(DamagingProjectileAPI projectile : engine.getProjectiles()){
if(deadShotList.contains(projectile)) continue;
if(projectile instanceof MissileAPI){
if(projectile.getElapsed() < 0.01f){
projectileHandler(projectile);
}
} else {
projectileHandler(projectile);
}
}
}

public void projectileHandler(DamagingProjectileAPI projectile){
if(projectile.getWeapon() == null) return;
if(projectile.getWeapon().getShip() == null) return;

CombatEntityAPI dTarg = projectile.getDamageTarget();

//PROJECTILE EXPLOSION HANDLER
if(dTarg != null){
int damageType;
switch (projectile.getDamageType()) {
case ENERGY:
damageType = 0;
break;
case HIGH_EXPLOSIVE:
damageType = 1;
break;
case KINETIC:
damageType = 2;
break;
case FRAGMENTATION:
damageType = 3;
break;
default:
damageType = 0;
break;
}
int power = Math.min(200,Math.max(1,Math.round(projectile.getWeapon().getDerivedStats().getDamagePerShot() / 15)));
doProjectileExplosion(projectile,dTarg,power,power > 3,damageType);
deadShotList.add(projectile);
}

//MUZZLE-FLASH HANDLER
if(projectile.getElapsed() < 0.01f){
int power = Math.min(200,Math.max(1,Math.round(projectile.getWeapon().getDerivedStats().getDamagePerShot() / 15)));
float newAngle = projectile.getFacing();
float vel = getRandomNumberInRange(5, 10f);
float size = getRandomNumberInRange(50f + (float) power,100f + (float) power);
float growth = getRandomNumberInRange(0.8f,0.9f);
float ratio = 1.0f;
float lifeTime = getRandomNumberInRange(0.1f,0.3f);
float fadeTime = getRandomNumberInRange(0.1f,0.15f);
Color projColor;
Color projColorFade;
if(!projCoreColorMap.containsKey(projectile.getProjectileSpecId())){
Color[] bColors = getProjectileColors(projectile);
projColor = bColors[0];
projColorFade = bColors[1];
} else {
projColor = (Color) projCoreColorMap.get(projectile.getProjectileSpecId());
projColorFade = (Color) projFringeColorMap.get(projectile.getProjectileSpecId());
}
int muzzleflash = getRandomNumberInRange(1,3);
Vector2f velocity = circularVelocity(newAngle,vel);
Vector2f targetVel = projectile.getWeapon().getShip().getVelocity();
velocity = velocity.translate(targetVel.getX(), targetVel.getY());
newParticle(projectile.getLocation(),velocity, size, ratio, newAngle, true, true, "fx_muzzleflash_00"+muzzleflash,projColor,projColorFade,lifeTime,fadeTime,growth);
}
}

public void doProjectileExplosion(DamagingProjectileAPI proj, CombatEntityAPI targ, int power, boolean doSmoke, int damageType){
int repeats = Math.min(7,Math.max(1, power / 5));
float facing = proj.getFacing();
Vector2f targetVel;
if(targ == null){
targetVel = new Vector2f(0.0f,0.0f);
} else {
targetVel = targ.getVelocity();
}
for(int i = 0; i < repeats; i++){
float angle = facing + 180 + getRandomNumberInRange(-35f,35f);
float vel = getRandomNumberInRange(5, 10f);
float size = getRandomNumberInRange(10f + (float) power,20f + (float) power);
float growth = getRandomNumberInRange(1.02f,1.05f);
float ratio = 1.0f;
float lifeTime = getRandomNumberInRange(0.1f,0.3f);
float fadeTime = getRandomNumberInRange(0.1f,0.15f);
Color projColor;
Color projColorFade;
if(!projCoreColorMap.containsKey(proj.getProjectileSpecId())){
Color[] bColors = getProjectileColors(proj);
projColor = bColors[0];
projColorFade = bColors[1];
} else {
projColor = (Color) projCoreColorMap.get(proj.getProjectileSpecId());
projColorFade = (Color) projFringeColorMap.get(proj.getProjectileSpecId());
}
int muzzleflash = getRandomNumberInRange(1,3);
Vector2f velocity = circularVelocity(angle,vel);
velocity = new Vector2f(velocity.getX() + targetVel.getX(), velocity.getY() + targetVel.getY());
//Smoke particles.  Limits on how powerful a shot needs to be to generate, to prevent certain borks and optimize
if(doSmoke && getRandomNumberInRange(0,10) > 7){
switch (damageType) {
case 0:
//Does a glow effect
newParticle(proj.getLocation(),new Vector2f(0f,0f), size * getRandomNumberInRange(0.75f,2.0f), ratio, getRandomNumberInRange(0f,359f), false, false, "fx_generic_pulse",projColor,projColorFade,lifeTime * 0.5f,fadeTime * getRandomNumberInRange(0.5f,0.85f),getRandomNumberInRange(1.005f,1.01f));
break;
case 1:
//Does a smoke-tendril effect
newParticle(proj.getLocation(),new Vector2f(0f,0f), size * getRandomNumberInRange(0.75f,2.0f), ratio, getRandomNumberInRange(0f,359f), false, false, "fx_smoke_tendril_00"+muzzleflash,new Color (255,255,255,255),new Color (255,255,255,255),lifeTime * getRandomNumberInRange(2f,5f),fadeTime * getRandomNumberInRange(5f,10f),getRandomNumberInRange(1.001f,1.005f));
break;
case 2:
//Does a spike effect
newParticle(proj.getLocation(),new Vector2f(0f,0f), size * getRandomNumberInRange(0.75f,2.0f), ratio, getRandomNumberInRange(0f,359f), false, false, "fx_smoke_tendril_00"+muzzleflash,new Color (255,255,255,255),new Color (255,255,255,255),lifeTime * getRandomNumberInRange(2f,5f),fadeTime * getRandomNumberInRange(5f,10f),getRandomNumberInRange(1.001f,1.005f));
break;
case 3:
//Does a burst effect
newParticle(proj.getLocation(),new Vector2f(0f,0f), size * getRandomNumberInRange(0.75f,2.0f), ratio, getRandomNumberInRange(0f,359f), false, false, "fx_smoke_tendril_00"+muzzleflash,new Color (255,255,255,255),new Color (255,255,255,255),lifeTime * getRandomNumberInRange(2f,5f),fadeTime * getRandomNumberInRange(5f,10f),getRandomNumberInRange(1.001f,1.005f));
break;
default:
//Does a burst effect
newParticle(proj.getLocation(),new Vector2f(0f,0f), size * getRandomNumberInRange(0.75f,2.0f), ratio, getRandomNumberInRange(0f,359f), false, false, "fx_smoke_tendril_00"+muzzleflash,new Color (255,255,255,255),new Color (255,255,255,255),lifeTime * getRandomNumberInRange(2f,5f),fadeTime * getRandomNumberInRange(5f,10f),getRandomNumberInRange(1.001f,1.005f));
break;
}
}
newParticle(proj.getLocation(),velocity, size, ratio, angle, true, true, "fx_muzzleflash_00"+muzzleflash,projColor,projColorFade,lifeTime,fadeTime,growth);
}
}

public SpriteAPI pickSprite(String spriteName){
if (spriteName.equalsIgnoreCase("fx_muzzleflash_001")){
return fx_muzzleflash_001;
} else if (spriteName.equalsIgnoreCase("fx_muzzleflash_002")) {
return fx_muzzleflash_002;
} else if (spriteName.equalsIgnoreCase("fx_muzzleflash_003")) {
return fx_muzzleflash_003;
} else if (spriteName.equalsIgnoreCase("fx_laserflash_001")) {
return fx_laserflash_001;
} else if (spriteName.equalsIgnoreCase("fx_laserflash_002")) {
return fx_laserflash_002;
} else if (spriteName.equalsIgnoreCase("fx_laserflash_003")) {
return fx_laserflash_003;
} else if (spriteName.equalsIgnoreCase("fx_shieldflash_001")) {
return fx_shieldflash_001;
} else if (spriteName.equalsIgnoreCase("fx_shieldflash_002")) {
return fx_shieldflash_002;
} else if (spriteName.equalsIgnoreCase("fx_shieldflash_003")) {
return fx_shieldflash_003;
} else if (spriteName.equalsIgnoreCase("fx_smoke_tendril_001")) {
return fx_smoke_tendril_001;
} else if (spriteName.equalsIgnoreCase("fx_smoke_tendril_002")) {
return fx_smoke_tendril_002;
} else if (spriteName.equalsIgnoreCase("fx_smoke_tendril_003")) {
return fx_smoke_tendril_003;
} else if (spriteName.equalsIgnoreCase("fx_generic_pulse")) {
return fx_generic_pulse;
} else {
return eSprite;
}
}


public void newParticle(Vector2f location, Vector2f velocity, float size, float ratio, float angle, boolean isAdditive, boolean isBlended, String spriteName, Color startColor, Color endColor, float lifeTime, float fadeTime, float growth){
//Don't build new particles if there are already too many.
if(particleList.size() > 1000) return;
//Don't build new particles outside the view-distance.
if(location == null || centerPoint == null) return;
if(MathUtils.getDistanceSquared(location, centerPoint) > longDist) return;
//Weird fix for angle weirdness
angle = angle - 90f;
Vector2f initialSize = new Vector2f(size / ratio, size * ratio);
fx_Particle data = new fx_Particle(pickSprite(spriteName), lifeTime, fadeTime, initialSize, location, velocity, angle, isAdditive, isBlended, startColor, endColor, growth);
particleList.add(data);
}

public Color blend(Color c1, Color c2, float ratio) {
if ( ratio > 1f ) ratio = 1f;
else if ( ratio < 0f ) ratio = 0f;
float iRatio = 1.0f - ratio;

int redOne = Math.min(255,Math.max(0,Math.round((c1.getRed() * iRatio) + (c2.getRed() * ratio))));
int greenOne = Math.min(255,Math.max(0,Math.round((c1.getGreen() * iRatio) + (c2.getGreen() * ratio))));
int blueOne = Math.min(255,Math.max(0,Math.round((c1.getBlue() * iRatio) + (c2.getBlue() * ratio))));
int alphaOne = Math.min(255,Math.max(0,Math.round((c1.getAlpha() * iRatio) + (c2.getAlpha() * ratio))));

return new Color(redOne,greenOne,blueOne,alphaOne);
}

@Override
public void renderInWorldCoords(ViewportAPI vapi) {
if(engine == null) return;
if(engine.isInFastTimeAdvance() || engine.isUIShowingDialog() || engine.isPaused()){ frameTime = 0f; return;}
centerPoint = vapi.getCenter();
//if(centerPoint == null) return;
/*
for(ShipAPI ship : engine.getShips()){
if(!engineList.contains(ship)){
for(ShipEngineAPI sEngine : ship.getEngineController().getShipEngines()){
EngineSlotAPI slot = sEngine.getEngineSlot();
ship.getEngineController().setFlameLevel(slot, 0f);
}
engineList.add(ship);
} else {
ShipEngineControllerAPI sEngineControl = ship.getEngineController();
if(!sEngineControl.isDisabled()
&& !sEngineControl.isFlamedOut()
&& !sEngineControl.isFlamingOut()
){
float angle = ship.getFacing() - 270f;
for(ShipEngineAPI sEngine : sEngineControl.getShipEngines()){
if(!sEngine.isDisabled()){
EngineSlotAPI slot = sEngine.getEngineSlot();
ship.getEngineController().setFlameLevel(slot, 0f);
Vector2f eLoc = sEngine.getLocation();
eSprite.setAngle(angle);
eSprite.setSize(20f, 20f);
eSprite.setColor(Color.yellow);
eSprite.setAdditiveBlend();
eSprite.renderAtCenter(eLoc.getX(), eLoc.getY());
}
}
}
}
}*/

//Runs the projectiles that are generated here, based on their parameters
for (fx_Particle thisParticle : particleList) {
//Don't bother moving or doing any math on dead particles
if(thisParticle.isDead()) continue;
//Age the living particles
thisParticle.timerChange(thisParticle.timeLeft() - frameTime);

float timeLeft = thisParticle.timeLeft();
boolean isFading = thisParticle.isFading();
//Sets this particle to fade out, if we're not already there
if(timeLeft <= 0f && !isFading){
thisParticle.fade();
thisParticle.timerChange(thisParticle.fadeTime);
thisParticle.totalTime = thisParticle.fadeTime;
}

//Kill particles and exit the loop, or move them around and Do Stuff.
if(timeLeft <= 0f && isFading){
thisParticle.kill();
} else {
Vector2f movement = thisParticle.position;
if(frameTime > 0f){
if(thisParticle.isFading){
thisParticle.curColor = blend(new Color(0,0,0,0),thisParticle.endRGB,timeLeft / thisParticle.totalTime);
} else {
if(thisParticle.isBlended){
thisParticle.curColor = blend(thisParticle.endRGB,thisParticle.startRGB,timeLeft / thisParticle.totalTime);
}
}
if (thisParticle.growth != 1.0f) thisParticle.size = new Vector2f(thisParticle.size.getX() * thisParticle.growth, thisParticle.size.getY() * thisParticle.growth);
movement = new Vector2f(movement.getX() + (thisParticle.velocity.getX() * frameTime),
movement.getY() + (thisParticle.velocity.getY() * frameTime));
}
thisParticle.position = movement;
SpriteAPI thisSprite = thisParticle.sprite;
thisSprite.setColor(thisParticle.curColor);
thisSprite.setAngle(thisParticle.angle);
thisSprite.setSize(thisParticle.size.getX(), thisParticle.size.getY());
if(thisParticle.isAdditive){
thisSprite.setAdditiveBlend();
} else {
thisSprite.setBlendFunc(GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
}
thisSprite.renderAtCenter(movement.getX(), movement.getY());
}
}
}

@Override
public void renderInUICoords(ViewportAPI vapi) {
}
}

Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 10:49:24 AM
Hmm.  That might explain the FX issues, but what about the code in the Rebalance mod, that also causes this issue, and doesn't do anything in the rendering cycle at all?

It's either this:
Code
//Drains a smallish amount of Hard Flux per second, depending on ship size and total Flux Capacity.
package data.scripts.plugins;

import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import java.util.List;


public class FluxCapHardFluxDrain implements EveryFrameCombatPlugin
{
CombatEngineAPI engine;

    @Override
public void init(CombatEngineAPI cEngine) {
this.engine = cEngine;
}
       
    @Override
public void advance(float amount, List events)
{
// Obvious exploits are obvious
if (engine == null) return;
if(engine.isInFastTimeAdvance() || engine.isUIShowingDialog() || engine.isPaused()) return;
float drainFraction = 150f;

for (ShipAPI ship : engine.getShips()){
//Skip all the ships that this shouldn't apply to, like ships that are Phased, Venting, etc., etc.
if(!ship.isAlive() || ship.isPhased() || ship.getFluxTracker().isOverloadedOrVenting() || ship.getHardFluxLevel() <= 0f) continue;
//If the ship qualifies for a drain, drain now.
//Start by computing the Drain Threshold, which is a ratio of Capacity vs. a number that results in total drain / second.
//Small stuff gets a better (lower) number here, because they get significantly less Capacity per OP, etc.
if(ship.isDestroyer()){
drainFraction = 250f;
} else if(ship.isCruiser()) {
drainFraction = 350f;
} else if(ship.isCapital()){
drainFraction = 500f;
}
//Now apply the drainThreshold vs Capacity and time elapsed to get the amount of Hard Flux drain this frame
drainFraction = (ship.getMutableStats().getFluxCapacity().getModifiedValue() / drainFraction) * engine.getElapsedInLastFrame();
//Apply time mult here, if that's a thing
if(engine.getTimeMult().getModifiedValue() != 1f){
drainFraction *= engine.getTimeMult().getModifiedValue();
}
//How much Hard Flux should we have now?
if ((ship.getFluxTracker().getHardFlux() - drainFraction) < 0f) {
ship.getFluxTracker().setHardFlux(0f);
} else {
drainFraction = ship.getFluxTracker().getHardFlux() - drainFraction;
//Set it.
ship.getFluxTracker().setHardFlux(drainFraction);
}
}
}

@Override
public void renderInWorldCoords(ViewportAPI vapi) {
}

@Override
public void renderInUICoords(ViewportAPI vapi) {
}
}

Or this:
Code
//What this does:  scales Beam damage w/ range, so that closer ranges mean more Beam damage.
package data.scripts.plugins;

import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.combat.WeaponAPI.DerivedWeaponStatsAPI;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import data.scripts.weapons.helperUtils.RebalCollisionCheck;

import org.lazywizard.lazylib.MathUtils;
import data.scripts.weapons.helperUtils.RebalWeaponUtils;
import java.util.List;

public class BeamDamageScalar implements EveryFrameCombatPlugin {
CombatEntityAPI target;
CombatEngineAPI engine;

@Override
public void init(CombatEngineAPI cEngine) {
this.engine = cEngine;
this.target = null;
}

@Override
    public void advance(float amount, List events){
        if(engine == null) return;
if(engine.isInFastTimeAdvance() || engine.isUIShowingDialog() || engine.isPaused()) return;

for(BeamAPI beam : engine.getBeams()){
if(beam.getBrightness() < 0.99f) continue;
if(!beam.didDamageThisFrame()) continue;

//Get the beam's target
target = beam.getDamageTarget();

//If we have a target, target is a Ship or Missile.   
if (target != null && target instanceof ShipAPI || target instanceof MissileAPI) {
boolean damageFlux = false;
if(target instanceof ShipAPI){
ShipAPI sTarg = (ShipAPI) target;
if(sTarg.getShield() != null){
if(sTarg.getShield().isOn() && RebalCollisionCheck.isInShieldArc(sTarg, beam.getTo())){
damageFlux = true;
}
}
}

//Now that we have the target, get the weapon ID and get the adjuested DPS and range
WeaponAPI weapon = beam.getWeapon();
float red = beam.getFringeColor().getRed();
float green = beam.getFringeColor().getGreen();
float blue = beam.getFringeColor().getBlue();

float colorMult = 1.0f;
//Red lasers are much more efficient up close
if(red > (blue + green)){
colorMult = 2.5f;
}
//Green lasers are more efficient than blue / purple
if(green > (blue + (red * 0.5f))){
colorMult = 1.75f;
}

DamageType damType = weapon.getDamageType();
DerivedWeaponStatsAPI stats = weapon.getDerivedStats();
float maxRange = weapon.getRange() * weapon.getShip().getMutableStats().getBeamWeaponRangeBonus().getBonusMult();
//The range is now used to give additional damage output, using how close we are to the target.
//Anything less than 1/3 range is at maximum damage, here at 3X damage (but easily adjusted).
float theRangeMult = Math.min(Math.max(maxRange / MathUtils.getDistance(beam.getTo(), beam.getFrom()),0f),5f) * colorMult;

float dam = (stats.getDps() * amount) / theRangeMult;
float emp = (stats.getEmpPerSecond()) * amount / theRangeMult;

//Do some additional Soft Flux damage now, based on the range
if(!damageFlux){
RebalWeaponUtils.rebalDamageThisEntity(beam.getTo(), target, dam, emp, damType, engine, beam.getSource(), false, true);
} else {
ShipAPI ship = (ShipAPI) target;
ship.getFluxTracker().increaseFlux(dam + emp, true);
}
}
}
    }

@Override
public void renderInWorldCoords(ViewportAPI vapi) {
}

@Override
public void renderInUICoords(ViewportAPI vapi) {
}
}
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 10:52:27 AM
But... that code isn't running. You're definitely barking up the wrong tree here - I'd suggest re-evaluating whatever is causing you to think that that code is running, because something about that is wrong.

(Unless, like, you fire up a new thread and explicitly run it from there for some reason. Seriously, that's the only scenario where that could be running. The title screen is deep inside a method call and not cycling through its advance/render loop during the new game creation progress bar stage.)


Maybe a step back: what's the issue? You mentioned a crash of some sort; what's the deal there? Not "probable causes", but what actually happens.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 11:01:35 AM
OK, I'll quit barking up the wrong tree; I'm not doing anything exotic :)

What's happening is that, once you hit "start game", there is a fairly high probability that the game terminates, during the cycle of Jan 1 to March 1, usually but not always before we exit January, after a long wait, where the game halts on that "day".

This appears to more-or-less directly correspond with the numbers on FPS / CPU; the lower both are at the time you hit "start game" the more likely the crash is to occur (at least, that's what appears to be true, but I've only tested this 20 times, as the timeout wait is long). 

What's important is that it doesn't happen, if these scripts aren't running... I think.

Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 11:03:46 AM
Hmm, weird! Is there an exception in the log when it crashes? Failing that, is there an hs_errXXX.pid file (XXX being a number) somewhere either in the Starsector or in the starsector-core directory?
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 11:11:31 AM
To the best of my knowledge, no (just did a search for *.pid, nope), but I'm on my laptop and I forget if I ever replicated the crash on this.  I'll have to check at home in about an hour and a half.

Best way to replicate the bug is to install:

64-bit Java SE 7 (from the 64-bit page)
FX Mod
Rebalance Mod
The Explorer Society

Installing all three at the same time seems to jump the likelihood a lot.

I know I'm compiling for SE 7 (Netbeans default, what we're still recommended to do to keep away from 8's issues).
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 11:36:51 AM
Just verified this on the laptop.  No .pid file was generated.

Sorry, Alex, I know I manage to kill the engine in the weirdest ways...
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 11:39:58 AM
Oh and... this might or might not be related... but on the first run, clean SS install... (i.e., Tutorial not-skippable) it didn't crash.  I'll try taking the Tutorial multiple times, perhaps this is a clue.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 11:47:03 AM
Nope, whether it's Tutorial or not, it's just luck whether it crashes or not.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 11:54:18 AM
Created a new game a bunch of times with the requisite mods, no crashes. Both with tutorial on and off.

(Latest downloads of FX, Rebal A/B/C, EZ Faction, Explorer Society)

If there's no exception in the log and no hs_err file, that's... not great. Means a jvm crash serious enough that it couldn't generate its own crash log (the hs_err file). Probably worth taking a look at the windows event log, in the applications section - that may have some info.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 11:59:38 AM
Wow, that's super-weird.  OK.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 02:03:56 PM
OK... I've gotten home and tried some things out.

It locks very consistently here if you have FX Plugin installed and there's anything going on, combat-wise, on the screen. 

Pretty sure that's a clue, but you keep telling me there's no way the scripts are running, so I'm pretty confused as to what's going on.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 03:30:14 PM
I've looked at the Event Log:
Code
Log Name:      Application
Source:        Application Hang
Date:          5/30/2017 4:48:17 PM
Event ID:      1002
Task Category: (101)
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      x-2
Description:
The program java.exe version 7.0.790.15 stopped interacting with Windows and was closed. To see if more information about the problem is available, check the problem history in the Security and Maintenance control panel.
 Process ID: 40b0
 Start Time: 01d2d985a888bb32
 Termination Time: 6
 Application Path: D:\Program Files (x86)\Fractal Softworks\Starsector\jre\bin\java.exe
 Report Id: ef6c25a2-a061-4314-8a63-6284228c3b96
 Faulting package full name:
 Faulting package-relative application ID:

Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Application Hang" />
    <EventID Qualifiers="0">1002</EventID>
    <Level>2</Level>
    <Task>101</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2017-05-30T20:48:17.901517600Z" />
    <EventRecordID>1283</EventRecordID>
    <Channel>Application</Channel>
    <Computer>x-2</Computer>
    <Security />
  </System>
  <EventData>
    <Data>java.exe</Data>
    <Data>7.0.790.15</Data>
    <Data>40b0</Data>
    <Data>01d2d985a888bb32</Data>
    <Data>6</Data>
    <Data>D:\Program Files (x86)\Fractal Softworks\Starsector\jre\bin\java.exe</Data>
    <Data>ef6c25a2-a061-4314-8a63-6284228c3b96</Data>
    <Data>
    </Data>
    <Data>
    </Data>
    <Binary>55006E006B006E006F0077006E0000000000</Binary>
  </EventData>
</Event>
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 04:12:13 PM
OK, here are things:

I've noticed that every "day" simulated adds nearly 20MB to the game's RAM allocation.  This memory is assigned, but is never released, so far as I can tell.  This might be a problem.

Starting multiple New Games just keeps expanding the number.  The initial New Game ends up at very near 2048MB; clearly, this is why I was having problems with 32-bit the minute I added any mods.

I tried this out in vmparams:
Code
-XgcPrio:pausetime

That resulted in a lot more starts before the crash, suggesting that garbage collection might be an issue. 

But when I had a ship on the screen that had been firing as of the moment I clicked "start game", boom, dead again.

So, I tried a line of code in each of the advance() things causing problems...
Code
if(!engine.isInCampaign() && !engine.isInCampaignSim() && !engine.isMission()) return;

And, wow!  I had FX mod images on the screen, but the game finished the "days" sequence.  Five times in a row. 

Sooooo... there's something.  But from what you've told me, that something is impossible?
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 05:19:05 PM
I'd be careful about drawing conclusions re: memory allocation - depending on what tools you're using to check it, the apparent results may be super misleading. I.E. if you're using task manager, that's worse than useless.

Quote
That resulted in a lot more starts before the crash, suggesting that garbage collection might be an issue.

(From personal experience: when something weird makes it look like garbage collection is an issue, it's probably not garbage collection.)

But when I had a ship on the screen that had been firing as of the moment I clicked "start game", boom, dead again.

Huh - tried the same thing, pressing "start game" with a ship firing. I see the firing animation flicker during the game creation process (due to buffer switching, not the script actually being called), but there's no crash.

So, I tried a line of code in each of the advance() things causing problems...
Code
if(!engine.isInCampaign() && !engine.isInCampaignSim() && !engine.isMission()) return;

And, wow!  I had FX mod images on the screen, but the game finished the "days" sequence.  Five times in a row. 

Sooooo... there's something.  But from what you've told me, that something is impossible?

Well, those plugins could still be responsible somehow. It'd just have to be something they do before the game creation process starts. If you have the source somewhere, I could take a quick look to see if anything jumps out.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 07:53:51 PM
One more note: if the issue was running out of memory, then you'd definitely get an exception in the log.

When you say "crash", does the game terminate or just hang? Asking because the windows event from your earlier post mentions that it "stopped responding", which is indicative of a hang. If it's a hang, then the most likely cause is an infinite loop somewhere in new game creation. Say, placing markets for explorer society or something like that.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 08:54:21 PM
Absolutely.  The source is in the mods' /scripts directory.

Quote
When you say "crash", does the game terminate or just hang?
It hangs, I think.

Quote
Say, placing markets for explorer society or something like that.
I know it's not related to the Market stuff.  That happens a lot earlier in the sequence.

Moreover, it happens when it's just FX / Rebal (both have EveryFrameCombatScripts). 

Infinite recursion seems pretty unlikely, but I can't rule that out without going over all the situations; pretty sure that's not it, though; the scripts are just essentially big iterators that operate once per frame.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 30, 2017, 09:34:09 PM
Infinite recursion would generate an exception of the stack overflow variety, so that's probably not it.

If you connect to the jvm using jvisualvm, get it to hang, and then to a thread dump, that would tell us what it's doing when it's hanging - that's probably the best way to track this down. jvisualvm should already be on your computer if you've got JDK 7 installer (JDK, not JRE), in the bin/ folder.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 30, 2017, 10:48:21 PM
I'll take a look tomorrow.

Is there a chance that CombatEngineAPIs being created because of the Fleets that get generated during the "days"?   Is that remotely possible?

The logs are full of calls to com.fs.starfarer.api.impl.campaign.fleets.BaseLimitedFleetManager and com.fs.starfarer.api.impl.campaign.shared.StarSystemActivityTracker right before it hangs.

I've seen another crash, even after these changes.  It's just not happening quite as... frequently.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: Alex on May 31, 2017, 08:46:08 AM
Is there a chance that CombatEngineAPIs being created because of the Fleets that get generated during the "days"?   Is that remotely possible?

Well, it's not entirely impossible that one might get created due to a bug somewhere, but it's not going to be advance()-ing. And, anyway, that should be fairly simple to test by adding some logging to the combat plugins.

The logs are full of calls to com.fs.starfarer.api.impl.campaign.fleets.BaseLimitedFleetManager and com.fs.starfarer.api.impl.campaign.shared.StarSystemActivityTracker right before it hangs.

That stuff just logs a lot, so doesn't necessarily mean much.
Title: Re: Global.getSector().isInNewGameAdvance()
Post by: xenoargh on May 31, 2017, 09:26:50 AM
I'll test that theory out by doing some logging.  It's about the only scenario that makes sense.  Perhaps what's going on is that there's an uninitialized variable or something that's causing calls to renderInWorldCoords()... that might explain the FX bug.