OOS problems and how to fix them

Guys, I'm really lost in this discussion, it's way too technical for me. But I have a probably simple question; I have two following random logs:

Code:
163779	254	Global	AI Diplo Open Borders	20	2
163780	254	Global	AI Diplo Defensive Pact	80	18
163781	254	Global	AI Diplo Trade Tech	5	2
163782	254	Global	AI Diplo Trade War	10	3
163783	254	Global	AI Diplo End War	50	2
163784	254	Global	AI Diplo Trade War	10	4
163785	254	Global	AI Diplo Limited Borders	20	16
163786	254	Global	AI Diplo Limited Borders	20	3
163787	254	Global	AI Diplo Trade Contacts	7	4
163788	254	Global	AI Diplo Trade Workers	25	3
163789	254	Global	AI Diplo Trade Military Units	20	14
163790	254	Global	AI Diplo Trade Bonus	5	2
163791	254	Global	AI Diplo Trade Map	20	8
163792	254	Global	OOS TESTING 1	100	94
163793	254	Global	AI Diplo Declare War Trade	40	1
163794	254	Global	Rev: bribe city	100	57
163795	254	Global	AI Update Area Targets	8	5
163796	254	Global	AI Promote	15	14
163797	254	Global	AI Promote	15	0
163798	254	Global	AI Promote	15	0
163799	254	Global	AI Promote	15	13

vs

Code:
163779	254	Global	AI Diplo Demand Tribute	1000	121
163780	254	Global	AI Diplo Demand Tribute	1000	225
163781	254	Global	AI Diplo Demand Tribute	1000	509
163782	254	Global	AI Diplo Demand Tribute	1000	384
163783	254	Global	AI Diplo Open Borders	20	0
163784	254	Global	AI Diplo Defensive Pact	80	38
163785	254	Global	AI Diplo Trade Tech	5	4
163786	254	Global	AI Diplo Trade War	10	1
163787	254	Global	AI Diplo End War	50	32
163788	254	Global	AI Diplo Trade War	10	1
163789	254	Global	AI Diplo Limited Borders	20	14
163790	254	Global	AI Diplo Limited Borders	20	8
163791	254	Global	AI Diplo Trade Contacts	7	3
163792	254	Global	AI Diplo Trade Workers	25	23
163793	254	Global	AI Diplo Trade Military Units	20	0
163794	254	Global	AI Diplo Trade Bonus	5	2
163795	254	Global	AI Diplo Trade Map	20	13
163796	254	Global	OOS TESTING 1	100	97
163797	254	Global	AI Diplo Declare War Trade	40	2
163798	254	Global	Rev: bribe city	100	5
163799	254	Global	AI Update Area Targets	8	7

but the first line is pointing me at

Global AI Diplo Open Borders
and
Global AI Diplo Demand Tribute

So what string should I examine to hunt that OOS bug? The first, the second or both? Is there a way to know if that OOS has been generated by the first or the second string?
 
@45°38'N-13°47'E:

I'm assuming the logs were identical before the lines you have posted here.

Then the OOS happened after the last identical and before the two first lines you mentioned; the AI trade/diplo. code is a good place to start IMO.
Was the line before the ones posted also trade/diplo. related? anyhow, you should figure out where the code goes from the last identical line, somewhere the two computers split way in that code and thats where the real source of the OOS lie.

AIAndy probably have some more specific help to offer.
 
It is a weird OOS as in not something that can be caused by the traditional sync problems like different config inis or a false bAsync.
hmm... ok well I'll let you know if I keep seeing the issue come up after I revert the previous poor attempt to 'fix' the other OOS.


Wingrep is fine for that.
Nothing better or more specifically made to help one profile python then huh?


Seriously, random changes will only make things worse. At least think a bit about what the function you changed that in is. As the AI in AI_chooseUnitImmediate tells you, this is an AI functions and the AI states are synced. Some AI functions are also used from the UI but not this one which is why it has no bAsync itself.
Ok, that makes sense but I was not aware that was a 'usually true' statement that AI_ functions are almost always synced. I mean when you take, for example the reference you give me in the next paragraph being started by the same you can see why I'd be a little lost there. Most of the AI_ functions are programmed with such vague terminology that I have a hard time getting a clear concept of what the function is for in the first place without taking hours of code evaluation to come up with an answer and I'm really trying to find ways to fix these quickly because I REALLY hate working on them but hate having OOS errors popping up even worse. So yeah, I'm a little impatient (and I have another player sitting here tapping the toes at times getting frustrated I haven't got this wrapped up in 5 minutes.)

If you want an example of an async call to AI_bestUnit check CvDLLButtonPopup::launchProductionPopup which uses it for the recommendations what unit to build.
Ok, searching specifically for that function, finding it, then searching for the reference to AI_bestUnit I found it. Now...

WHY WASN'T THAT SHOWING WHEN I ASKED FOR ALL REFERENCES TO AI_bestUnit??????????????????? The list it gave me for that completely failed to show this particular call in the solution! I suppose I'm going to have to stop relying on that tool to be thorough and I'll have to start doing searches throughout the code for those calls instead. VERY frustrating! I just verified it for myself that it really doesn't show this function call when you look for all references to the function. THAT certainly doesn't help to make debugging these any easier though KNOWING that it's so unreliable does help to be more thorough.

So it looks like my next steps are to revert the last adjustment then start putting sensors out to figure out where other references to AI_bestUnit may exist that didn't display for me the first time through to figure out what function is causing an undeclared async state. THEN look for exactly what is causing the async state and set bAsync properly from there. And I may have to look through python to find it. *shudder*.

Is that pretty much your assessment of the progress so far?

@45°38'N-13°47'E:

I'm assuming the logs were identical before the lines you have posted here.

Then the OOS happened after the last identical and before the two first lines you mentioned; the AI trade/diplo. code is a good place to start IMO.
Was the line before the ones posted also trade/diplo. related? anyhow, you should figure out where the code goes from the last identical line, somewhere the two computers split way in that code and thats where the real source of the OOS lie.

AIAndy probably have some more specific help to offer.

Exactly and sometimes that question of 'how did it get from point A on both computers to point B on one computer and point C on the other?' is a hell of a maze. But it's like going through and finding where the algorythm river splits. It's tough. I'm starting to think I'm going to have to chart it out somehow. If anyone knows any good free flowchart programs let me know please.
 
So it looks like my next steps are to revert the last adjustment then start putting sensors out to figure out where other references to AI_bestUnit may exist that didn't display for me the first time through to figure out what function is causing an undeclared async state. THEN look for exactly what is causing the async state and set bAsync properly from there. And I may have to look through python to find it. *shudder*.

Is that pretty much your assessment of the progress so far?
That won't help. The desync that was in the logs shows the same random number queries on both sides but with different result. But a RNG like the one used here is supposed to yield the same sequence of numbers given the same seed. That means the seed on one side must actually have changed or, more likely, despite that it says Globals as name, it is actually a different random stream that Koshling set up for the multithreaded processing. But then why doesn't it show the name of the city but Globals in the log.
 
New OOS, first time I get it:

Code:
138726	311	Global	First Strike	1	0

vs

Code:
138726	311	Global	First Strike	2	0

which leads in cvunit.cpp (inside void CvUnit::setCombatUnit(CvUnit* pCombatUnit, bool bAttacking) ) to

Code:
setCombatFirstStrikes((pCombatUnit->immuneToFirstStrikes()) ? 0 : (firstStrikes() + GC.getGameINLINE().getSorenRandNum(chanceFirstStrikes() + 1, "First Strike")));

A couple of lines above there's an activePlayer call:

Code:
		m_bCombatFocus = (bAttacking && !(gDLL->getInterfaceIFace()->isFocusedWidget()) && ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) || ((pCombatUnit->getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && !(GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS)))));
		m_combatUnit = pCombatUnit->getIDInfo();

Can you make something out of this? Is that activePlayer call legal?
About 310/600 turns, middle Renaissance; no more OOS caused by ship movements until now in this game, so that's been definetely solved I'd say. :goodjob:
 
That won't help. The desync that was in the logs shows the same random number queries on both sides but with different result. But a RNG like the one used here is supposed to yield the same sequence of numbers given the same seed. That means the seed on one side must actually have changed or, more likely, despite that it says Globals as name, it is actually a different random stream that Koshling set up for the multithreaded processing. But then why doesn't it show the name of the city but Globals in the log.
I was wondering if one of the async randoms was called with Globals in the log. It's something I was meaning to look at.

Also... I'm first looking to solve the first OOS which wasn't a result of this - attempting to fix that did lead to this log report and if one of the async randoms mentioned called as Global then it was my erroneous fix that caused this problem in the first place. I could be way off here but I figure I should fix the first problem that I was trying to fix and do it properly before trying to tackle this one.

One thing I'll be working on for that is setting up the logging mechanism you and I were discussing. That MAY prove somewhat helpful for this problem too.

However, I've been suspecting multi-threading may have something to do with it as well.

45°38'N-13°47'E;13099265 said:
New OOS, first time I get it:

Code:
138726	311	Global	First Strike	1	0

vs

Code:
138726	311	Global	First Strike	2	0

which leads in cvunit.cpp (inside void CvUnit::setCombatUnit(CvUnit* pCombatUnit, bool bAttacking) ) to

Code:
setCombatFirstStrikes((pCombatUnit->immuneToFirstStrikes()) ? 0 : (firstStrikes() + GC.getGameINLINE().getSorenRandNum(chanceFirstStrikes() + 1, "First Strike")));

A couple of lines above there's an activePlayer call:

Code:
		m_bCombatFocus = (bAttacking && !(gDLL->getInterfaceIFace()->isFocusedWidget()) && ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) || ((pCombatUnit->getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) && !(GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS)))));
		m_combatUnit = pCombatUnit->getIDInfo();

Can you make something out of this? Is that activePlayer call legal?
About 310/600 turns, middle Renaissance; no more OOS caused by ship movements until now in this game, so that's been definetely solved I'd say. :goodjob:
The activePlayer call should be ok given that it's a graphic issue However, the bracketing may be messed up here to get the isMPOption call to work properly - it may not be protecting against the activePlayer call which is what it's intended to do I'm sure. Perhaps it should be:
Code:
		m_bCombatFocus = ((bAttacking && !(gDLL->getInterfaceIFace()->isFocusedWidget()) && ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) || ((pCombatUnit->getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())) && !(GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS)))));
		m_combatUnit = pCombatUnit->getIDInfo();
m_bCombatFocus = ((bAttacking && !(gDLL->getInterfaceIFace()->isFocusedWidget()) && ((getOwnerINLINE() == GC.getGameINLINE().getActivePlayer()) || ((pCombatUnit->getOwnerINLINE() == GC.getGameINLINE().getActivePlayer())) && !(GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS)))));
m_combatUnit = pCombatUnit->getIDInfo();

I'm thinking it isMPOption was supposed to protect against all those getActivePlayer calls and it really doesn't for the first one. On the other hand, m_bCombatFocus is pretty much an async operation anyhow - it involves the screen zoomin during combat I believe. So this shouldn't be problematic in the first place.

I've noticed an OOS that SEEMS to deal with first strike but it always seems to be going out of sync there AFTER previous randoms have already gone out of sync earlier so I'm thinking it was already out of sync by the time the combat began. But if your logs show it coming up as the first instance of an out of sync random then we do have to look from the spot you gave. Unfortunately IIRC it's the first call in a combat is it not? That's not a good thing because it could mean a lot of other things happening before that point that threw us out of sync. This could mean another problem in attack evaluations perhaps? Something to really hunt down I think.

However, the adjustment above MAY work (and also may not do anything but perhaps make it so the battle never zooms in when playing with simultaneous turns.)

Wait... one very interesting thing about that problem is that one of the first strike possible results is different from the other. The difference is in the possibility rather than the roll itself which is usually what differs when OOS errors begin. Here, it's a different sided dice being rolled on both sides so for one system the unit should have a total possible FS of 3 and on another, 2. A promotion maybe? Perhaps the battlefield promotion mechanism might've caused that? I've often wondered if there was a problem in there somewhere.
 
I'm seeing an OOS that likely takes place here:
bool CvUnitAI::AI_guardCity(bool bLeave, bool bSearch, int iMaxPath)

And since no randoms cause this and only archers and healers positions vary based on whether they chose to defend a city or not (presumably considering the difference on systems was whether they remained in a city or not) and the # of groups differed as well, I presume the problem lies here:
Code:
				if ( getGroup()->AI_hasBeneficialPropertyEffectForCity(pCity) )
				{
					//	We have at least one unit that can help the ciy's property control (aka crime usually)
					//	Split ou he best such unit and have it defend in the city
					CvSelectionGroup* pOldGroup = getGroup();
					CvUnit* pEjectedUnit = getGroup()->AI_ejectBestPropertyManipulator(pCity);
					
					FAssert(pEjectedUnit != NULL);
					pEjectedUnit->AI_setUnitAIType(UNITAI_CITY_DEFENSE);

					if ( atPlot(pCity->plot()) )
					{
						//	Mark the ejected unit as part of the city garrison
						pEjectedUnit->getGroup()->AI_setAsGarrison(pCity);
						pEjectedUnit->getGroup()->pushMission(MISSION_SKIP, -1, -1, 0, false, false, MISSIONAI_GUARD_CITY, NULL);
						return (pEjectedUnit->getGroup() == pOldGroup || pEjectedUnit == this);
					}
					else if ( pEjectedUnit->generatePath(pCity->plot(), 0, true) )
					{
						//	Mark the ejected unit as part of the city garrison
						pEjectedUnit->getGroup()->AI_setAsGarrison(pCity);
						pEjectedUnit->getGroup()->pushMission(MISSION_MOVE_TO, pCity->getX_INLINE(), pCity->getY_INLINE(), 0, false, false, MISSIONAI_GUARD_CITY, NULL);
						return (pEjectedUnit->getGroup() == pOldGroup || pEjectedUnit == this);
					}
					else
					{
						//	If we can't move after all regroup and continue regular defensive processing
						pEjectedUnit->joinGroup(pOldGroup);
					}
				}
specifically I suspect the boolean that starts this whole if section.

Unfortunately, I have no idea what down that path could be causing a difference of processing!

Anyone have any ideas?

EDIT: Commenting out that section proved unsuccessful in repairing the problem so that must not be 'it'. I'm going to try building some targeted logging such as AIAndy and I discussed previously.
There also seems to be an odd random OOS showing up at the AI choose missionary check which I should also setup some logging around so as to track down. There really shouldn't be an async state for that check ever I don't think...
 
Oh well, around turn 360/600 we started to have serious OOS problems for 3-4 turns in a row. What's strange is that Random Logs are identical, while OOSLogs show usually differences in Next Map Rand Value and Next Soren Rand Value. I'm not sure if this is connected but it all started after I've renamed one of my units (and changed the name back to original). This was the last time I've got different random logs:

Code:
39403	362	Global	Rev: Naming	100	58
39404	362	Global	AI Best Unit	50	34
39405	362	Global	AI Unit Birthmark	10000	2329
39406	362	Global	AI Unit Birthmark	10000	273
39407	362	Global	AI Unit Birthmark	10000	4395
39408	362	Global	AI Unit Birthmark	10000	3916

vs

Code:
39403	362	Global	AI Best Unit	50	29
39404	362	Global	AI Unit Birthmark	10000	6813
39405	362	Global	AI Unit Birthmark	10000	2329
39406	362	Global	AI Unit Birthmark	10000	273
39407	362	Global	AI Unit Birthmark	10000	4395
39408	362	Global	Rev: Culture	12	4

What I find strange is that there's no "Rev: Naming" string anywhere in the code. So where is this coming from? I've scanned both AND and C2C source files and I don't have a clue. Anyway, after this OOS we've been able to play a couple of turns without OOS, then we started getting OOS every turn; what's made me suspicious in this case as a possible OOS cause is that we exchanged a couple of units via diplomacy. I gifted her some cannons and Man'O'War and she gifted them back to me a few turns later. We got OOS errors every turn since we exchanged our units. But then again, I'm not sure where in the code I have to look for that part of diplomacy.

Edit: well, I've found that "Rev: Naming" source, it's in DynamicCivNames.py. So I suppose the problem was not renaming one of my units but probably something related to a Civ's name, IIRC that turn a new civ was spawned as a vassal of another civ after a city rebellion.

Edit2: Maybe the problem might be in AI Best Unit? I've seen this part of the code inside CvCityAI.cpp (in UnitTypes CvCityAI::AI_bestUnit):

Code:
		if (bValid && (bNoWeighting || aiUnitAIVal[iI] > 0))
		{
			if ( !bNoWeighting )
			{
				if ( !bNoRand )
				{
					if (bAsync)
					{
						aiUnitAIVal[iI] += GC.getASyncRand().get(iMilitaryWeight, "AI Best UnitAI ASYNC");
					}
					else
					{
						aiUnitAIVal[iI] += getCitySorenRandNum(iMilitaryWeight, "AI Best UnitAI");
					}
				}

which might cause different random values, if I'm not mistaken.
 
I'm doing everything I can to track down the AI_bestUnit problem. So far it's been tracked down to a best going theory that it has to do with uninitialized variables in the Contract Broker system. Some crazy stuff. Once I have a firm concept of exactly what the fix is (or fixes are) I'll let you know.

The naming issue seems different. I'd be surprised if you would find a random within the async process of you renaming a unit (though if it was it'd surely cause an OOS.) I was going to say it probably would be located in python somewhere and you already confirmed that. That does seem to be the root of THIS OOS you're talking about. And I suppose it's probably a random when setting up a new civ as you said... but in naming I'm wondering why we'd need a random at all. That's kinda funny to me. And why it would be called in an async context. Makes me wonder if that's what happens when there are multiple valid terms for the nation - and if it's only supposed to influence the player who's looking at the nation's name. I saw some odd swapping of a civ's naming every round and maybe that has something to do with it. Might be the easiest method would be to replace the random with a way to establish a priority. I mean... do we really care what the name of the civ is so long as it's something rational and recognizable?

Anyhow, I'd look very carefully at the context in which that random is called. Being called async, I'm not sure how the random should be structured in python to get around it being problematic (as a global). Something LIKE the one in AI_bestUnit (getASyncRand) would probably work if that can be called in py and you can be sure that the random there is always being called in async context. It would not work if its called in sync context at times.
 
What if we place in that part of AIBestUnit a && !isNetworkMultiplayer in that bAsync and another || isNetworkMultiplayer after that else condition so that async is only used when not in MP and sync is only used in MP? Would that help or would it make things worst?

Edit: on second thought that could cause problems, I think. Since AI_BestUnit is also called in asynchronous context as AIAndy pointed out, we should allow it even if the game is Multiplayer. But maybe we could make sure it's not used by AI with a && isHuman check in that bAsync true case. That is, if there are not other asynchronous uses beside that pointed out by AIAndy. At least I suppose so.
 
More on the issue: from the OOS Logs, it seems like the problem was AI Best Unit while the code I provided two posts above is from UnitTypes CvCityAI::AI_bestUnit which relays on AI Best Unit AI. On the contrary, the code is very similar but those OOS logs about AI Best Unit refer to a random call inside UnitTypes CvCityAI::AI_bestUnitAIAI and not UnitTypes CvCityAI::AI_bestUnit. Is this normal? I mean, inside AI_bestUnit there's a call for AI Best Unit AI while inside AI_bestUnitAI there's a call for AI Best Unit. Anyway, the isHuman check I was proposing should go inside AI code.
Hell, it's really a nightmare hunting these OOS problems.
 
Wait... one very interesting thing about that problem is that one of the first strike possible results is different from the other. The difference is in the possibility rather than the roll itself which is usually what differs when OOS errors begin. Here, it's a different sided dice being rolled on both sides so for one system the unit should have a total possible FS of 3 and on another, 2. A promotion maybe? Perhaps the battlefield promotion mechanism might've caused that? I've often wondered if there was a problem in there somewhere.

I've got another of these First Strike OOS; but this time neither of us human players were at war, if this can help. And problem was always in the possibility rather than in the roll itself. Anyway we're at turn 403/600 halfway in the industrial era in AND. Not too bad. :)
 
45°38'N-13°47'E;13112882 said:
I've got another of these First Strike OOS; but this time neither of us human players were at war, if this can help. And problem was always in the possibility rather than in the roll itself. Anyway we're at turn 403/600 halfway in the industrial era in AND. Not too bad. :)

I've yet to really encounter this one in such a way I can track it.

So - an update on my progress here so far:

I've been finding that it's very possible that an undefined variable put to use as a pointer in a function call's parameters, aka:
int i;
void CvWhatever::doesSomething(int &i)

may well be a villainous creature for OOS error generation.

I've been deeply tracking the processing with new logging points to pinpoint exactly where out of sync errors are originating. Along the way, little tweaks like adding = 0; to int i; in the example above (particularly in the regions where the tracking has led me so far) have been appearing to clear up the issue. I still haven't run enough checks to know for sure yet but the only other answers as to how it may be now going so long without OOS are:
1) What SEEMED to be a constant and repetitive OOS error previously was not so reliably taking place as it appeared and I just had an unusual 100 round run when I wasn't able to go more than a round previously.
or
2) The tracking itself is somehow incidentally 'fixing' the issue. Doubt this but it seems like every time I turn it off I get an OOS to pop back up (though it may again be a new one.)

Anyhow - will probably be generating a distinctly different OOS debugging dll out of this project. The tracking can't be run for too long or you'll get an overwhelmingly large log. And it really get's rediculously zoomed in to find the issue - AIAndy would probably have to laugh ;) But it IS super helpful!

Once all the fixes are found for certainty I'll be extruding those fixes to the core dll for an SVN update.
 
I've yet to really encounter this one in such a way I can track it.

So - an update on my progress here so far:

I've been finding that it's very possible that an undefined variable put to use as a pointer in a function call's parameters, aka:
int i;
void CvWhatever::doesSomething(int &i)

may well be a villainous creature for OOS error generation.

I've been deeply tracking the processing with new logging points to pinpoint exactly where out of sync errors are originating. Along the way, little tweaks like adding = 0; to int i; in the example above (particularly in the regions where the tracking has led me so far) have been appearing to clear up the issue. I still haven't run enough checks to know for sure yet but the only other answers as to how it may be now going so long without OOS are:
1) What SEEMED to be a constant and repetitive OOS error previously was not so reliably taking place as it appeared and I just had an unusual 100 round run when I wasn't able to go more than a round previously.
or
2) The tracking itself is somehow incidentally 'fixing' the issue. Doubt this but it seems like every time I turn it off I get an OOS to pop back up (though it may again be a new one.)

Anyhow - will probably be generating a distinctly different OOS debugging dll out of this project. The tracking can't be run for too long or you'll get an overwhelmingly large log. And it really get's rediculously zoomed in to find the issue - AIAndy would probably have to laugh ;) But it IS super helpful!

Once all the fixes are found for certainty I'll be extruding those fixes to the core dll for an SVN update.

If you think I can help by testing those possible fixes in AND, just let me know. If you upload your files somewhere I can use winmerge to import code from your files into my testing source files. Otherwise I'll wait for you to upload a "final" version. Just let me know if I can help by testing, as there are not so many people testing multiplayer. :)
 
ok... I will certainly share it all with you. The tracking mechanism leaves a LOT of needless lines of code all about that I should clean up if I'm going to share.

Also... last night's tests sure lended some evidence that perhaps the tracking itself IS the fix somehow. However, I've taken some actions to change some methods and we'll see if I can get some better results that way.
 
Ok, a few days of testing and we can't seem to find another OOS error with or without the tracking mechanism I setup being in place. So I'm left to believe that among the following are very real fixes to OOS errors. The tracking mechanism led me to these as my only theories to address the locations where things were going out of sync and apparently they've worked. So, here's what I've come up with... I'm not sure how much of this is shared between C2C and AND:
OOS Fixes:
1)
Code:
 bool CvUnitAI::processContracts(int iMinPriority)
{
	PROFILE_FUNC();

	//	Currently not implemented for domains other than land
	if ( getDomainType() != DOMAIN_LAND )
	{
		return false;
	}

	bool bContractAlreadyEstablished = (m_contractsLastEstablishedTurn == GC.getGameINLINE().getGameTurn());

	//	Have we advertised ourselves as available yet?
	if ( !bContractAlreadyEstablished )
	{
		GET_PLAYER(getOwnerINLINE()).getContractBroker().lookingForWork(this, iMinPriority);

		m_contractsLastEstablishedTurn = GC.getGameINLINE().getGameTurn();
		m_contractualState = CONTRACTUAL_STATE_AWAITING_WORK;
		if( gUnitLogLevel >= 3 )
		{
			logBBAI("    Unit %S (%d) for player %d (%S) at (%d,%d) advertising for work\n",
					getUnitInfo().getDescription(),
					getID(),
					getOwner(),
					GET_PLAYER(getOwner()).getCivilizationDescription(0),
					plot()->getX_INLINE(),
					plot()->getY_INLINE());
		}
	}
	//TB OOS fix: Giving these a default seems to repair numerous OOS errors
	int		iAtX = 0;
	int		iAtY = 0;
Was
Code:
 	int		iAtX;
	int		iAtY;

2)
Code:
 void CvUnit::init(int iID, UnitTypes eUnit, UnitAITypes eUnitAI, PlayerTypes eOwner, int iX, int iY, DirectionTypes eFacingDirection, int iBirthmark)
{
	CvWString szBuffer;
	int iUnitName;
	int iI;

	FAssert(NO_UNIT != eUnit);

	//	If the current viewport is not yet initialized center it on the first unit created for the active player
	if ( GC.getGameINLINE().getActivePlayer() == eOwner && GC.getCurrentViewport()->getState() == VIEWPORT_MODE_UNINITIALIZED && UNIT_BIRTHMARK_TEMP_UNIT != iBirthmark )
	{
		GC.getCurrentViewport()->setOffsetToShow(iX, iY);
	}

	//--------------------------------
	// Init saved data
	reset(iID, eUnit, eOwner);

	if ( eOwner != NO_PLAYER && eUnitAI == UNITAI_SUBDUED_ANIMAL)
	{
		GET_PLAYER(eOwner).NoteAnimalSubdued();
	}
	//	Koshling -  moved this earlier to get unitAI set up so that
	//	constraint checking on the unitAI can work more uniformly
	AI_init(eUnitAI, iBirthmark);

	if(eFacingDirection == NO_DIRECTION)
		m_eFacingDirection = DIRECTION_SOUTH;
	else
		m_eFacingDirection = eFacingDirection;

	//--------------------------------
	// Init containers

	//--------------------------------
	// Init pre-setup() data
	setXY(iX, iY, false);

	if ( getGroup() == NULL )
	{
	//TB OOS fix - POSSIBLE that this represents a fix but I consider it a longshot since they should really mean the same thing (-1)
		::MessageBox(NULL,
					getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID after set position in init\n" : "Unit with no group after set position in init\n",
					"CvGameCoreDLL Diagnostics",
					MB_OK);
	}
The difference... this last section WAS:
Code:
 	if ( getGroup() == NULL )
	{
		::MessageBox(NULL,
					getGroupID() == -1 ? "Unit with NULL group ID after set position in init\n" : "Unit with no group after set position in init\n",
					"CvGameCoreDLL Diagnostics",
					MB_OK);
	}

3)And:
Code:
 void CvPlayer::updateGroupCycle(CvUnit* pUnit, bool bFarMove)
{
	PROFILE_FUNC();

	CLLNode<IDInfo>* pUnitNode;
	CLLNode<int>* pReinsertSearchStart;
	CLLNode<int>* pSelectionGroupNode;
	CLLNode<int>* pBestSelectionGroupNode;
	CvSelectionGroup* pLoopSelectionGroup;
	CvUnit* pHeadUnit;
	CvUnit* pBeforeUnit;
	CvUnit* pAfterUnit;
	CvUnit* pLoopUnit;
	CvPlot* pPlot;
	int iValue;
	int iBestValue;

	if (!(pUnit->onMap()))
	{
		return;
	}

	if ( isTempUnit(pUnit) )
	{
		return;
	}

	EnterCriticalSection(&c_GroupCycleSection);

	FAssertMsg(pUnit->getGroup() != NULL, "Unit->getGroup() is not assigned a valid value");

	pReinsertSearchStart = removeGroupCycle(pUnit->getGroupID());

	pPlot = pUnit->plot();

	pBeforeUnit = NULL;
	pAfterUnit = NULL;

//TB OOS Fix: Another longshot that did seem to help
	if ( pUnit->getGroup() == NULL )
	{
			::MessageBox(NULL,
						pUnit->getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID on call to CvPlayer::updateGroupCycle\n" : "Unit with no group on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
	}
Was:
Code:
 	if ( pUnit->getGroup() == NULL )
	{
			::MessageBox(NULL,
						pUnit->getGroupID() == -1 ? "Unit with NULL group ID on call to CvPlayer::updateGroupCycle\n" : "Unit with no group on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
	}

4)And:
Code:
 		if ( pLoopUnit->getGroup() == NULL )
		{
			::MessageBox(NULL,
						pLoopUnit->getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID found in stack on call to CvPlayer::updateGroupCycle\n" : "Unit with no group found in stack on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
		}
Was:
Code:
 void CvPlayer::updateGroupCycle(CvUnit* pUnit, bool bFarMove)
{
	PROFILE_FUNC();

	CLLNode<IDInfo>* pUnitNode;
	CLLNode<int>* pReinsertSearchStart;
	CLLNode<int>* pSelectionGroupNode;
	CLLNode<int>* pBestSelectionGroupNode;
	CvSelectionGroup* pLoopSelectionGroup;
	CvUnit* pHeadUnit;
	CvUnit* pBeforeUnit;
	CvUnit* pAfterUnit;
	CvUnit* pLoopUnit;
	CvPlot* pPlot;
	int iValue;
	int iBestValue;

	if (!(pUnit->onMap()))
	{
		return;
	}

	if ( isTempUnit(pUnit) )
	{
		return;
	}

	EnterCriticalSection(&c_GroupCycleSection);

	FAssertMsg(pUnit->getGroup() != NULL, "Unit->getGroup() is not assigned a valid value");

	pReinsertSearchStart = removeGroupCycle(pUnit->getGroupID());

	pPlot = pUnit->plot();

	pBeforeUnit = NULL;
	pAfterUnit = NULL;

//TB OOS Fix: Another longshot that did seem to help
	if ( pUnit->getGroup() == NULL )
	{
			::MessageBox(NULL,
						pUnit->getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID on call to CvPlayer::updateGroupCycle\n" : "Unit with no group on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
	}
	pUnitNode = pPlot->headUnitNode();

	while (pUnitNode != NULL)
	{
		pLoopUnit = ::getUnit(pUnitNode->m_data);
		pUnitNode = pPlot->nextUnitNode(pUnitNode);

		if ( pLoopUnit->getGroup() == NULL )
		{
			::MessageBox(NULL,
						pLoopUnit->getGroupID() == -1 ? "Unit with NULL group ID found in stack on call to CvPlayer::updateGroupCycle\n" : "Unit with no group found in stack on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
		}

5)
Code:
 	//TB OOS Debug
	if (!pSelectionGroup->AI_isControlled() || GET_PLAYER(pSelectionGroup->getHeadOwner()).isHuman())
	{
		gDLL->getFAStarIFace()->ForceReset(finder);
		CvSelectionGroup::setGroupToCacheFor(pSelectionGroup);
		return pSelectionGroup->generatePath(pFromPlot, pToPlot, gDLL->getFAStarIFace()->GetInfo(finder), false, NULL, MAX_INT);
	}
Was:
Code:
 int pathDestValid(int iToX, int iToY, const void* pointer, FAStar* finder)
{
	PROFILE_FUNC();

	CvSelectionGroup* pSelectionGroup;
	CvPlot* pToPlot;
	CvPlot* pFromPlot;

	pToPlot = GC.getMapExternal().plot(iToX, iToY);
	FAssert(pToPlot != NULL);

	pFromPlot = GC.getMapExternal().plot(gDLL->getFAStarIFace()->GetStartX(finder), gDLL->getFAStarIFace()->GetStartY(finder));
	FAssert(pFromPlot != NULL);

	//	Safety valve since minidumps have shown that this (unknown how) can still occassionally
	//	happen (attempt to generate a path that starts or ends off the viewport)
	if ( pToPlot == NULL || pFromPlot == NULL )
	{
		return false;
	}

	pSelectionGroup = ((CvSelectionGroup *)pointer);

	if (pSelectionGroup->atPlot(pToPlot))
	{
		return TRUE;
	}

	if (pSelectionGroup->getDomainType() == DOMAIN_IMMOBILE)
	{
		return FALSE;
	}

	//OutputDebugString(CvString::format("PathDestValid (%d,%d)\n", iToX, iToY).c_str());
	if (!pSelectionGroup->AI_isControlled() || pSelectionGroup->getHeadOwner() == GC.getGameINLINE().getActivePlayer())

6)
Code:
 	if ( pUnit->canDefend() )
	{
		unitDetails.iDefensiveValue = iUnitStr;
	}
	//TB OOS Debug: Almost certainly a solid fix here
	else
	{
		unitDetails.iDefensiveValue = 0;
	}
	if ( pUnit->canAttack() )
	{
		unitDetails.iOffensiveValue = iUnitStr;
	}
	else
	{
		unitDetails.iOffensiveValue = 0;
	}
Was:
Code:
 void	CvContractBroker::lookingForWork(CvUnit* pUnit, int iMinPriority)
{
	PROFILE_FUNC();

	advertisingUnit	unitDetails;
	int				iUnitStr = GC.getGameINLINE().AI_combatValue(pUnit->getUnitType());

	unitDetails.bIsWorker = (pUnit->AI_getUnitAIType() == UNITAI_WORKER);

	//	Combat values are just the crude value of the unit type for now - should add in promotions
	//	here for sure
	if ( pUnit->canDefend() )
	{
		unitDetails.iDefensiveValue = iUnitStr;
	}
	if ( pUnit->canAttack() )
	{
		unitDetails.iOffensiveValue = iUnitStr;
	}

AND at the bottom of same function there were some unnecessary and possibly somehow problematic brackets. Removed those so now shows:
Code:
//TB OOS fix: another one that only MIGHT have had an effect to repair things at all
	MEMORY_TRACK_EXEMPT();

	m_advertisingUnits.push_back(unitDetails);
rather than:
Code:
 	{
		MEMORY_TRACK_EXEMPT();

		m_advertisingUnits.push_back(unitDetails);
	}

7)
Code:
 			//TB OOS Debug: from here we were not given a beginning definition
			int iBestCityTenderKey = 0;
			int iValue = 0;
			UnitTypes eUnit = NO_UNIT;
			//to here
Was:
Code:
 void CvContractBroker::finalizeTenderContracts(void)
{
	PROFILE_FUNC();

	std::map<int,int>	tenderAllocations;

	CvPathGenerator::EnableMaxPerformance(true);

	//	No need to lock here - this is always run in a single threaded context

	for(int iI = 0; iI < (int)m_workRequests.size(); iI++)
	{
		if ( !m_workRequests[iI].bFulfilled )
		{
			int iBestValue = 0;
			int iBestCityTenderKey;
			int iValue;
			UnitTypes eUnit;

And in the same function later on down the line:
Code:
 //TB OOS Debug: Undefined variable being called as a pointer in a later function call
								UnitAITypes eAIType = NO_UNITAI;
Was just UnitAITypes eAIType; which was apparently a problem considering how it cleared up OOS errors taking place to change it.

8)
Code:
	//TB OOS Debug: Undefined variable causing an OOS
	int iUnitValue = 0;
Was:
Code:
 void CvCityAI::AI_chooseProduction()
{
	PROFILE_FUNC();

	CvArea* pWaterArea;
	bool bWasFoodProduction;
	bool bLandWar;
	bool bAssault;
	bool bDefenseWar;
	bool bPrimaryArea;
	bool bFinancialTrouble;
	bool bDanger;
	bool bChooseUnit;
	bool bInhibitUnits = false;
	int iProductionRank;
	int iCulturePressure;
	m_bRequestedBuilding = false;	
	m_bRequestedUnit = false;	

	CvPlayerAI& kPlayer = GET_PLAYER(getOwnerINLINE());

	if (kPlayer.isAnarchy())
	{
		return;
	}

	bDanger = AI_isDanger();
	
	m_iBuildPriority = CITY_BUILD_PRIORITY_CEILING;
	m_iTempBuildPriority = CITY_BUILD_PRIORITY_CEILING;

	// only clear the dirty bit if we actually do a check, multiple items might be queued
	AI_setChooseProductionDirty(false);
/************************************************************************************************/
/* Afforess	                  Start		 04/22/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
	if(GC.getUSE_AI_CHOOSE_PRODUCTION_CALLBACK())
	{
		PYTHON_ACCESS_LOCK_SCOPE

		// allow python to handle it
		CyCity* pyCity = new CyCity(this);
		CyArgsList argsList;
		argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity));	// pass in city class
		long lResult=0;
		PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "AI_chooseProduction", argsList.makeFunctionArgs(), &lResult);
		delete pyCity;	// python fxn must not hold on to this pointer
		if (lResult == 1)
		{
			return;
		}
	}

	if (isHuman() && isProductionAutomated())
	{
		if (!GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_MODDER_3))
		{
			AI_buildGovernorChooseProduction();
			return;
		}
		else
		{
			for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
			{
				if (AI_isEmphasizeCommerce((CommerceTypes)iI))
				{
					bInhibitUnits = true;
				}
			}
			for (int iI = 0; iI < NUM_YIELD_TYPES; iI++)
			{
				if (AI_isEmphasizeYield((YieldTypes)iI))
				{
					bInhibitUnits = true;
				}
			}
		}
	}

/************************************************************************************************/
/* Afforess	                     END                                                            */
/************************************************************************************************/
	
	CvArea* pArea = area();
	pWaterArea = waterArea(true);
	bool bMaybeWaterArea = false;
	bool bWaterDanger = false;
    
	if (pWaterArea != NULL)
	{
		bMaybeWaterArea = true;
		if (!GET_TEAM(getTeam()).AI_isWaterAreaRelevant(pWaterArea))
		{
			pWaterArea = NULL;
		}

		bWaterDanger = kPlayer.AI_getWaterDanger(plot(), 4) > 0;
	}

	bWasFoodProduction = isFoodProduction();
	//AIAndy: Not used here at the moment
	//bool bHasMetHuman = GET_TEAM(getTeam()).hasMetHuman();
	bool bMassing = (pArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT_MASSING);
	bLandWar = ((pArea->getAreaAIType(getTeam()) == AREAAI_OFFENSIVE) || (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE) || bMassing);
	bDefenseWar = (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE);
	bool bAssaultAssist = (pArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT_ASSIST);
	bAssault = bAssaultAssist || (pArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT) || bMassing;
	bPrimaryArea = kPlayer.AI_isPrimaryArea(pArea);
	bFinancialTrouble = kPlayer.AI_isFinancialTrouble();
	iCulturePressure = AI_calculateCulturePressure();
	int iNumCitiesInArea = pArea->getCitiesPerPlayer(getOwnerINLINE());
	bool bImportantCity = false; //be very careful about setting this.
	bool bBigCultureCity = false;
	int iCultureRateRank = findCommerceRateRank(COMMERCE_CULTURE);
    int iCulturalVictoryNumCultureCities = GC.getGameINLINE().culturalVictoryNumCultureCities();

	int iWarSuccessRatio = GET_TEAM(getTeam()).AI_getWarSuccessCapitulationRatio();
	int iEnemyPowerPerc = GET_TEAM(getTeam()).AI_getEnemyPowerPercent(true);
	int iWarTroubleThreshold = 0;

	//	Are our best worker units one-shot usage (mostly)?
	bool bBestWorkerIsOneShot = false;
	int iUnitValue;

Also under void CvCityAI::AI_chooseProduction():
Find the line:
Code:
 	int iEcononmyFlagsThreasholdWeighting;
and delete it. Then go down a bit to:
Code:
 	iEcononmyFlagsThreasholdWeighting = (100*iEconomyFlagBits)/8;
and just add int before it to declare it right there. This keeps the declaration of iEcononmyFlagsThreasholdWeighting at the point of declaration. I don't THINK this one really fixes anything but it does make it a little OOS safer potentially.
 
Ok, a few days of testing and we can't seem to find another OOS error with or without the tracking mechanism I setup being in place. So I'm left to believe that among the following are very real fixes to OOS errors. The tracking mechanism led me to these as my only theories to address the locations where things were going out of sync and apparently they've worked. So, here's what I've come up with... I'm not sure how much of this is shared between C2C and AND:
OOS Fixes:
1)
Code:
 bool CvUnitAI::processContracts(int iMinPriority)
{
	PROFILE_FUNC();

	//	Currently not implemented for domains other than land
	if ( getDomainType() != DOMAIN_LAND )
	{
		return false;
	}

	bool bContractAlreadyEstablished = (m_contractsLastEstablishedTurn == GC.getGameINLINE().getGameTurn());

	//	Have we advertised ourselves as available yet?
	if ( !bContractAlreadyEstablished )
	{
		GET_PLAYER(getOwnerINLINE()).getContractBroker().lookingForWork(this, iMinPriority);

		m_contractsLastEstablishedTurn = GC.getGameINLINE().getGameTurn();
		m_contractualState = CONTRACTUAL_STATE_AWAITING_WORK;
		if( gUnitLogLevel >= 3 )
		{
			logBBAI("    Unit %S (%d) for player %d (%S) at (%d,%d) advertising for work\n",
					getUnitInfo().getDescription(),
					getID(),
					getOwner(),
					GET_PLAYER(getOwner()).getCivilizationDescription(0),
					plot()->getX_INLINE(),
					plot()->getY_INLINE());
		}
	}
	//TB OOS fix: Giving these a default seems to repair numerous OOS errors
	int		iAtX = 0;
	int		iAtY = 0;
Was
Code:
 	int		iAtX;
	int		iAtY;

2)
Code:
 void CvUnit::init(int iID, UnitTypes eUnit, UnitAITypes eUnitAI, PlayerTypes eOwner, int iX, int iY, DirectionTypes eFacingDirection, int iBirthmark)
{
	CvWString szBuffer;
	int iUnitName;
	int iI;

	FAssert(NO_UNIT != eUnit);

	//	If the current viewport is not yet initialized center it on the first unit created for the active player
	if ( GC.getGameINLINE().getActivePlayer() == eOwner && GC.getCurrentViewport()->getState() == VIEWPORT_MODE_UNINITIALIZED && UNIT_BIRTHMARK_TEMP_UNIT != iBirthmark )
	{
		GC.getCurrentViewport()->setOffsetToShow(iX, iY);
	}

	//--------------------------------
	// Init saved data
	reset(iID, eUnit, eOwner);

	if ( eOwner != NO_PLAYER && eUnitAI == UNITAI_SUBDUED_ANIMAL)
	{
		GET_PLAYER(eOwner).NoteAnimalSubdued();
	}
	//	Koshling -  moved this earlier to get unitAI set up so that
	//	constraint checking on the unitAI can work more uniformly
	AI_init(eUnitAI, iBirthmark);

	if(eFacingDirection == NO_DIRECTION)
		m_eFacingDirection = DIRECTION_SOUTH;
	else
		m_eFacingDirection = eFacingDirection;

	//--------------------------------
	// Init containers

	//--------------------------------
	// Init pre-setup() data
	setXY(iX, iY, false);

	if ( getGroup() == NULL )
	{
	//TB OOS fix - POSSIBLE that this represents a fix but I consider it a longshot since they should really mean the same thing (-1)
		::MessageBox(NULL,
					getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID after set position in init\n" : "Unit with no group after set position in init\n",
					"CvGameCoreDLL Diagnostics",
					MB_OK);
	}
The difference... this last section WAS:
Code:
 	if ( getGroup() == NULL )
	{
		::MessageBox(NULL,
					getGroupID() == -1 ? "Unit with NULL group ID after set position in init\n" : "Unit with no group after set position in init\n",
					"CvGameCoreDLL Diagnostics",
					MB_OK);
	}

3)And:
Code:
 void CvPlayer::updateGroupCycle(CvUnit* pUnit, bool bFarMove)
{
	PROFILE_FUNC();

	CLLNode<IDInfo>* pUnitNode;
	CLLNode<int>* pReinsertSearchStart;
	CLLNode<int>* pSelectionGroupNode;
	CLLNode<int>* pBestSelectionGroupNode;
	CvSelectionGroup* pLoopSelectionGroup;
	CvUnit* pHeadUnit;
	CvUnit* pBeforeUnit;
	CvUnit* pAfterUnit;
	CvUnit* pLoopUnit;
	CvPlot* pPlot;
	int iValue;
	int iBestValue;

	if (!(pUnit->onMap()))
	{
		return;
	}

	if ( isTempUnit(pUnit) )
	{
		return;
	}

	EnterCriticalSection(&c_GroupCycleSection);

	FAssertMsg(pUnit->getGroup() != NULL, "Unit->getGroup() is not assigned a valid value");

	pReinsertSearchStart = removeGroupCycle(pUnit->getGroupID());

	pPlot = pUnit->plot();

	pBeforeUnit = NULL;
	pAfterUnit = NULL;

//TB OOS Fix: Another longshot that did seem to help
	if ( pUnit->getGroup() == NULL )
	{
			::MessageBox(NULL,
						pUnit->getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID on call to CvPlayer::updateGroupCycle\n" : "Unit with no group on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
	}
Was:
Code:
 	if ( pUnit->getGroup() == NULL )
	{
			::MessageBox(NULL,
						pUnit->getGroupID() == -1 ? "Unit with NULL group ID on call to CvPlayer::updateGroupCycle\n" : "Unit with no group on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
	}

4)And:
Code:
 		if ( pLoopUnit->getGroup() == NULL )
		{
			::MessageBox(NULL,
						pLoopUnit->getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID found in stack on call to CvPlayer::updateGroupCycle\n" : "Unit with no group found in stack on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
		}
Was:
Code:
 void CvPlayer::updateGroupCycle(CvUnit* pUnit, bool bFarMove)
{
	PROFILE_FUNC();

	CLLNode<IDInfo>* pUnitNode;
	CLLNode<int>* pReinsertSearchStart;
	CLLNode<int>* pSelectionGroupNode;
	CLLNode<int>* pBestSelectionGroupNode;
	CvSelectionGroup* pLoopSelectionGroup;
	CvUnit* pHeadUnit;
	CvUnit* pBeforeUnit;
	CvUnit* pAfterUnit;
	CvUnit* pLoopUnit;
	CvPlot* pPlot;
	int iValue;
	int iBestValue;

	if (!(pUnit->onMap()))
	{
		return;
	}

	if ( isTempUnit(pUnit) )
	{
		return;
	}

	EnterCriticalSection(&c_GroupCycleSection);

	FAssertMsg(pUnit->getGroup() != NULL, "Unit->getGroup() is not assigned a valid value");

	pReinsertSearchStart = removeGroupCycle(pUnit->getGroupID());

	pPlot = pUnit->plot();

	pBeforeUnit = NULL;
	pAfterUnit = NULL;

//TB OOS Fix: Another longshot that did seem to help
	if ( pUnit->getGroup() == NULL )
	{
			::MessageBox(NULL,
						pUnit->getGroupID() == FFreeList::INVALID_INDEX ? "Unit with NULL group ID on call to CvPlayer::updateGroupCycle\n" : "Unit with no group on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
	}
	pUnitNode = pPlot->headUnitNode();

	while (pUnitNode != NULL)
	{
		pLoopUnit = ::getUnit(pUnitNode->m_data);
		pUnitNode = pPlot->nextUnitNode(pUnitNode);

		if ( pLoopUnit->getGroup() == NULL )
		{
			::MessageBox(NULL,
						pLoopUnit->getGroupID() == -1 ? "Unit with NULL group ID found in stack on call to CvPlayer::updateGroupCycle\n" : "Unit with no group found in stack on call to CvPlayer::updateGroupCycle\n",
						"CvGameCoreDLL Diagnostics",
						MB_OK);
		}

5)
Code:
 	//TB OOS Debug
	if (!pSelectionGroup->AI_isControlled() || GET_PLAYER(pSelectionGroup->getHeadOwner()).isHuman())
	{
		gDLL->getFAStarIFace()->ForceReset(finder);
		CvSelectionGroup::setGroupToCacheFor(pSelectionGroup);
		return pSelectionGroup->generatePath(pFromPlot, pToPlot, gDLL->getFAStarIFace()->GetInfo(finder), false, NULL, MAX_INT);
	}
Was:
Code:
 int pathDestValid(int iToX, int iToY, const void* pointer, FAStar* finder)
{
	PROFILE_FUNC();

	CvSelectionGroup* pSelectionGroup;
	CvPlot* pToPlot;
	CvPlot* pFromPlot;

	pToPlot = GC.getMapExternal().plot(iToX, iToY);
	FAssert(pToPlot != NULL);

	pFromPlot = GC.getMapExternal().plot(gDLL->getFAStarIFace()->GetStartX(finder), gDLL->getFAStarIFace()->GetStartY(finder));
	FAssert(pFromPlot != NULL);

	//	Safety valve since minidumps have shown that this (unknown how) can still occassionally
	//	happen (attempt to generate a path that starts or ends off the viewport)
	if ( pToPlot == NULL || pFromPlot == NULL )
	{
		return false;
	}

	pSelectionGroup = ((CvSelectionGroup *)pointer);

	if (pSelectionGroup->atPlot(pToPlot))
	{
		return TRUE;
	}

	if (pSelectionGroup->getDomainType() == DOMAIN_IMMOBILE)
	{
		return FALSE;
	}

	//OutputDebugString(CvString::format("PathDestValid (%d,%d)\n", iToX, iToY).c_str());
	if (!pSelectionGroup->AI_isControlled() || pSelectionGroup->getHeadOwner() == GC.getGameINLINE().getActivePlayer())

6)
Code:
 	if ( pUnit->canDefend() )
	{
		unitDetails.iDefensiveValue = iUnitStr;
	}
	//TB OOS Debug: Almost certainly a solid fix here
	else
	{
		unitDetails.iDefensiveValue = 0;
	}
	if ( pUnit->canAttack() )
	{
		unitDetails.iOffensiveValue = iUnitStr;
	}
	else
	{
		unitDetails.iOffensiveValue = 0;
	}
Was:
Code:
 void	CvContractBroker::lookingForWork(CvUnit* pUnit, int iMinPriority)
{
	PROFILE_FUNC();

	advertisingUnit	unitDetails;
	int				iUnitStr = GC.getGameINLINE().AI_combatValue(pUnit->getUnitType());

	unitDetails.bIsWorker = (pUnit->AI_getUnitAIType() == UNITAI_WORKER);

	//	Combat values are just the crude value of the unit type for now - should add in promotions
	//	here for sure
	if ( pUnit->canDefend() )
	{
		unitDetails.iDefensiveValue = iUnitStr;
	}
	if ( pUnit->canAttack() )
	{
		unitDetails.iOffensiveValue = iUnitStr;
	}

AND at the bottom of same function there were some unnecessary and possibly somehow problematic brackets. Removed those so now shows:
Code:
//TB OOS fix: another one that only MIGHT have had an effect to repair things at all
	MEMORY_TRACK_EXEMPT();

	m_advertisingUnits.push_back(unitDetails);
rather than:
Code:
 	{
		MEMORY_TRACK_EXEMPT();

		m_advertisingUnits.push_back(unitDetails);
	}

7)
Code:
 			//TB OOS Debug: from here we were not given a beginning definition
			int iBestCityTenderKey = 0;
			int iValue = 0;
			UnitTypes eUnit = NO_UNIT;
			//to here
Was:
Code:
 void CvContractBroker::finalizeTenderContracts(void)
{
	PROFILE_FUNC();

	std::map<int,int>	tenderAllocations;

	CvPathGenerator::EnableMaxPerformance(true);

	//	No need to lock here - this is always run in a single threaded context

	for(int iI = 0; iI < (int)m_workRequests.size(); iI++)
	{
		if ( !m_workRequests[iI].bFulfilled )
		{
			int iBestValue = 0;
			int iBestCityTenderKey;
			int iValue;
			UnitTypes eUnit;

And in the same function later on down the line:
Code:
 //TB OOS Debug: Undefined variable being called as a pointer in a later function call
								UnitAITypes eAIType = NO_UNITAI;
Was just UnitAITypes eAIType; which was apparently a problem considering how it cleared up OOS errors taking place to change it.

8)
Code:
	//TB OOS Debug: Undefined variable causing an OOS
	int iUnitValue = 0;
Was:
Code:
 void CvCityAI::AI_chooseProduction()
{
	PROFILE_FUNC();

	CvArea* pWaterArea;
	bool bWasFoodProduction;
	bool bLandWar;
	bool bAssault;
	bool bDefenseWar;
	bool bPrimaryArea;
	bool bFinancialTrouble;
	bool bDanger;
	bool bChooseUnit;
	bool bInhibitUnits = false;
	int iProductionRank;
	int iCulturePressure;
	m_bRequestedBuilding = false;	
	m_bRequestedUnit = false;	

	CvPlayerAI& kPlayer = GET_PLAYER(getOwnerINLINE());

	if (kPlayer.isAnarchy())
	{
		return;
	}

	bDanger = AI_isDanger();
	
	m_iBuildPriority = CITY_BUILD_PRIORITY_CEILING;
	m_iTempBuildPriority = CITY_BUILD_PRIORITY_CEILING;

	// only clear the dirty bit if we actually do a check, multiple items might be queued
	AI_setChooseProductionDirty(false);
/************************************************************************************************/
/* Afforess	                  Start		 04/22/10                                               */
/*                                                                                              */
/*                                                                                              */
/************************************************************************************************/
	if(GC.getUSE_AI_CHOOSE_PRODUCTION_CALLBACK())
	{
		PYTHON_ACCESS_LOCK_SCOPE

		// allow python to handle it
		CyCity* pyCity = new CyCity(this);
		CyArgsList argsList;
		argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity));	// pass in city class
		long lResult=0;
		PYTHON_CALL_FUNCTION4(__FUNCTION__, PYGameModule, "AI_chooseProduction", argsList.makeFunctionArgs(), &lResult);
		delete pyCity;	// python fxn must not hold on to this pointer
		if (lResult == 1)
		{
			return;
		}
	}

	if (isHuman() && isProductionAutomated())
	{
		if (!GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_MODDER_3))
		{
			AI_buildGovernorChooseProduction();
			return;
		}
		else
		{
			for (int iI = 0; iI < NUM_COMMERCE_TYPES; iI++)
			{
				if (AI_isEmphasizeCommerce((CommerceTypes)iI))
				{
					bInhibitUnits = true;
				}
			}
			for (int iI = 0; iI < NUM_YIELD_TYPES; iI++)
			{
				if (AI_isEmphasizeYield((YieldTypes)iI))
				{
					bInhibitUnits = true;
				}
			}
		}
	}

/************************************************************************************************/
/* Afforess	                     END                                                            */
/************************************************************************************************/
	
	CvArea* pArea = area();
	pWaterArea = waterArea(true);
	bool bMaybeWaterArea = false;
	bool bWaterDanger = false;
    
	if (pWaterArea != NULL)
	{
		bMaybeWaterArea = true;
		if (!GET_TEAM(getTeam()).AI_isWaterAreaRelevant(pWaterArea))
		{
			pWaterArea = NULL;
		}

		bWaterDanger = kPlayer.AI_getWaterDanger(plot(), 4) > 0;
	}

	bWasFoodProduction = isFoodProduction();
	//AIAndy: Not used here at the moment
	//bool bHasMetHuman = GET_TEAM(getTeam()).hasMetHuman();
	bool bMassing = (pArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT_MASSING);
	bLandWar = ((pArea->getAreaAIType(getTeam()) == AREAAI_OFFENSIVE) || (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE) || bMassing);
	bDefenseWar = (pArea->getAreaAIType(getTeam()) == AREAAI_DEFENSIVE);
	bool bAssaultAssist = (pArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT_ASSIST);
	bAssault = bAssaultAssist || (pArea->getAreaAIType(getTeam()) == AREAAI_ASSAULT) || bMassing;
	bPrimaryArea = kPlayer.AI_isPrimaryArea(pArea);
	bFinancialTrouble = kPlayer.AI_isFinancialTrouble();
	iCulturePressure = AI_calculateCulturePressure();
	int iNumCitiesInArea = pArea->getCitiesPerPlayer(getOwnerINLINE());
	bool bImportantCity = false; //be very careful about setting this.
	bool bBigCultureCity = false;
	int iCultureRateRank = findCommerceRateRank(COMMERCE_CULTURE);
    int iCulturalVictoryNumCultureCities = GC.getGameINLINE().culturalVictoryNumCultureCities();

	int iWarSuccessRatio = GET_TEAM(getTeam()).AI_getWarSuccessCapitulationRatio();
	int iEnemyPowerPerc = GET_TEAM(getTeam()).AI_getEnemyPowerPercent(true);
	int iWarTroubleThreshold = 0;

	//	Are our best worker units one-shot usage (mostly)?
	bool bBestWorkerIsOneShot = false;
	int iUnitValue;

Also under void CvCityAI::AI_chooseProduction():
Find the line:
Code:
 	int iEcononmyFlagsThreasholdWeighting;
and delete it. Then go down a bit to:
Code:
 	iEcononmyFlagsThreasholdWeighting = (100*iEconomyFlagBits)/8;
and just add int before it to declare it right there. This keeps the declaration of iEcononmyFlagsThreasholdWeighting at the point of declaration. I don't THINK this one really fixes anything but it does make it a little OOS safer potentially.
 
Still investigating that

AI Diplo Declare War Trade OOS

I'm not sure if it has any meaning but in void CvPlayerAI::AI_doDiplo() code, I've seen that mostly there are some lines looking like this
Code:
if (GET_PLAYER((PlayerTypes)iI).isHuman())
	        {
		if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
		{

before implementing any deal. But when we come to the AI Diplo Declare War Trade part, the code is different:

Code:
		if (!(GET_PLAYER((PlayerTypes)iI).isHuman()) || !(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
		{

Does it make any sense? Does it look correct?
 
Top Bottom