OOS problems and how to fix them

Mmmmm, it looks like an entire section of code is missing. That part I've reported here above it's meant only for AI; I'm not 100% sure what it does, but it's probably some way to buy someone to make war with someone else (not buying an ally for an ongoing war - that part is some lines above - but simply paying someone to wage war to someone else). Problem is that there's no code for this kind of trade with human players. I hope I can fix it, if you haven't already. I'll let you know.
 
Code:
	//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);
	}
Those changes don't do anything meaningful. They don't hurt either though. If this code was really executed, you would get a Windows message box which I assume you don't.

6)
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);
	}
These are meaningful as a limit to the lifetime of the variable that MEMORY_TRACK_EXEMPT creates. So you should keep them there (this only matters when you do memory tracking but extra brackets like that don't hurt).

Apart from that well done on all the effort this must have cost.
 
Thanks! And you basically confirmed my suspicions that there wasn't much if any difference in those particular changes. No reason not to try the tweaks I figured since I also figured either way it wouldn't really make a difference theoretically but they were related to the OOS error locations so I thought MAYBE...
lol
 
45°38'N-13°47'E;13118685 said:
Mmmmm, it looks like an entire section of code is missing. That part I've reported here above it's meant only for AI; I'm not 100% sure what it does, but it's probably some way to buy someone to make war with someone else (not buying an ally for an ongoing war - that part is some lines above - but simply paying someone to wage war to someone else). Problem is that there's no code for this kind of trade with human players. I hope I can fix it, if you haven't already. I'll let you know.

I was kinda thinking something along those lines. I don't think human players CAN ask another player to go to war if they are not at war with the target so that wouldn't be a highly suspect place for an OOS error.

At some point here what I'll do is explain exactly how I setup my tracking mechanism and how I made it work for me to find the previous OOS error fixes - would probably help you a lot in your effort on that one.
 
I was kinda thinking something along those lines. I don't think human players CAN ask another player to go to war if they are not at war with the target so that wouldn't be a highly suspect place for an OOS error.

I actually think it's possible but I need to check. What I'm not sure it's possible, is for an AI player to ask a human player to go at war with someone that AI isn't at war with; I suspect this is what's causing that OOS, because it should work between AIs but not with human players. I'm still investigating though. Another suspicious thing is if someoneelse's vassal is able to ask a human player to go to war with someone else.

At some point here what I'll do is explain exactly how I setup my tracking mechanism and how I made it work for me to find the previous OOS error fixes - would probably help you a lot in your effort on that one.

Thank you TB, that would surely help (if I'm smart enough to understand what you've done)! :)
 
I found the source of the first strike problem... commander caching.

This:
Code:
	//Perhaps do not cache commanders because it causes an OOS error to do so?  AIs make abortive odds calcs as well do they not?
	if ( !isHuman() && !GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS))
	{
		m_iCommanderCacheTurn = GC.getGameINLINE().getGameTurn();
		m_iCachedCommander = (pBestCommander == NULL ? NO_COMMANDER_ID : pBestCommander->getID());
	}

	return pBestCommander;
}
placed at the bottom of
CvUnit* CvUnit::getCommander() const
will solve this. It slows turn times a bit for multi-player games but leaves it functioning faster for single player games. This method is a bit more accurate in terms of enforcing game rules as well. Koshling is right to want to cache this as it does indeed get called a LOT but in a simultaneous turns game the delay worth it to avoid OOS errors. And of course, playing without commanders would improve turn speed quite a bit for multi-player games if it's an issue.

The only way the maximum first strike value could vary as is showing in the Random Logs is either this or something far more complicated like a promo assignment taking place on only one system and not the other and I don't believe that can happen without another random call showing an OOS indicator (either from evaluating the best promo to select or the random in the field promotion mechanism) which it clearly did not on the log I got that pointed this one out.

Testing showed this worked.

I also had an OOS error in my fort AI code recently posted that I just fixed but I don't think you've got the superforts update so that shouldn't apply for AND.


I'm sure you'll understand the explanation of how the logging tracker would work. Just need to find the time to explain it in full here is all.
 
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.

Is this what you've done here for iAtX and iAtY?
If so, when I find an OOS pointing me to "Combat", which leads me here

Code:
if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Combat") < iDefenderOdds)

inside CvUnit ResolveCombat function, following your reasoning could it be that the problem is in
int iDefenderOdds
not being initialized some lines above?

Code:
void CvUnit::resolveCombat(CvUnit* pDefender, CvPlot* pPlot, CvBattleDefinition& kBattle)
{
	PROFILE_FUNC();
	MEMORY_TRACK();

	CombatDetails cdAttackerDetails;
	CombatDetails cdDefenderDetails;

	AI_setPredictedHitPoints(-1);
	pDefender->AI_setPredictedHitPoints(-1);
	int iAttackerStrength = currCombatStr(NULL, NULL, &cdAttackerDetails);
	int iAttackerFirepower = currFirepower(NULL, NULL);
	int iDefenderStrength;
	int iAttackerDamage;
	int iDefenderDamage;
	[COLOR="Red"]int iDefenderOdds[/COLOR];
/************************************************************************************************/
/* Afforess	                  Start		 03/15/10                      Coded By: KillMePlease   */
/*                                                                                              */
/* Occasional Promotions                                                                        */
/************************************************************************************************/
	bool bAttackerWithdrawn = false;
	bool bAttackerHasLostNoHP = true;
	int iAttackerInitialDamage = getDamage();
	int iDefenderInitialDamage = pDefender->getDamage();
	int iInitialDefXP = pDefender->getExperience100();
	int iInitialAttXP = getExperience100();
	int iInitialAttGGXP = GET_PLAYER(getOwnerINLINE()).getCombatExperience();
	int iInitialDefGGXP = GET_PLAYER(pDefender->getOwnerINLINE()).getCombatExperience();
	bool bDynamicXP = GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP);
/************************************************************************************************/
/* Afforess	                     END                                                            */
/************************************************************************************************/

	getDefenderCombatValues(*pDefender, pPlot, iAttackerStrength, iAttackerFirepower, [COLOR="Red"]iDefenderOdds[/COLOR], iDefenderStrength, iAttackerDamage, iDefenderDamage, &cdDefenderDetails, pDefender);
	int iAttackerKillOdds = iDefenderOdds * (100 - withdrawalProbability()) / 100;

	if (isHuman() || pDefender->isHuman())
	{
		//Added ST
		CyArgsList pyArgsCD;
		pyArgsCD.add(gDLL->getPythonIFace()->makePythonObject(&cdAttackerDetails));
		pyArgsCD.add(gDLL->getPythonIFace()->makePythonObject(&cdDefenderDetails));
		pyArgsCD.add(getCombatOdds(this, pDefender));
		CvEventReporter::getInstance().genericEvent("combatLogCalc", pyArgsCD.makeFunctionArgs());
	}

	collateralCombat(pPlot, pDefender);
/************************************************************************************************/
/* Afforess	                  Start		 02/22/10                Coded by: KillMePlease         */
/*                                                                                              */
/*   Defender Withdraw                                                                          */
/************************************************************************************************/
	int iCloseCombatRoundNum = -1;
	bool bTryMobileWithdraw = false;	//if unit will be trying to withdraw from a plot it occupies
	if (pPlot->getNumDefenders(pDefender->getOwner()) == 1 && pDefender->baseMoves() > baseMoves())	//must be faster than attacker
	{
		bTryMobileWithdraw = true;
	}
	int iWinningOdds = getCombatOdds(this, pDefender);
	bool bDefenderSkirmish = false; //iWinningOdds > 60;
	m_combatResult.bDefenderWithdrawn = false;
	m_combatResult.pPlot = NULL;
	m_combatResult.iAttacksCount++;
/************************************************************************************************/
/* Afforess	                     END                                                            */
/************************************************************************************************/
	
	while (true)
	{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      02/21/10                                jdog5000      */
/*                                                                                              */
/* Lead From Behind                                                                             */
/************************************************************************************************/
		// From Lead From Behind by UncutDragon
/* original code
		if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("COMBAT_DIE_SIDES"), "Combat") < iDefenderOdds)
*/		// modified
		[B]if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Combat") < [COLOR="Red"]iDefenderOdds[/COLOR])[/B]

Code is different between AND and C2C here because of your CombatMod which I haven't imported in AND, but I just want to make sure I've understood what you meant.
 
45°38'N-13°47'E;13118044 said:
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?

More on this problem that I'm still investigating. It looks like there's a feature that was never completed in AND and was imported in the same truncated state into C2C. Specifically, it's something related to trading war with other civs. The code in the dll has never been completed and I'm looking for a way to complete it. But there's more, there are parts missing probably in python too. Have a look at BUG options in C2C (as in AND), under Alert Tab. You'll see there's a missing function "Trade War Alert" or something like that. I've commented out that part in AND more than 1 year ago because I've got it that it was a never completed feature (it's found in BugAlertsOptionsTab.py

Code:
		#RevolutionDCM start - extra civlerts
		self.addCheckbox(screen, center, "MoreCiv4lerts__WarTrade")
		#RevolutionDCM end

But of course removing this line doesn't remove the problem. I'll see what I can do and I'll share my findings here but I'll have to reverse-engineer the code again to understand what's doing what, so it could take time. :(
I guess this thing is causing one of the most frequent OOS I've got until now.
 
Interesting... I've never worked with the diplomacy side of stuff at all so I'm not sure how any of it functions fundamentally. Good luck with finding the fix and again, please explicitly express how to resolve the issue once you've got it hunted down!
 
Does the scoreboard work in C2C multi player?

I notice that The_J posted a fix for the OOS problems with using it quite awhile ago but it does not seem to be in C2C. He only tested it in hot seat. See here

edit If the scoreboard is not on in multi player and you want to test a version with this fix put the attached in Assets/Python/Screens - make a back up first.
 
What do I know!:D I just read it in the doco. Any way you should not see what the other humans scores are until you meet them! TB if it works OK for you I'll put it on the SVN. It is about 4 lines of code.
 
45°38'N-13°47'E;13144982 said:
Is this what you've done here for iAtX and iAtY?
If so, when I find an OOS pointing me to "Combat", which leads me here

Code:
if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Combat") < iDefenderOdds)

inside CvUnit ResolveCombat function, following your reasoning could it be that the problem is in
int iDefenderOdds
not being initialized some lines above?

Code:
void CvUnit::resolveCombat(CvUnit* pDefender, CvPlot* pPlot, CvBattleDefinition& kBattle)
{
	PROFILE_FUNC();
	MEMORY_TRACK();

	CombatDetails cdAttackerDetails;
	CombatDetails cdDefenderDetails;

	AI_setPredictedHitPoints(-1);
	pDefender->AI_setPredictedHitPoints(-1);
	int iAttackerStrength = currCombatStr(NULL, NULL, &cdAttackerDetails);
	int iAttackerFirepower = currFirepower(NULL, NULL);
	int iDefenderStrength;
	int iAttackerDamage;
	int iDefenderDamage;
	[COLOR="Red"]int iDefenderOdds[/COLOR];
/************************************************************************************************/
/* Afforess	                  Start		 03/15/10                      Coded By: KillMePlease   */
/*                                                                                              */
/* Occasional Promotions                                                                        */
/************************************************************************************************/
	bool bAttackerWithdrawn = false;
	bool bAttackerHasLostNoHP = true;
	int iAttackerInitialDamage = getDamage();
	int iDefenderInitialDamage = pDefender->getDamage();
	int iInitialDefXP = pDefender->getExperience100();
	int iInitialAttXP = getExperience100();
	int iInitialAttGGXP = GET_PLAYER(getOwnerINLINE()).getCombatExperience();
	int iInitialDefGGXP = GET_PLAYER(pDefender->getOwnerINLINE()).getCombatExperience();
	bool bDynamicXP = GC.getGameINLINE().isModderGameOption(MODDERGAMEOPTION_IMPROVED_XP);
/************************************************************************************************/
/* Afforess	                     END                                                            */
/************************************************************************************************/

	getDefenderCombatValues(*pDefender, pPlot, iAttackerStrength, iAttackerFirepower, [COLOR="Red"]iDefenderOdds[/COLOR], iDefenderStrength, iAttackerDamage, iDefenderDamage, &cdDefenderDetails, pDefender);
	int iAttackerKillOdds = iDefenderOdds * (100 - withdrawalProbability()) / 100;

	if (isHuman() || pDefender->isHuman())
	{
		//Added ST
		CyArgsList pyArgsCD;
		pyArgsCD.add(gDLL->getPythonIFace()->makePythonObject(&cdAttackerDetails));
		pyArgsCD.add(gDLL->getPythonIFace()->makePythonObject(&cdDefenderDetails));
		pyArgsCD.add(getCombatOdds(this, pDefender));
		CvEventReporter::getInstance().genericEvent("combatLogCalc", pyArgsCD.makeFunctionArgs());
	}

	collateralCombat(pPlot, pDefender);
/************************************************************************************************/
/* Afforess	                  Start		 02/22/10                Coded by: KillMePlease         */
/*                                                                                              */
/*   Defender Withdraw                                                                          */
/************************************************************************************************/
	int iCloseCombatRoundNum = -1;
	bool bTryMobileWithdraw = false;	//if unit will be trying to withdraw from a plot it occupies
	if (pPlot->getNumDefenders(pDefender->getOwner()) == 1 && pDefender->baseMoves() > baseMoves())	//must be faster than attacker
	{
		bTryMobileWithdraw = true;
	}
	int iWinningOdds = getCombatOdds(this, pDefender);
	bool bDefenderSkirmish = false; //iWinningOdds > 60;
	m_combatResult.bDefenderWithdrawn = false;
	m_combatResult.pPlot = NULL;
	m_combatResult.iAttacksCount++;
/************************************************************************************************/
/* Afforess	                     END                                                            */
/************************************************************************************************/
	
	while (true)
	{
/************************************************************************************************/
/* BETTER_BTS_AI_MOD                      02/21/10                                jdog5000      */
/*                                                                                              */
/* Lead From Behind                                                                             */
/************************************************************************************************/
		// From Lead From Behind by UncutDragon
/* original code
		if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("COMBAT_DIE_SIDES"), "Combat") < iDefenderOdds)
*/		// modified
		[B]if (GC.getGameINLINE().getSorenRandNum(GC.getCOMBAT_DIE_SIDES(), "Combat") < [COLOR="Red"]iDefenderOdds[/COLOR])[/B]

Code is different between AND and C2C here because of your CombatMod which I haven't imported in AND, but I just want to make sure I've understood what you meant.
Time for an Epic Thread Resurrect!

My wife and I tried a new MP game last night. We had a lot of OOS errors that seemed related to battle. I checked this logic since I never did follow up on it and I think this was correct but applied to much more than just the highlighted variable. I'll put a 'fix' to this on the SVN soon.

But now I'm looking at weird like I've never theorized before. I've never known of an OOS problem in the property system so that right up front eliminates a lot of the current primary OOS problem I face. But how we get:
Player 9, Disease (Scurvy), Building class count plus building: 1
Player 9, Disease (Swine Flu), Building class count plus building: 0
Player 9, Disease (Bird Flu), Building class count plus building: 0
Player 9, Disease (Anthrax), Building class count plus building: 0
Player 9, Disease (Dysentery), Building class count plus building: 1
differing from
Player 9, Disease (Scurvy), Building class count plus building: 0
Player 9, Disease (Swine Flu), Building class count plus building: 0
Player 9, Disease (Bird Flu), Building class count plus building: 0
Player 9, Disease (Anthrax), Building class count plus building: 0
Player 9, Disease (Dysentery), Building class count plus building: 0
with no other differences but:
Player 9 Power: 27
vs
Player 9 Power: 25
and an expected difference in the building counts that just reiterates what the first portion expresses... is entirely confusing to me.

The Power difference I've figured out does have to do with those buildings actually having a 1 for their <iPower> tag. Which is plain not right imo. It means a lot of property buildings need to be fixed of this.

The question this brings up (@Alberts2 or even AIAndy if you're seeing this!):
How? How would it be possible for everything to vary but the existing diseases? We aren't using any randomness (and the random logs show no differences) in the property counts that generate those buildings.

I'm currently thinking it may have to do with something that maybe took place earlier that wouldn't show now as different. Like perhaps they had a healer that moved and got killed but on different rounds or something. There used to be an AI issue there. (But that's been restructured too - not to say it can't ALSO have another issue.)








Another thing this compare brings up:
@Alberts2: Can you explain this? You made some changes to the BuildUps and I'm wondering how this is happening:
Player 8, Unit ID: 49162, Chaser
X: 139, Y: 53
Damage: 0
Experience: 7
Level: 3
Promotions:
Bottleneck I
Self Heal I
Build Up (Chase Plans) I
Build Up (Chase Plans) II
Build Up (Chase Plans) III
Build Up (Chase Plans) IV
Build Up (Chase Plans) V
Build Up (Escape Plans) I
Build Up (Escape Plans) II
Build Up (Escape Plans) III
Build Up (Escape Plans) IV
Build Up (Escape Plans) V
How is this unit building up into two different buildups? At least the player can't appear to do this but apparently the AI can somehow. I'll look into it at some point soon but since that's something I can wait on I figured I'd ask you since you were more recently making tweaks there.
 
How is this unit building up into two different buildups? At least the player can't appear to do this but apparently the AI can somehow. I'll look into it at some point soon but since that's something I can wait on I figured I'd ask you since you were more recently making tweaks there.
I actually reported that this can happen and how it happens some months ago. If a unit build up one line and merges with two other units they will keep that build up even if they start building up a different line or make a move/attack.

I think they could return to only having one build-up line if they were to start building up the one that is stuck; it would then disappear as it should when the unit stops that build up.
 
I believe I had fixed that first issue. Proof being players can't do this anymore. But there's now an issue with the AI resetting the BuildUp properly to no buildups when it goes to change or moves or whatever... doesn't clear properly somehow.
 
Top Bottom