Crash to desktop, with recurring error code in .dmp file

Well, I suppose this is a sort of a work in progress. I decided to do some experimenting, and tried to compile(release) a .dll with the pTheirNearestCity initialised, and it still crashes.
Gonna have a break from it for a bit and will upload some data related to the crash later on today.

Edit: With dave uk's dll it doesn't crash, and that's with the initialisation mentioned above,
 
Last edited:
I decided to do some experimenting, and tried to compile(release) a .dll with the pTheirNearestCity initialised, and it still crashes.
Could be a different cause maybe. But if you compile a debug DLL from the same source (i.e. with pNearestCity initialized), there is no crash?
Then the turn completed. :confused:
And the crash with the release DLL is consistently reproducible while the debug DLL never crashes (on that particular turn)? That could be, or maybe it's just crashing randomly with both builds.
So I replaced the VS dll with the standard dave uk dll, tried the turn again, and no crash.
But that DLL had been crashing before, and you haven't changed anything about it? Or ... I guess we're talking about a different turn/ savegame now.
Edit: With dave uk's dll it doesn't crash, and that's with the initialisation mentioned above,
I thought dave's DLL, just with pTheirNearestCity=NULL (and re-compiled for the change to take effect) is what crashes. 🙃
And which team is that? Does it have cities? Assuming that each team has only one member, no player has been defeated and it's not a scenario, the positioning on the Foreign Advisor should correspond to the ID, i.e. the human civ in the first slot (ID 0), then increasing IDs from left to right. That's eTeam, I assume. May also need to know
m_eID under "this" --> "CvTeam" in the Locals window
to really figure out what's going on, i.e. why that latter team is apparently unable to locate cities of team 11 (not even when cheating with map knowledge).
Hovering over the text you mentioned revealed nothing. Looking in the tabs under (locals, auto, etc) provided a lot of info, but as you thought this was possibly not the cause of the crash, I left it and waited for another break.
I guess that makes sense; the debugger doesn't keep track of the node returned by the headMissionQueueNode() function. Could insert a variable above the switch block
MissionTypes eTmp = headMissionQueueNode()->m_data.eMissionType;
then recompile, choose Debug when the assertion fails and then hover over eTmp (or find it in the Locals window). That would be just a temporary change to diagnose the problem. But I'd agree that it's not a priority.
CvTeamAI.cpp Line 6566 bShareValid not defined
Runtime check failure #3 variable 'bShareValid' is being used without being defined.
Sheesh, dave has introduced a lot of those errors. Also the one I came across in my brief debugging session:Though the convention of declaring all variables at the start of a function – surely already widely discouraged at the time – is really Firaxis's fault. So, bShareValid gets declared at the start of CvTeamAI::AI_doWar, but is only initialized and supposed to be used in this if block:
Spoiler :
Code:
if (AI_getWarPlanStateCounter((TeamTypes)iI) > ((10 * iTimeModifier) / (bEnemyVictoryLevel4 ? 400 : 100)))
{
	bAreaValid = false;
	bShareValid = false;

	for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
	{
		if (AI_isPrimaryArea(pLoopArea))
		{
			if (GET_TEAM((TeamTypes)iI).countNumCitiesByArea(pLoopArea) > 0)
			{
				bShareValid = true;

				AreaAITypes eAreaAI = AI_calculateAreaAIType(pLoopArea, true);

				if ( eAreaAI == AREAAI_DEFENSIVE)
				{
					bAreaValid = false;
				}
				else if( eAreaAI == AREAAI_OFFENSIVE )
				{
					bAreaValid = true;
				}
			}
		}
	}

	if ( (bAreaValid && (iEnemyPowerPercent < 140)) || (!bShareValid && (iEnemyPowerPercent < 110)) || (GET_TEAM((TeamTypes)iI).AI_getLowestVictoryCountdown() >= 0) )
	{
		if( gTeamLogLevel >= 1 )
		{
			logBBAI("      Team %d (%S) switching WARPLANS against team %d (%S) from PREPARING_TOTAL to TOTAL after %d turns with enemy power percent %d", getID(), GET_PLAYER(getLeaderID()).getCivilizationDescription(0), iI, GET_PLAYER(GET_TEAM((TeamTypes)iI).getLeaderID()).getCivilizationDescription(0), AI_getWarPlanStateCounter((TeamTypes)iI), iEnemyPowerPercent );
		}
		AI_setWarPlan(((TeamTypes)iI), WARPLAN_TOTAL);
	}
	else if (AI_getWarPlanStateCounter((TeamTypes)iI) > ((20 * iAbandonTimeModifier) / 100))
	{
		if( gTeamLogLevel >= 1 )
		{
			logBBAI("      Team %d (%S) abandoning WARPLAN_TOTAL_PREPARING against team %d (%S) after %d turns with enemy power percent %d", getID(), GET_PLAYER(getLeaderID()).getCivilizationDescription(0), iI, GET_PLAYER(GET_TEAM((TeamTypes)iI).getLeaderID()).getCivilizationDescription(0), AI_getWarPlanStateCounter((TeamTypes)iI), iEnemyPowerPercent );
		}
		AI_setWarPlan(((TeamTypes)iI), NO_WARPLAN);
	}
}
dave has added an else block that uses the undefined variable:
Code:
//CD Tweaks - opportunistic convert to dogpile to take advantage of a powerful target being attacked by someone else
else if (bShareValid && iEnemyPowerPercent >= 140 && GET_TEAM((TeamTypes)iI).getAtWarCount(true) > 0)
I guess the easiest fix is to move this part
Spoiler :
Code:
bAreaValid = false;
bShareValid = false;
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
	if (AI_isPrimaryArea(pLoopArea))
	{
		if (GET_TEAM((TeamTypes)iI).countNumCitiesByArea(pLoopArea) > 0)
		{
			bShareValid = true;

			AreaAITypes eAreaAI = AI_calculateAreaAIType(pLoopArea, true);

			if ( eAreaAI == AREAAI_DEFENSIVE)
			{
				bAreaValid = false;
			}
			else if( eAreaAI == AREAAI_OFFENSIVE )
			{
				bAreaValid = true;
			}
		}
	}
}
above this line:
if (AI_getWarPlanStateCounter((TeamTypes)iI) > ((10 * iTimeModifier) / (bEnemyVictoryLevel4 ? 400 : 100)))
This bug is not going to cause a crash on its own, I also doubt that it'll hurt the AI much, but it can prevent other problems from being reproduced reliably (because the value of bShareValid will be unpredictable if it's not initialized) and can cause out-of-sync errors in multiplayer (resulting from different values of bShareValid on two players' PCs).
Still no connections to the getcompletedspaceshipproject from the dmp file?
No, still just code for AI war planning. I've just seen a dmp posted in the FfH forums. I'm curious what, if anything, lfgr will be able to learn from that.
 
Could be a different cause maybe. But if you compile a debug DLL from the same source (i.e. with pNearestCity initialized), there is no crash?

And the crash with the release DLL is consistently reproducible while the debug DLL never crashes (on that particular turn)? That could be, or maybe it's just crashing randomly with both builds.

But that DLL had been crashing before, and you haven't changed anything about it? Or ... I guess we're talking about a different turn/ savegame now.

I thought dave's DLL, just with pTheirNearestCity=NULL (and re-compiled for the change to take effect) is what crashes. 🙃
And which team is that? Does it have cities? Assuming that each team has only one member, no player has been defeated and it's not a scenario, the positioning on the Foreign Advisor should correspond to the ID, i.e. the human civ in the first slot (ID 0), then increasing IDs from left to right. That's eTeam, I assume. May also need to knowto really figure out what's going on, i.e. why that latter team is apparently unable to locate cities of team 11 (not even when cheating with map knowledge).I guess that makes sense; the debugger doesn't keep track of the node returned by the headMissionQueueNode() function. Could insert a variable above the switch block
MissionTypes eTmp = headMissionQueueNode()->m_data.eMissionType;
then recompile, choose Debug when the assertion fails and then hover over eTmp (or find it in the Locals window). That would be just a temporary change to diagnose the problem. But I'd agree that it's not a priority.Sheesh, dave has introduced a lot of those errors. Also the one I came across in my brief debugging session:Though the convention of declaring all variables at the start of a function – surely already widely discouraged at the time – is really Firaxis's fault. So, bShareValid gets declared at the start of CvTeamAI::AI_doWar, but is only initialized and supposed to be used in this if block:
Spoiler :
Code:
if (AI_getWarPlanStateCounter((TeamTypes)iI) > ((10 * iTimeModifier) / (bEnemyVictoryLevel4 ? 400 : 100)))
{
    bAreaValid = false;
    bShareValid = false;

    for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
    {
        if (AI_isPrimaryArea(pLoopArea))
        {
            if (GET_TEAM((TeamTypes)iI).countNumCitiesByArea(pLoopArea) > 0)
            {
                bShareValid = true;

                AreaAITypes eAreaAI = AI_calculateAreaAIType(pLoopArea, true);

                if ( eAreaAI == AREAAI_DEFENSIVE)
                {
                    bAreaValid = false;
                }
                else if( eAreaAI == AREAAI_OFFENSIVE )
                {
                    bAreaValid = true;
                }
            }
        }
    }

    if ( (bAreaValid && (iEnemyPowerPercent < 140)) || (!bShareValid && (iEnemyPowerPercent < 110)) || (GET_TEAM((TeamTypes)iI).AI_getLowestVictoryCountdown() >= 0) )
    {
        if( gTeamLogLevel >= 1 )
        {
            logBBAI("      Team %d (%S) switching WARPLANS against team %d (%S) from PREPARING_TOTAL to TOTAL after %d turns with enemy power percent %d", getID(), GET_PLAYER(getLeaderID()).getCivilizationDescription(0), iI, GET_PLAYER(GET_TEAM((TeamTypes)iI).getLeaderID()).getCivilizationDescription(0), AI_getWarPlanStateCounter((TeamTypes)iI), iEnemyPowerPercent );
        }
        AI_setWarPlan(((TeamTypes)iI), WARPLAN_TOTAL);
    }
    else if (AI_getWarPlanStateCounter((TeamTypes)iI) > ((20 * iAbandonTimeModifier) / 100))
    {
        if( gTeamLogLevel >= 1 )
        {
            logBBAI("      Team %d (%S) abandoning WARPLAN_TOTAL_PREPARING against team %d (%S) after %d turns with enemy power percent %d", getID(), GET_PLAYER(getLeaderID()).getCivilizationDescription(0), iI, GET_PLAYER(GET_TEAM((TeamTypes)iI).getLeaderID()).getCivilizationDescription(0), AI_getWarPlanStateCounter((TeamTypes)iI), iEnemyPowerPercent );
        }
        AI_setWarPlan(((TeamTypes)iI), NO_WARPLAN);
    }
}
dave has added an else block that uses the undefined variable:
Code:
//CD Tweaks - opportunistic convert to dogpile to take advantage of a powerful target being attacked by someone else
else if (bShareValid && iEnemyPowerPercent >= 140 && GET_TEAM((TeamTypes)iI).getAtWarCount(true) > 0)
I guess the easiest fix is to move this part
Spoiler :
Code:
bAreaValid = false;
bShareValid = false;
for(pLoopArea = GC.getMapINLINE().firstArea(&iLoop); pLoopArea != NULL; pLoopArea = GC.getMapINLINE().nextArea(&iLoop))
{
    if (AI_isPrimaryArea(pLoopArea))
    {
        if (GET_TEAM((TeamTypes)iI).countNumCitiesByArea(pLoopArea) > 0)
        {
            bShareValid = true;

            AreaAITypes eAreaAI = AI_calculateAreaAIType(pLoopArea, true);

            if ( eAreaAI == AREAAI_DEFENSIVE)
            {
                bAreaValid = false;
            }
            else if( eAreaAI == AREAAI_OFFENSIVE )
            {
                bAreaValid = true;
            }
        }
    }
}
above this line:
if (AI_getWarPlanStateCounter((TeamTypes)iI) > ((10 * iTimeModifier) / (bEnemyVictoryLevel4 ? 400 : 100)))
This bug is not going to cause a crash on its own, I also doubt that it'll hurt the AI much, but it can prevent other problems from being reproduced reliably (because the value of bShareValid will be unpredictable if it's not initialized) and can cause out-of-sync errors in multiplayer (resulting from different values of bShareValid on two players' PCs).No, still just code for AI war planning. I've just seen a dmp posted in the FfH forums. I'm curious what, if anything, lfgr will be able to learn from that.
I had a 2nd attempt at a Release dll and the crash has not occurred. So I must have done something wrong. Probably because the release dll was not in the correct place, I found it and put it in the right place. No crashes as yet. Previously, with debugger attached it was not crashing, but I'm more confident, and a little bit more competent with all this now.

So teamid does not correspond to the team number in wbs file?

I will make those adjustments and compile another release dll.
 
Hi f1rpo, I am sorry to trouble you again. I have a debug problem again!

Since we were last in this thread, I've played a couple of 1000 turns with no CTD.
I'm playing on a different map, but same mod(dave uk modmod).
The only modification I made to the cpp files was the initialisation that was causing a crash, you mentioned some other code to change, but I was too eager to keep playing.
I performed a debug, it requested a break at:

CvTeam.cpp
Line 2592
Fassert (iCount >= getAtWarCount(bIgnoreMinors));

Prior to the crash/break, once the game had loaded, this was displayed in VStudio ouput:
Code:
The thread 'win32 thread' 0xd48 exited with code 0
(repeated x2 with different hexadecimal)
Upon my second examination, the hexadecimal was different. I suspect it is unrelated to the CTD, but related to a different issue I am having with MAF. due to having a new laptop.

Can supply more info if required.
 
Good call, probably, to play rather than grind on with minor bugfixes. But this one is another showstopper, I guess(?). (I assume that there is a crash also when ignoring the exception.)

The assertion seems to say that some team is in a war without having set a war plan type. The war plan type (limited, total, defensive, recently attacked, dogpile) is relevant for the AI. Here's the surrounding source code, for reference:
Spoiler :
Code:
int CvTeam::getAnyWarPlanCount(bool bIgnoreMinors) const
{
    int iCount;
    int iI;

    iCount = 0;

    for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
    {
        if (GET_TEAM((TeamTypes)iI).isAlive())
        {
            //CD Tweaks - ignore minors now also ignores civs with no cities (rebels, not quite killed when requireCompleteKills is on)
            if (!bIgnoreMinors || (!(GET_TEAM((TeamTypes)iI).isMinorCiv()) && GET_TEAM((TeamTypes)iI).getNumCities() > 0))
            //CD Tweaks End
            {
                if (AI_getWarPlan((TeamTypes)iI) != NO_WARPLAN)
                {
                    FAssert(iI != getID());
                    iCount++;
                }
            }
        }
    }

    FAssert(iCount >= getAtWarCount(bIgnoreMinors));

    return iCount;
}

int CvTeam::getAtWarCount(bool bIgnoreMinors, bool bIgnoreVassals) const
{
    int iCount;
    int iI;

    iCount = 0;

    for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
    {
        if (GET_TEAM((TeamTypes)iI).isAlive())
        {
            //CD Tweaks - ignore minors now also ignores civs with no cities (rebels, not quite killed when requireCompleteKills is on)
            if (!bIgnoreMinors || (!(GET_TEAM((TeamTypes)iI).isMinorCiv()) && GET_TEAM((TeamTypes)iI).getNumCities() > 0))
            //CD Tweaks End
            {
                if( !bIgnoreVassals || !(GET_TEAM((TeamTypes)iI).isAVassal()))
                {
                    if (isAtWar((TeamTypes)iI))
                    {
                        FAssert(iI != getID());
                        FAssert(!(AI_isSneakAttackPreparing((TeamTypes)iI)));
                        iCount++;
                    }
                }
            }
        }
    }

    return iCount;
}
There are changes by Dave in there, but those should only make a difference when playing with Complete Kills (and even then, no error in the code is apparent to me). To find out more about the cause of the failed assertion, the debugger would be needed; for a start, the call stack at the time that the assertion fails. However, the crash could be entirely unrelated, so it would better – and should not be any more difficult – to debug the crash directly and then to inspect the call stack and other relevant context. As in these screenshots, for example:
Spoiler :
Unhandled exception (popup):
Spoiler :

01.jpg

Call stack, local data at the location of the crash:
Spoiler :

02.jpg

Having moved down a few frames on the stack (through double-click):
Spoiler :

03.jpg


Prior to the crash/break, once the game had loaded, this was displayed in VStudio ouput:
Code:
The thread 'win32 thread' 0xd48 exited with code 0
(repeated x2 with different hexadecimal)
Upon my second examination, the hexadecimal was different. I suspect it is unrelated to the CTD, but related to a different issue I am having with MAF. due to having a new laptop.
Those hexadecimals are thread IDs, it seems. I don't know how those get assigned, but I don't think it's worrisome to get different ones each time that the game exits.
 
Good call, probably, to play rather than grind on with minor bugfixes. But this one is another showstopper, I guess(?). (I assume that there is a crash also when ignoring the exception.)

The assertion seems to say that some team is in a war without having set a war plan type. The war plan type (limited, total, defensive, recently attacked, dogpile) is relevant for the AI. Here's the surrounding source code, for reference:
Spoiler :
Code:
int CvTeam::getAnyWarPlanCount(bool bIgnoreMinors) const
{
    int iCount;
    int iI;

    iCount = 0;

    for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
    {
        if (GET_TEAM((TeamTypes)iI).isAlive())
        {
            //CD Tweaks - ignore minors now also ignores civs with no cities (rebels, not quite killed when requireCompleteKills is on)
            if (!bIgnoreMinors || (!(GET_TEAM((TeamTypes)iI).isMinorCiv()) && GET_TEAM((TeamTypes)iI).getNumCities() > 0))
            //CD Tweaks End
            {
                if (AI_getWarPlan((TeamTypes)iI) != NO_WARPLAN)
                {
                    FAssert(iI != getID());
                    iCount++;
                }
            }
        }
    }

    FAssert(iCount >= getAtWarCount(bIgnoreMinors));

    return iCount;
}

int CvTeam::getAtWarCount(bool bIgnoreMinors, bool bIgnoreVassals) const
{
    int iCount;
    int iI;

    iCount = 0;

    for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
    {
        if (GET_TEAM((TeamTypes)iI).isAlive())
        {
            //CD Tweaks - ignore minors now also ignores civs with no cities (rebels, not quite killed when requireCompleteKills is on)
            if (!bIgnoreMinors || (!(GET_TEAM((TeamTypes)iI).isMinorCiv()) && GET_TEAM((TeamTypes)iI).getNumCities() > 0))
            //CD Tweaks End
            {
                if( !bIgnoreVassals || !(GET_TEAM((TeamTypes)iI).isAVassal()))
                {
                    if (isAtWar((TeamTypes)iI))
                    {
                        FAssert(iI != getID());
                        FAssert(!(AI_isSneakAttackPreparing((TeamTypes)iI)));
                        iCount++;
                    }
                }
            }
        }
    }

    return iCount;
}
There are changes by Dave in there, but those should only make a difference when playing with Complete Kills (and even then, no error in the code is apparent to me). To find out more about the cause of the failed assertion, the debugger would be needed; for a start, the call stack at the time that the assertion fails. However, the crash could be entirely unrelated, so it would better – and should not be any more difficult – to debug the crash directly and then to inspect the call stack and other relevant context. As in these screenshots, for example:
Spoiler :
Unhandled exception (popup):Call stack, local data at the location of the crash:Having moved down a few frames on the stack (through double-click):

Those hexadecimals are thread IDs, it seems. I don't know how those get assigned, but I don't think it's worrisome to get different ones each time that the game exits.
Thanks. I remember that now. I forgot the relevance of it yesterday.
I will get that info and report back.
 
Good call, probably, to play rather than grind on with minor bugfixes. But this one is another showstopper, I guess(?). (I assume that there is a crash also when ignoring the exception.)
OK, I have the call stack


Code:
>    CvGameCoreDLL.dll!CvTeam::getAnyWarPlanCount(bool bIgnoreMinors)  Line 2592 + 0x43 bytes    C++
     CvGameCoreDLL.dll!CvPlayerAI::AI_getStrategyHash()  Line 21501 + 0x1a bytes    C++
     CvGameCoreDLL.dll!CvPlayerAI::AI_isDoStrategy(int iStrategy)  Line 21235 + 0x8 bytes    C++
     CvGameCoreDLL.dll!CvPlayerAI::AI_getVictoryStrategyHash()  Line 21155 + 0x9a bytes    C++
     CvGameCoreDLL.dll!CvPlayerAI::AI_isDoVictoryStrategy(int iVictoryStrategy)  Line 20943 + 0x8 bytes    C++
     CvGameCoreDLL.dll!CvPlayerAI::AI_targetCityValue(CvCity * pCity, bool bRandomize, bool bIgnoreAttackers)  Line 3610 + 0x1d bytes    C++
     CvGameCoreDLL.dll!CvPlayerAI::AI_findTargetCity(CvArea * pArea)  Line 3692 + 0x10 bytes    C++
     CvGameCoreDLL.dll!CvPlayerAI::AI_updateAreaTargets()  Line 1215 + 0xc bytes    C++
     CvGameCoreDLL.dll!CvTeamAI::AI_updateAreaTargets()  Line 272    C++
     CvGameCoreDLL.dll!CvTeamAI::AI_updateAreaStragies(bool bTargets)  Line 256    C++
     CvGameCoreDLL.dll!CvTeamAI::AI_setWarPlan(TeamTypes eIndex, WarPlanTypes eNewValue, bool bWar)  Line 5678 + 0xf bytes    C++
     CvGameCoreDLL.dll!CvTeam::declareWar(TeamTypes eTeam, bool bNewDiplo, WarPlanTypes eWarPlan, bool bCancelPacts)  Line 1520 + 0x6e bytes    C++
     CvGameCoreDLL.dll!CvTeam::declareWar(TeamTypes eTeam, bool bNewDiplo, WarPlanTypes eWarPlan, bool bCancelPacts)  Line 1927    C++
     CvGameCoreDLL.dll!CyTeam::declareWar(int eTeam, bool bNewDiplo, int eWarPlan)  Line 77    C++
     CvGameCoreDLL.dll!boost::python::detail::invoke<int,void (__thiscall CyTeam::*)(int,bool,int),boost::python::arg_from_python<CyTeam &>,boost::python::arg_from_python<int>,boost::python::arg_from_python<bool>,boost::python::arg_from_python<int> >(boost::python::detail::invoke_tag_<1,1> __formal, boost::python::detail::invoke_tag_<1,1> __formal, void (int, bool, int)* & f, boost::python::arg_from_python<CyTeam &> & tc, boost::python::arg_from_python<int> & ac0, boost::python::arg_from_python<bool> & ac1, boost::python::arg_from_python<int> & ac2)  Line 94 + 0x32 bytes    C++
     CvGameCoreDLL.dll!boost::python::detail::caller_arity<4>::impl<void (__thiscall CyTeam::*)(int,bool,int),boost::python::default_call_policies,boost::mpl::vector5<void,CyTeam &,int,bool,int> >::operator()(_object * args_, _object * __formal)  Line 199 + 0x42 bytes    C++
     CvGameCoreDLL.dll!boost::python::objects::caller_py_function_impl<boost::python::detail::caller<void (__thiscall CyTeam::*)(int,bool,int),boost::python::default_call_policies,boost::mpl::vector5<void,CyTeam &,int,bool,int> > >::operator()(_object * args, _object * kw)  Line 39    C++
     boost_python-vc71-mt-1_32.dll!1000ea8d()    
     [Frames below may be incorrect and/or missing, no symbols loaded for boost_python-vc71-mt-1_32.dll]  
     boost_python-vc71-mt-1_32.dll!1000eb45()    
     boost_python-vc71-mt-1_32.dll!100136c1()    
     boost_python-vc71-mt-1_32.dll!10013766()    
     boost_python-vc71-mt-1_32.dll!1000eb8c()    
     boost_python-vc71-mt-1_32.dll!1000ebd0()    
     python24.dll!1e0193cc()    
     python24.dll!1e026702()    
     python24.dll!1e02767e()    
     python24.dll!1e0293b7()    
     python24.dll!1e02a3bc()    
     python24.dll!1e0264b6()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02645b()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02a3bc()    
     python24.dll!1e0264b6()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02645b()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02645b()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02645b()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02a3bc()    
     python24.dll!1e05f62f()    
     python24.dll!1e0193cc()    
     python24.dll!1e02d224()    
     KernelBase.dll!7579dad7()    
     Civ4BeyondSword.exe!00684d80()    
     Civ4BeyondSword.exe!004dd129()    
     python24.dll!1e0193cc()    
     python24.dll!1e026702()    
     python24.dll!1e02767e()    
     python24.dll!1e0293b7()    
     python24.dll!1e02645b()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02645b()    
     python24.dll!1e02766c()    
     python24.dll!1e0293b7()    
     python24.dll!1e02a3bc()    
     python24.dll!1e05f62f()    
     python24.dll!1e0193cc()    
     python24.dll!1e0262a6()    
     python24.dll!1e0193a1()    
     Civ4BeyondSword.exe!008d94f6()    
     Civ4BeyondSword.exe!008d9564()    
     Civ4BeyondSword.exe!008d9704()    
     Civ4BeyondSword.exe!004d4f5b()    
     Civ4BeyondSword.exe!004da0e8()    
     CvGameCoreDLL.dll!CvDllPythonEvents::postEvent(CyArgsList & eventData)  Line 24 + 0x4a bytes    C++
     CvGameCoreDLL.dll!CvDllPythonEvents::reportEndPlayerTurn(int iGameTurn, PlayerTypes ePlayer)  Line 217    C++
     CvGameCoreDLL.dll!CvEventReporter::endPlayerTurn(int iGameTurn, PlayerTypes ePlayer)  Line 123    C++
     CvGameCoreDLL.dll!CvPlayer::doTurn()  Line 4318    C++
     CvGameCoreDLL.dll!CvPlayer::setTurnActive(bool bNewValue, bool bDoTurn)  Line 13568    C++
     CvGameCoreDLL.dll!CvPlayer::setAutoMoves(bool bNewValue)  Line 13651    C++
     CvGameCoreDLL.dll!CvGame::updateMoves()  Line 8006    C++
     CvGameCoreDLL.dll!CvGame::update()  Line 2273    C++
     Civ4BeyondSword.exe!00415321()    
     KernelBase.dll!7584440b()    
     Civ4BeyondSword.exe!004d6f46()    
     Civ4BeyondSword.exe!008f497f()    
     kernel32.dll!767e00f9()    
     ntdll.dll!76ff7bbe()    
     ntdll.dll!76ff7b8e()    
     Civ4BeyondSword.exe!00790074()    
     Civ4BeyondSword.exe!00790074()    
     Civ4BeyondSword.exe!0078006f()    
     Civ4BeyondSword.exe!0078006f()    
     Civ4BeyondSword.exe!0078006f()    
     Civ4BeyondSword.exe!0064006e()    
     Civ4BeyondSword.exe!0064006e()    
     Civ4BeyondSword.exe!0069006c()    
     Civ4BeyondSword.exe!0064006e()    
     Civ4BeyondSword.exe!0075006c()    
     Civ4BeyondSword.exe!0075006c()    
     Civ4BeyondSword.exe!0075006c()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0073005f()    
     Civ4BeyondSword.exe!0073005f()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064002e()    
     Civ4BeyondSword.exe!0064006e()    
     Civ4BeyondSword.exe!0064006e()    
     Civ4BeyondSword.exe!0064006e()    
     Civ4BeyondSword.exe!0064006e()
Image below if that helps
Spoiler :

callstack.png


Where do I go from there? I'm keeping the VStudio open.
 
Last edited:
Seems that the declaration of war comes from Python. I guess that means a Random Event (Wedding Feud) or, more likely, something to do with Revolutions, Barbarian Civs or Assimilation. (I've searched the mod's Python folder for declareWar calls.) Double-clicking the CvTeam::declareWar frames on the stack and, for each of the two frames, hovering over the eTeam variable in the code editor should reveal the IDs of the teams that are going to war. Then, if you can figure out which civs those IDs correspond to (through the scenario file?), perhaps then you can also guess why this war is being declared, and maybe that'll give one of us an idea why the at-war counter is inconsistent with the war-plan counter.

I still don't really think that this inconsistency is directly related to the crash. So it would imo be better to ignore the failed assertion and look at the call stack at the time of the actual crash. But, if you can easily find out just what sort of war this is - by debugging the failed assertion -, then that could be a good idea too – before moving on to the crash.
 
I could only find 1 eTeam variable in the stack. It referred to a team who is a vassal. They are already at war.
I reloaded with debugger, bribed the master of the vassal to make peace.
A different crash occurred, one that I have already encountered, but was able to work around. I attempted to range bombard a tile, it had an enemy worker on the tile. The game crashed when I clicked the bombard icon.
This crash is similar, but with an AI team attempting the same thing. A variable is NULL and it is causing a freak out.
Call stack below.
Spoiler :
callstack.2.png
 
I could only find 1 eTeam variable in the stack. It referred to a team who is a vassal. They are already at war.
Oh, I see, looking at the actual line numbers, there's a recursive declareWar call. I had assumed that A declares on B (eTeam) and B on A, but it's actually A on B (apparently triggered from Python) and then a vassal of A also declaring on B. And B, if I interpret your post correctly, is a vassal as well. I have a vague suspicion that the Python code should only cause free teams to declare, i.e. that it might lack an isVassal check somewhere. Or, if you say they're "already at war," maybe an isAtWar check is missing in Python. The debugger can't inspect the Python context, but one could search for all declareWar calls in Python and insert print statements to identify the one that causes this particular war.
I reloaded with debugger, bribed the master of the vassal to make peace.
That's a pragmatic solution. :)

The bombardment crash happens here, apparently (and, for once, a failed assertion should occur right before the crash):
Spoiler :
Code:
bool CvUnitAI::AI_bombardCity()
{
	PROFILE_FUNC();

	CvCity* pBombardCity;

	if (canBombard(plot()) || canRBombard(plot()))
	{
		pBombardCity = bombardTarget(plot(),true);
		FAssertMsg(pBombardCity != NULL, "BombardCity is not assigned a valid value");

		// do not bombard cities with no defenders
		int iDefenderStrength = pBombardCity->plot()->AI_sumStrength(...
So either canBombard or (more likely) the DCM function canRBombard returns true, but bombardTarget (with bIncludeRBombard=true, a parameter added by Dave) fails to find a city to bombard. These are all CvUnit functions. I don't see anything in canRBombard that guarantees that there will be a city to bombard; the function only seems to check whether the unit has a capacity to execute ranged bombardments in general. A little strange to me that this doesn't crash more frequently. Anyway, I don't know who added that alternative canRBombard check or to what end; can't see any good that it might do. So I'd recommend just commenting that out:
Code:
if (canBombard(plot()) /*|| canRBombard(plot())*/)

Edit: I'm guessing that the intention was to allow the AI to use ranged bombardment (though I don't even know the specific rules for that DCM mechanism). The important part to change was CvUnit::bombardTarget; that's where Dave added the bIncludeRBombard parameter. Perhaps those changes are correctly implemented. He probably also added the canRBombard check, and that's not needed and, in fact, harmful.
 
Last edited:
Oh, I see, looking at the actual line numbers, there's a recursive declareWar call. I had assumed that A declares on B (eTeam) and B on A, but it's actually A on B (apparently triggered from Python) and then a vassal of A also declaring on B. And B, if I interpret your post correctly, is a vassal as well. I have a vague suspicion that the Python code should only cause free teams to declare, i.e. that it might lack an isVassal check somewhere. Or, if you say they're "already at war," maybe an isAtWar check is missing in Python. The debugger can't inspect the Python context, but one could search for all declareWar calls in Python and insert print statements to identify the one that causes this particular war.
That's a pragmatic solution. :)

The bombardment crash happens here, apparently (and, for once, a failed assertion should occur right before the crash):
Spoiler :
Code:
bool CvUnitAI::AI_bombardCity()
{
    PROFILE_FUNC();

    CvCity* pBombardCity;

    if (canBombard(plot()) || canRBombard(plot()))
    {
        pBombardCity = bombardTarget(plot(),true);
        FAssertMsg(pBombardCity != NULL, "BombardCity is not assigned a valid value");

        // do not bombard cities with no defenders
        int iDefenderStrength = pBombardCity->plot()->AI_sumStrength(...
So either canBombard or (more likely) the DCM function canRBombard returns true, but bombardTarget (with bIncludeRBombard=true, a parameter added by Dave) fails to find a city to bombard. These are all CvUnit functions. I don't see anything in canRBombard that guarantees that there will be a city to bombard; the function only seems to check whether the unit has a capacity to execute ranged bombardments in general. A little strange to me that this doesn't crash more frequently. Anyway, I don't know who added that alternative canRBombard check or to what end; can't see any good that it might do. So I'd recommend just commenting that out:
Code:
if (canBombard(plot()) /*|| canRBombard(plot())*/)

Edit: I'm guessing that the intention was to allow the AI to use ranged bombardment (though I don't even know the specific rules for that DCM mechanism). The important part to change was CvUnit::bombardTarget; that's where Dave added the bIncludeRBombard parameter. Perhaps those changes are correctly implemented. He probably also added the canRBombard check, and that's not needed and, in fact, harmful.

So I edit CvUnitAI.cpp
I can place # in front of each line(of code that you mention)
Re compile a release .dll

Is that correct?
 
It's just this one line
if (canBombard(plot()) /*|| canRBombard(plot())*/)
and I've already inserted comment tokens. C++ uses // for single-line or end-of-line comments and /*...*/ for multi-line or in-line comments. Hopefully, that'll avoid the crash upon ranged AI units attempting to bombard cities. Might want to compile a Debug build first in case that the crash (or a related one) still occurs. For continuing your game, you'll probably want a Release build for greater speed and to avoid seeing (non-critical) failed assertions.
 
Top Bottom