MNAI-U: unofficial build & bugfixes

This says that somebody is trying to read from an address they aren't meant to. Normally, I'd assume this is due to an invalid eTeam parameter (e.g., NO_TEAM), but then I'd assume the error would show up in the getTeam() call inside getTeamNonInl(). Anyway, there's no way for me to solve this without access to the full stacktrace. If you want, I can debug this for you, but you'd need to upload the mod and a savegame, of course. Also I probably won't get to it the next 1-2 weeks.


That's unsurprising, since I didn't release a new version since your last report. :)
I'll quote myself:



You mean it's already glowing blue, and stays that way despite losing XP?

There are almost 900 references in python to gc.getTeam so I have not checked many of them, but the very first one to show when I use Find All looks the most suspicious to me
in Blizzards.py
Code:
   def isDeepening(self):
       iDeepening = gc.getInfoTypeForString('PROJECT_THE_DEEPENING')
       for i in range(CyGame().countCivTeamsAlive()-1):
           pTeam = gc.getTeam(i)
           if pTeam.getProjectCount(iDeepening) > 0:
               return true

       return false
That is not how we cycle through teams anywhere else.
Do you think it could fix the bug if I changed that code to this:
Code:
   def isDeepening(self):
       iDeepening = gc.getInfoTypeForString('PROJECT_THE_DEEPENING')
       for iLoopTeam in xrange(gc.getMAX_TEAMS()):
           pTeam = gc.getTeam(iLoopTeam)
           if pTeam.isAlive():
               if pTeam.getProjectCount(iDeepening) > 0:
                   return True

       return False
(I had made other changes tot hat file to tie blizzard spreading to the White Hand religion instead of the Illian civ, but that part of the code is the same in my modmod and MNAI so you may want to change it too.)

edit. It didn't stop a getTeamNonInl() related crash though.


When I went to change "%s1" to "a city," I noticed what that I think is the real problem.

pCity is a parameter of playerCityLost, but that parameter does not get used at the end of the function because the pCity variable was just reassigned multiple times in the previous loop. That means that the name would never be from the city that is lost but rather from whatever city appears last in PyPlayer(iPlayer).getCityList().

I have not tested this yet, but suspect that the bug could be easily fixed by using loopCity instead of pCity within that loop.

Edit: It didn't stop the
Code:
def playerCityLost( player, pCity, bConquest = True ) :

       if( player.getNumCities() < 1 or player.isBarbarian() ) :
           return

       iPlayer = player.getID()
       capital = player.getCapitalCity()
   
       if not capital.isNone():
           capitalArea = capital.area().getID()
       else:
           capitalArea = -1

       iRevIdxChange = 0

       if( game.getGameTurn() - pCity.getGameTurnAcquired() < 2 ) :
           iRevIdxChange = 0
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: very recently acquired, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( game.getGameTurn() - pCity.getGameTurnAcquired() < 20 ) :
           iRevIdxChange = 50
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: recently acquired, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.isCapital() ) :
           iRevIdxChange = 400
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: capital, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.getHighestPopulation() < 4 ) :
           iRevIdxChange = 50
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: small city, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.plot().calculateCulturePercent(iPlayer) > 60 and pCity.countTotalCultureTimes100() > 100*100 ) :
           iRevIdxChange = 150
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: majority nationality, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.getOriginalOwner() == iPlayer ) :
           iRevIdxChange = 125
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: original founder, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       else :
           iRevIdxChange = 100
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: default, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))

       if( not pCity.area().getID() == capitalArea and iRevIdxChange > 25 ) :
           iRevIdxChange -= 25
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - %s is a colony, reducing effect to %d"%(pCity.getName(),iRevIdxChange))

       if( not bConquest ) :
           if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Not conquest, reducing rev index increase from %d"%(iRevIdxChange))
           iRevIdxChange = iRevIdxChange/3.0

       iRevIdxChange = int(math.floor( cityLostModifier*iRevIdxChange + .5))
   
       if( player.isRebel() ) :
           iRevIdxChange /= 2

       if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s (%d bConq): %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),bConquest,iRevIdxChange))



       for city in PyPlayer(iPlayer).getCityList() :
           pCity = city.GetCy()
           pCity.changeRevolutionIndex( iRevIdxChange )
           revIdxHist = RevData.getCityVal(pCity,'RevIdxHistory')
           revIdxHist['Events'][0] += iRevIdxChange
           RevData.updateCityVal( pCity, 'RevIdxHistory', revIdxHist )

       # REVOLUTION_ALERTS 03/2021 lfgr
       if iRevIdxChange != 0 :
           InterfaceUtils.addMessage( iPlayer, PyHelpers.getText(
                   "[COLOR_NEGATIVE_TEXT]The loss of %s1 has increased revolutionary sentiment throughout your empire[COLOR_REVERT]",
                   pCity.getName() ) ) # LFGR_TODO: Translate
Code:
def playerCityLost( player, pCity, bConquest = True ) :

       if( player.getNumCities() < 1 or player.isBarbarian() ) :
           return

       iPlayer = player.getID()
       capital = player.getCapitalCity()
   
       if not capital.isNone():
           capitalArea = capital.area().getID()
       else:
           capitalArea = -1

       iRevIdxChange = 0

       if( game.getGameTurn() - pCity.getGameTurnAcquired() < 2 ) :
           iRevIdxChange = 0
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: very recently acquired, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( game.getGameTurn() - pCity.getGameTurnAcquired() < 20 ) :
           iRevIdxChange = 50
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: recently acquired, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.isCapital() ) :
           iRevIdxChange = 400
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: capital, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.getHighestPopulation() < 4 ) :
           iRevIdxChange = 50
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: small city, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.plot().calculateCulturePercent(iPlayer) > 60 and pCity.countTotalCultureTimes100() > 100*100 ) :
           iRevIdxChange = 150
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: majority nationality, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       elif( pCity.getOriginalOwner() == iPlayer ) :
           iRevIdxChange = 125
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: original founder, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))
       else :
           iRevIdxChange = 100
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s: default, %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),iRevIdxChange))

       if( not pCity.area().getID() == capitalArea and iRevIdxChange > 25 ) :
           iRevIdxChange -= 25
           #if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - %s is a colony, reducing effect to %d"%(pCity.getName(),iRevIdxChange))

       if( not bConquest ) :
           if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Not conquest, reducing rev index increase from %d"%(iRevIdxChange))
           iRevIdxChange = iRevIdxChange/3.0

       iRevIdxChange = int(math.floor( cityLostModifier*iRevIdxChange + .5))
   
       if( player.isRebel() ) :
           iRevIdxChange /= 2

       if( LOG_DEBUG ) : CvUtil.pyPrint("  Revolt - Loss of %s by %s (%d bConq): %d rev idx change"%(pCity.getName(),player.getCivilizationDescription(0),bConquest,iRevIdxChange))

       for city in PyPlayer(iPlayer).getCityList() :
           loopCity = city.GetCy()
           loopCity.changeRevolutionIndex( iRevIdxChange )
           revIdxHist = RevData.getCityVal(loopCity,'RevIdxHistory')
           revIdxHist['Events'][0] += iRevIdxChange
           RevData.updateCityVal( loopCity, 'RevIdxHistory', revIdxHist )

       # REVOLUTION_ALERTS 03/2021 lfgr
       if iRevIdxChange != 0 :
           InterfaceUtils.addMessage( iPlayer, PyHelpers.getText(
                   "[COLOR_NEGATIVE_TEXT]The loss of %s1 has increased revolutionary sentiment throughout your empire[COLOR_REVERT]",
                   pCity.getName() ) ) # LFGR_TODO: Translate

Edit: I just analyzed another dump file where the memory issues does not seem to be in the same function
Code:
Microsoft (R) Windows Debugger Version 10.0.22415.1002 X86
Copyright (c) Microsoft Corporation. All rights reserved.


Loading Dump File [C:\Users\jonmi_r2m\AppData\Local\Temp\Civ4BeyondSword.exe.dmp]
User Mini Dump File with Full Memory: Only application data is available


************* Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       srv*
Symbol search path is: srv*
Executable search path is:
Windows 8 Version 9200 MP (4 procs) Free x86 compatible
Product: WinNt, suite: SingleUserTS Personal
Machine Name:
Debug session time: Tue Aug 10 09:39:13.000 2021 (UTC - 4:00)
System Uptime: 2 days 1:59:51.197
Process Uptime: 0 days 0:57:35.000
................................................................
........................................
Loading unloaded module list
..........................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(1c5c.29c8): Access violation - code c0000005 (first/second chance not available)
For analysis of this file, run !analyze -v
eax=00000000 ebx=7d807ad8 ecx=00000000 edx=00ae9654 esi=7d807ab0 edi=7d807b08
eip=77c238dc esp=0019e850 ebp=0019e85c iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
ntdll!NtGetContextThread+0xc:
77c238dc c20800          ret     8
0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

*** WARNING: Unable to verify checksum for Mss32.dll

KEY_VALUES_STRING: 1

   Key  : AV.Dereference
    Value: NullPtr

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.mSec
    Value: 4077

    Key  : Analysis.DebugAnalysisManager
    Value: Create

    Key  : Analysis.Elapsed.mSec
    Value: 16183

    Key  : Analysis.Init.CPU.mSec
    Value: 859

    Key  : Analysis.Init.Elapsed.mSec
    Value: 32622

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 79

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 179991

    Key  : Timeline.Process.Start.DeltaSec
    Value: 3455

    Key  : WER.Process.Version
    Value: 3.1.9.0


NTGLOBALFLAG:  0

PROCESS_BAM_CURRENT_THROTTLED: 0

PROCESS_BAM_PREVIOUS_THROTTLED: 0

APPLICATION_VERIFIER_FLAGS:  0

CONTEXT:  (.ecxr)
eax=00be6274 ebx=00000000 ecx=00000000 edx=00ae9654 esi=00000000 edi=00000000
eip=004d7dd4 esp=0019f9fc ebp=5c76dd28 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010202
Civ4BeyondSword+0xd7dd4:
004d7dd4 8b01            mov     eax,dword ptr [ecx]  ds:002b:00000000=????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 004d7dd4 (Civ4BeyondSword+0x000d7dd4)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 00000000
Attempt to read from address 00000000

PROCESS_NAME:  Civ4BeyondSword.exe

READ_ADDRESS:  00000000

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  00000000

EXCEPTION_PARAMETER2:  00000000

STACK_TEXT: 
WARNING: Stack unwind information not available. Following frames may be wrong.
0019f9f8 04642387     00000000 00000000 00000000 Civ4BeyondSword+0xd7dd4
00000000 00000000     00000000 00000000 00000000 CvGameCoreDLL!CvUnit::plot+0x4b7


SYMBOL_NAME:  Civ4BeyondSword+d7dd4

MODULE_NAME: Civ4BeyondSword

IMAGE_NAME:  Civ4BeyondSword.exe

STACK_COMMAND:  ~0s ; .ecxr ; kb

FAILURE_BUCKET_ID:  NULL_POINTER_READ_c0000005_Civ4BeyondSword.exe!Unknown

OSPLATFORM_TYPE:  x86

OSNAME:  Windows 8

IMAGE_VERSION:  3.1.9.0

FAILURE_ID_HASH:  {72026eb1-068b-3165-ce81-b11615a37d19}

Followup:     MachineOwner
---------
 
Last edited:
It is and it can be leveled up to level 2 with less than 4 xp.
What's probably happening is the flag for a level up is set because it reached the xp threshold but isn't cleared once the xp goes back down again. There is simply nothing in the code to check to see if it no longer qualifies for the level up after the xp loss occurs. On the bright side it also means that if an xp loss would take you back down a level (after you had picked a promotion) you don't lose a level nor lose the promotion you had gained because of it.
 
I'm getting a crash with a somewhat different dump file in my current game when I set a Lightbringer to explore automatically.

Spoiler :
Code:
Microsoft (R) Windows Debugger Version 10.0.22415.1002 X86
Copyright (c) Microsoft Corporation. All rights reserved.


Loading Dump File [C:\Users\jonmi_r2m\AppData\Local\Temp\Civ4BeyondSword.exe.dmp]
User Mini Dump File with Full Memory: Only application data is available


************* Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       srv*
Symbol search path is: srv*
Executable search path is:
Windows 8 Version 9200 MP (4 procs) Free x86 compatible
Product: WinNt, suite: SingleUserTS Personal
Machine Name:
Debug session time: Thu Sep  2 16:03:50.000 2021 (UTC - 4:00)
System Uptime: 9 days 0:25:11.274
Process Uptime: 0 days 0:37:06.000
................................................................
.........................................
Loading unloaded module list
............................
This dump file has an exception of interest stored in it.
The stored exception information can be accessed via .ecxr.
(31e4.298): Access violation - code c0000005 (first/second chance not available)
For analysis of this file, run !analyze -v
eax=00000000 ebx=724cafa8 ecx=040e0fb8 edx=048dc8a8 esi=724caf80 edi=724cafd8
eip=77bb38dc esp=0019e890 ebp=0019e89c iopl=0         nv up ei pl nz ac pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
ntdll!NtGetContextThread+0xc:
77bb38dc c20800          ret     8
0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

*** WARNING: Unable to verify checksum for python24.dll
*** WARNING: Unable to verify checksum for Mss32.dll

KEY_VALUES_STRING: 1

   Key  : AV.Dereference
    Value: NullClassPtr

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.mSec
    Value: 4843

    Key  : Analysis.DebugAnalysisManager
    Value: Create

    Key  : Analysis.Elapsed.mSec
    Value: 11946

    Key  : Analysis.Init.CPU.mSec
    Value: 640

    Key  : Analysis.Init.Elapsed.mSec
    Value: 8465

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 80

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 779111

    Key  : Timeline.Process.Start.DeltaSec
    Value: 2226

    Key  : WER.Process.Version
    Value: 3.1.9.0


NTGLOBALFLAG:  0

PROCESS_BAM_CURRENT_THROTTLED: 0

PROCESS_BAM_PREVIOUS_THROTTLED: 0

APPLICATION_VERIFIER_FLAGS:  0

CONTEXT:  (.ecxr)
eax=00000000 ebx=4b708710 ecx=040e0fb8 edx=048dc8a8 esi=00000000 edi=ffffffff
eip=04713199 esp=0019fa38 ebp=4b6f9ba8 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
CvGameCoreDLL!CombatDetails::CombatDetails+0x40939:
04713199 837e14ff        cmp     dword ptr [esi+14h],0FFFFFFFFh ds:002b:00000014=????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 04713199 (CvGameCoreDLL!CombatDetails::CombatDetails+0x00040939)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 00000014
Attempt to read from address 00000014

PROCESS_NAME:  Civ4BeyondSword.exe

READ_ADDRESS:  00000014

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  00000000

EXCEPTION_PARAMETER2:  00000014

STACK_TEXT: 
WARNING: Stack unwind information not available. Following frames may be wrong.
0019fa58 0465a6a7     00000000 00000000 4b6f9ba8 CvGameCoreDLL!CombatDetails::CombatDetails+0x40939
0019fa74 0470d5ed     00000000 00000007 4b6f9ba8 CvGameCoreDLL!CvPlot::isGoody+0x187
0019faa8 046dcb21     00000007 00000007 0046606e CvGameCoreDLL!CombatDetails::CombatDetails+0x3ad8d
0019fad4 1e029a88     00000001 648b0000 195ba1a8 CvGameCoreDLL!CombatDetails::CombatDetails+0xa2c1
00000000 00000000     00000000 00000000 00000000 python24!PyEval_EvalFrame+0x2208


SYMBOL_NAME:  CvGameCoreDLL!CombatDetails::CombatDetails+40939

MODULE_NAME: CvGameCoreDLL

IMAGE_NAME:  CvGameCoreDLL.dll

STACK_COMMAND:  ~0s ; .ecxr ; kb

FAILURE_BUCKET_ID:  NULL_CLASS_PTR_READ_c0000005_CvGameCoreDLL.dll!CombatDetails::CombatDetails

OSPLATFORM_TYPE:  x86

OSNAME:  Windows 8

FAILURE_ID_HASH:  {7c76b37e-d32c-3bb3-90a1-57c1de3a48f4}

Followup:     MachineOwner
---------

0:000> !analyze -v
*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************


KEY_VALUES_STRING: 1

    Key  : AV.Dereference
    Value: NullClassPtr

    Key  : AV.Fault
    Value: Read

    Key  : Analysis.CPU.mSec
    Value: 3874

    Key  : Analysis.DebugAnalysisManager
    Value: Create

    Key  : Analysis.Elapsed.mSec
    Value: 4422

    Key  : Analysis.Init.CPU.mSec
    Value: 5484

    Key  : Analysis.Init.Elapsed.mSec
    Value: 20420

    Key  : Analysis.Memory.CommitPeak.Mb
    Value: 84

    Key  : Timeline.OS.Boot.DeltaSec
    Value: 779111

    Key  : Timeline.Process.Start.DeltaSec
    Value: 2226

    Key  : WER.Process.Version
    Value: 3.1.9.0


NTGLOBALFLAG:  0

PROCESS_BAM_CURRENT_THROTTLED: 0

PROCESS_BAM_PREVIOUS_THROTTLED: 0

APPLICATION_VERIFIER_FLAGS:  0

CONTEXT:  (.ecxr)
eax=00000000 ebx=4b708710 ecx=040e0fb8 edx=048dc8a8 esi=00000000 edi=ffffffff
eip=04713199 esp=0019fa38 ebp=4b6f9ba8 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
CvGameCoreDLL!CombatDetails::CombatDetails+0x40939:
04713199 837e14ff        cmp     dword ptr [esi+14h],0FFFFFFFFh ds:002b:00000014=????????
Resetting default scope

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 04713199 (CvGameCoreDLL!CombatDetails::CombatDetails+0x00040939)
   ExceptionCode: c0000005 (Access violation)
  ExceptionFlags: 00000000
NumberParameters: 2
   Parameter[0]: 00000000
   Parameter[1]: 00000014
Attempt to read from address 00000014

PROCESS_NAME:  Civ4BeyondSword.exe

READ_ADDRESS:  00000014

ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  00000000

EXCEPTION_PARAMETER2:  00000014

STACK_TEXT: 
WARNING: Stack unwind information not available. Following frames may be wrong.
0019fa58 0465a6a7     00000000 00000000 4b6f9ba8 CvGameCoreDLL!CombatDetails::CombatDetails+0x40939
0019fa74 0470d5ed     00000000 00000007 4b6f9ba8 CvGameCoreDLL!CvPlot::isGoody+0x187
0019faa8 046dcb21     00000007 00000007 0046606e CvGameCoreDLL!CombatDetails::CombatDetails+0x3ad8d
0019fad4 1e029a88     00000001 648b0000 195ba1a8 CvGameCoreDLL!CombatDetails::CombatDetails+0xa2c1
00000000 00000000     00000000 00000000 00000000 python24!PyEval_EvalFrame+0x2208


SYMBOL_NAME:  CvGameCoreDLL!CombatDetails::CombatDetails+40939

MODULE_NAME: CvGameCoreDLL

IMAGE_NAME:  CvGameCoreDLL.dll

STACK_COMMAND:  ~0s ; .ecxr ; kb

FAILURE_BUCKET_ID:  NULL_CLASS_PTR_READ_c0000005_CvGameCoreDLL.dll!CombatDetails::CombatDetails

OSPLATFORM_TYPE:  x86

OSNAME:  Windows 8

FAILURE_ID_HASH:  {7c76b37e-d32c-3bb3-90a1-57c1de3a48f4}

Followup:     MachineOwner
---------

The first few times I clicked the automate button my mistake, and wasn't sure if it was the spread religion or explore version.

This time I pressed explore automatically and the game crashed when the unit entered the tile with the Seven Pines. I thought at first the issue could have to do with the unit losing the Eremite promotion when leaving a city with a Desert Shrine and thus losing the ability to see the normally invisible Mask of Kylorin on that tile in the middle of capturing it. Then I loaded the save and found that the mask was already mine as it had been captured many turns prior, and found that I got the same issue if I first moved the Lightbringer out of the city and let its Eremite promotion be removed before clicking automate.

I then found it happens when other automated units enter that same tile. It also happened whenever I automated a unit that was already on that tile.

I was guessing it had something to do with either SPELL_PEACE_SEVEN_PINES or SPELL_TAKE_MASK_KYLORIN, although both spells worked fine when I cast them manually.

When I change <bAllowAI>0</bAllowAI> to <bAllowAI>0</bAllowAI> in SPELL_TAKE_MASK_KYLORIN then I don't get the crash anymore. Automated unit don't go onto that tile anymore when exploring, and those automated while on that tile just leave.


While trying to see if the Peace spell could be part of the problem I noticed that it uses for iTeam2 in xrange(gc.getMAX_CIV_TEAMS()): while every other spell besides Veil of Night uses for iTeam2 in xrange(gc.getMAX_TEAMS()):

What is the difference between gc.getMAX_CIV_TEAMS() and gc.getMAX_TEAMS()? Is there a reason why we ought to use one or the other?
 
Looking at the code, it seems that "explore automatically" indeed allows picking up equipment. That seems like a bad idea anyway, but it doesn't explain the crash, of course.
I can't really make sense out of the stack trace you posted. If you can't figure out the crash you could also upload the "full dump" file somewhere (which unfortunately is something like 1 GB). That usually shows the full program state at the time of the crash. I could take a look at it in about 2 weeks.

Another thought: Does SPELL_TAKE_MASK_KYLORIN do anything unusual, like killing the unit that takes it?

What is the difference between gc.getMAX_CIV_TEAMS() and gc.getMAX_TEAMS()? Is there a reason why we ought to use one or the other?
MAY_TEAMS is one more. It includes the barbarian team, while MAX_CIV_TEAMS does not.
 
Looking at the code, it seems that "explore automatically" indeed allows picking up equipment. That seems like a bad idea anyway, but it doesn't explain the crash, of course.
I can't really make sense out of the stack trace you posted. If you can't figure out the crash you could also upload the "full dump" file somewhere (which unfortunately is something like 1 GB). That usually shows the full program state at the time of the crash. I could take a look at it in about 2 weeks.

Another thought: Does SPELL_TAKE_MASK_KYLORIN do anything unusual, like killing the unit that takes it?
It does not kill the unit directly, but most casters cannot survive summoning a hostile Elder Dragon (with limited duration and a promotion that combines the effects of Hidden Nationality and Enraged) that is immediately forced to attack the caster.
Code:
       <SpellInfo>
           <Type>SPELL_TAKE_MASK_KYLORIN</Type>
           <Description>TXT_KEY_SPELL_TAKE_MASK_OF_ESUS</Description>
           <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
           <Help>TXT_KEY_SPELL_TAKE_EQUIPMENT_MASK_KYLORIN_HELP</Help>
           <TechPrereq>TECH_ARCANE_LORE</TechPrereq>
           <UnitInStackPrereq>EQUIPMENT_MASK_KYLORIN</UnitInStackPrereq>
           <bCasterNoDuration>1</bCasterNoDuration>
           <bAllowAI>0</bAllowAI>
           <iAIWeight>50</iAIWeight>
           <bIgnoreHasCasted>1</bIgnoreHasCasted>
           <AddPromotionType1>PROMOTION_MASK_OF_ESUS</AddPromotionType1>
           <bBuffCasterOnly>1</bBuffCasterOnly>
           <bAbility>1</bAbility>
           <bNoInterruptUnitCycling>1</bNoInterruptUnitCycling>
           <iMiscastChance>-100</iMiscastChance>
           <PyResult>spellTakeMaskKylorin(pCaster, eSpell)</PyResult>
           <PyRequirement>reqTakeEquipmentUnit(pCaster, 'EQUIPMENT_MASK_KYLORIN')</PyRequirement>
           <PyHelp>helpTogglePromotionsBuffCaster(lpUnits, eSpell, ['PROMOTION_MASK_OF_ESUS'], [], False)</PyHelp>
           <Effect>EFFECT_SPELL1</Effect>
           <Sound>AS3D_SPELL_TRAIN</Sound>
           <bGraphicalOnly>1</bGraphicalOnly>
           <Button>Art/Units/Heroes/ImprimaturCouncil/Kylorin/Kylorin.dds</Button>
       </SpellInfo>

def spellTakeMaskKylorin(pCaster, eSpell=-1):
   spellTakeEquipmentUnit(pCaster, 'EQUIPMENT_MASK_KYLORIN')
   if pCaster.getUnitType() != gc.getInfoTypeForString('UNIT_KYLORIN'):
       pPlot = pCaster.plot()
       iUnit = gc.getInfoTypeForString('UNIT_ELDER_DRAGON')
       pPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
       newUnit = pPlayer.initUnit(iUnit, pPlot.getX(), pPlot.getY(), UnitAITypes.UNITAI_HERO, DirectionTypes.DIRECTION_SOUTH)
       newUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_PARANOID'), True)
       newUnit.setDuration(2)
       if not pCaster.isDelayedDeath():
           newUnit.attack(pCaster.plot(), False)

Each of the named masks has some similar code, with the Mask of Barbatos being defended by Barbatos himself, the Mask of Asmoday being defended by multiple Succubi, the Mask of Alexis being defended by multiple Changelings, the Mask of Os-Gabella defended by multiple Warp Bubbles, and the Mask of Faeryl Viconia being defended by multiple Unseelie Shadows (although I don't actually place this one on the map, as canonically the others Masks were each hidden at a specific unique feature while Faeryl carries her Mask with her).

The generic Mask of Esus has no such code to make any unit defend it. All of the SPELL_TAKE_MASK_X abilities add the same Mask of Esus promotion, which will cause the unit to drop a generic Mask of Esus instead of one of the special named ones that are more dangerous to pick up.

It may be worth noting that the Mask of Esus promotion also make the unit Hidden Nationality.


The spell works fine when I cast it manually, only crashing when the unit is automated. When I automate a unit and get a crash I don't see the dragon on screen or any mention of it in the logs. Are you thinking it might get generated after the unit has moved on to a different tile where attacking caused a problem, or that the automation code continues trying to do things with the unit after it is killed?


If I remove <bAllowAI>1</bAllowAI> from all the take equipment spells, would that only prevent automated units from picking up equipment or would it also prevent any AI players from using equipment?
MAY_TEAMS is one more. It includes the barbarian team, while MAX_CIV_TEAMS does not.
Does that mean I could safely eliminate statements like if iTeam2 != gc.getBARBARIAN_TEAM(): or if pPlayer.isBarbarian():continue from within loops if I use MAX_CIV_TEAMS instead of MAY_TEAMS?


--
By the way, I often find that priests wind up with the Automate Terriforming AI/automation button even when they have no terraforming spells. I think it is due to terraforming spells that use the Divine promotion and have a religion prereq different from that of the caster.
 
Last edited:
not sure if it has to do with the issue MC is reporting, but I've noticed a few CTDs in EMM 0.6b6 after enabling AI Autoplay: it doesn't always crash, but when it does it's on turn 1, shortly after enabling it
 
that the automation code continues trying to do things with the unit after it is killed?
Yes, that would be my guess.

If I remove <bAllowAI>1</bAllowAI> from all the take equipment spells, would that only prevent automated units from picking up equipment or would it also prevent any AI players from using equipment?
Both, as far as I can tell.

Does that mean I could safely eliminate statements like if iTeam2 != gc.getBARBARIAN_TEAM(): or if pPlayer.isBarbarian():continue from within loops if I use MAX_CIV_TEAMS instead of MAY_TEAMS?
Exactly.

By the way, I often find that priests wind up with the Automate Terriforming AI/automation button even when they have no terraforming spells. I think it is due to terraforming spells that use the Divine promotion and have a religion prereq different from that of the caster.
You're probably right, I'll fix that.

not sure if it has to do with the issue MC is reporting, but I've noticed a few CTDs in EMM 0.6b6 after enabling AI Autoplay: it doesn't always crash, but when it does it's on turn 1, shortly after enabling it
Do you have a savegame where this reliably happens?
 
i've noticed that the old HOF at the end of the game is now gone, cause the game installs a different folder from FFH/MNAI , as of a few versions ago. can you make a way to load the old HOF into the new folder? losing the old one breaks immersion
 
Last edited:
After the stasis spell ends, Creative leaders do not always get back the +2 culture per turn (seems to work in the capitol, but other cities sometimes end up with 0 cultures per turn after the spell had ended).
 

Attachments

  • MNAI 1.CivBeyondSwordSave
    328.7 KB · Views: 17
Last edited:
When clicking on the completed rituals log:
 

Attachments

  • Civ4ScreenShot0000.JPG
    Civ4ScreenShot0000.JPG
    136.9 KB · Views: 42
Alright...got a new, totally exciting problem...we can't play Somnium in any version of MNAI-U after 2.7.2. Somnium works in 2.7.2 (and of course in MNAI 2.7.1 which we always used to play in), but in 2.8, 2.8.1 and 2.9 Somnium will not load up at all beyond the screen to choose an opponent. After you attempt to play with AI or against a human in MP the Somnium button and Shift+F7 cease working as well in 2.8 and later. I don't understand what changed to make Somnium not work, but we've never been able to get it to work. I've tried looking at CvEventManager, but can't figure out what changed with diff's to see what is going on with the python. There are no errors, it's just nothing seems to be happening.
 
Top Bottom