OOS problems and how to fix them

It is an important information and the change we added earlier in this investigation does deactivate that kind of optimization for human players. There might be some more path caching somewhere though.

What Toffer90 says it's not what happened to us. First of all, it only happened with naval units so far (well, it happened just once with a worker, but that was before I imported your latest changes into the dll of AND, so I think that part was solved). Second we did not interrupt a go-to order and reissued to the same destination. As I explained in the post above, the galley was 2 tiles north from that barbarian galley you see in the screenshot; my wife moved it 1 tile south, just north of the barbarian galley; it didn't stop its movement by itself: my wife stopped it there. Then she resumed moving her galley programming the galley to move 1 square south of the barbarian galley, where you can see it in the screenshot (2 tiles movement, 1SW and 1SE). That's when the OOS happened because her galley moved on her pc but not on mine (in fact I immediately saved the game and if we reload it, it looks like her galley hasn't moved and it's still 1 tile north of the barbarian galley). So no, it's not something related to moving the unit to the same destination after its moves have been interrupted.
 
More on this pathing caching issue; looking around the code for cached parts, I've come across this:

bool CvPlot::HaveCachedPathValidityResult(void* entity, bool bIsAlternateResult, bool& cachedResult)

and

void CvPlot::CachePathValidityResult(void* entity, bool bIsAlternateResult, bool cachedResult)

in CvPlot.cpp which also leads me to investigate bIsAlternateResult and finding in cvgamecoreutils.cpp

Code:
int pathValid(FAStarNode* parent, FAStarNode* node, int data, const void* pointer, FAStar* finder)
{
	PROFILE_FUNC();

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

	if (parent == NULL)
	{
		return TRUE;
	}

	pFromPlot = GC.getMapExternal().plot(parent->m_iX, parent->m_iY);
	pToPlot = GC.getMapExternal().plot(node->m_iX, node->m_iY);

	pSelectionGroup = ((CvSelectionGroup *)pointer);

#ifdef USE_OLD_PATH_GENERATOR
	// XXX might want to take this out...
	if (pSelectionGroup->getDomainType() == DOMAIN_SEA)
	{
		PROFILE("pathValid domain sea");

#if 0
		//	Optimisation short-circuit some invalid pathing choices quickly
		if (!pToPlot->isWater() && !pSelectionGroup->canMoveAllTerrain() && !pToPlot->isCanMoveSeaUnits())
		{
			if (!pToPlot->isCity()) 
			{
				return FALSE;
			}
		}
#endif
		//	Can't cross diagonally across 'land'
		if (pFromPlot->isWater() && pToPlot->isWater())
		{
			if (!(GC.getMapINLINE().plotINLINE(pFromPlot->getX_INLINE(), pToPlot->getY_INLINE())->isWater()) && !(GC.getMapINLINE().plotINLINE(pToPlot->getX_INLINE(), pFromPlot->getY_INLINE())->isWater()))
			{
				if( !(pSelectionGroup->canMoveAllTerrain()) )
				{
					return FALSE;
				}
			}
		}
	}

	//	KOSHLING MOD - none of the rest of the calculation depends on pToPlot, 
	//	so we can cache the results from one request for each parent/pFromPlot.
	bool bCheckNonInvisibleDanger = !(gDLL->getFAStarIFace()->GetInfo(finder) & MOVE_IGNORE_DANGER) &&
									pSelectionGroup->AI_isControlled() &&
									((parent->m_iData2 > 1) || (parent->m_iData1 == 0));

	bool bResult;
	if ( !pFromPlot->HaveCachedPathValidityResult( pSelectionGroup, bCheckNonInvisibleDanger, bResult ) )
	{
		bResult = pathValidInternal(pFromPlot, bCheckNonInvisibleDanger, pSelectionGroup, gDLL->getFAStarIFace()->GetInfo(finder));

		pFromPlot->CachePathValidityResult( pSelectionGroup, bCheckNonInvisibleDanger, bResult );
	}

	return bResult;
#else
	bool bResult;
	
	if ( pFromPlot != NULL && pToPlot != NULL )
	{
		bResult = ((CvSelectionGroup *)pointer)->getPath().containsNode(pFromPlot) || ((CvSelectionGroup *)pointer)->getPath().containsNode(pToPlot);
	}
	else
	{
		bResult = false;
	}

	//OutputDebugString(CvString::format("PathValid (%d,%d)->(%d,%d): [%d]\n", pFromPlot->getX_INLINE(), pFromPlot->getY_INLINE(), pToPlot->getX_INLINE(), pToPlot->getY_INLINE(),bResult).c_str());
	if (!pSelectionGroup->AI_isControlled())
	{
		//OutputDebugString("Force reset finder\n");
		gDLL->getFAStarIFace()->ForceReset(finder);
	}

	return bResult;
#endif
}

As I've said I'm not a very skilled programmer but are we sure that it's safe to cache this part as Koshling was supposing?
 
Something more I've found, although I'm not sure if it's related to our problem. I remember getActivePlayer can cause OOS on some occasion; well, in CvGameCoreUtils .cpp it's used in int pathDestValid. Is it legal to use it there (in red)?

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() == [COLOR="Red"]GC.getGameINLINE().getActivePlayer()[/COLOR])
	{
		gDLL->getFAStarIFace()->ForceReset(finder);
		CvSelectionGroup::setGroupToCacheFor(pSelectionGroup);
		return pSelectionGroup->generatePath(pFromPlot, pToPlot, gDLL->getFAStarIFace()->GetInfo(finder), false, NULL, MAX_INT);
	}
	else
	{
		bool bDummy;

		return NewPathDestValid(pSelectionGroup, iToX, iToY, gDLL->getFAStarIFace()->GetInfo(finder), bDummy);
	}
}
 
45°38'N-13°47'E;13073284 said:
Something more I've found, although I'm not sure if it's related to our problem. I remember getActivePlayer can cause OOS on some occasion; well, in CvGameCoreUtils .cpp it's used in int pathDestValid. Is it legal to use it there (in red)?
Probably not a valid use. This is a function though that is only used by the old pathfinder (I think at least, Koshling is the expert on the pathfinding code).

The CachePathValidityResult might well be part of the problem though, if for some reason bad caching happens.
 
Probably not a valid use. This is a function though that is only used by the old pathfinder (I think at least, Koshling is the expert on the pathfinding code).

You're right, I've searched the dll for pathDestValid and I've found it called only in void CvMap::setup()

Code:
	gDLL->getFAStarIFace()->Initialize(&GC.getPathFinder(), getGridWidthINLINE(), getGridHeightINLINE(), isWrapXINLINE(), isWrapYINLINE(), pathDestValid, pathHeuristic, pathCost, pathValid, pathAdd, NULL, NULL);
	gDLL->getFAStarIFace()->Initialize(&GC.getInterfacePathFinder(), getGridWidthINLINE(), getGridHeightINLINE(), isWrapXINLINE(), isWrapYINLINE(), pathDestValid, pathHeuristic, pathCost, pathValid, pathAdd, NULL, NULL);

which probably isn't related to OOS

The CachePathValidityResult might well be part of the problem though, if for some reason bad caching happens.

Yeah, I'm looking through the code and more than int pathValid (which is also used in the old path generator), I think bool ContextFreeNewPathValidFunc could be responsible (there's an identical part of the code)

Code:
bool ContextFreeNewPathValidFunc(CvSelectionGroup* pSelectionGroup, int iFromX, int iFromY, int iToX, int iToY, int iFlags, bool isTerminus, bool bMoveTerminationChecksOnly, int iPathTurns, bool* pbToNodeInvalidity, bool* pbValidAsTerminus)
{
	PROFILE_FUNC();

	CvPlot* pFromPlot;
	CvPlot* pToPlot;
	PlayerTypes eOwner = pSelectionGroup->getHeadOwner();

	pFromPlot = GC.getMapINLINE().plotSorenINLINE(iFromX,iFromY);
	FAssert(pFromPlot != NULL);
	pToPlot = GC.getMapINLINE().plotSorenINLINE(iToX, iToY);
	FAssert(pToPlot != NULL);

	if ( pbValidAsTerminus != NULL )
	{
		*pbValidAsTerminus = false;
	}

	if ( !bMoveTerminationChecksOnly )
	{
		switch(pSelectionGroup->getDomainType())
		{
		case DOMAIN_SEA:
			{
				PROFILE("pathValid domain sea");

		#if 0
				//	Optimisation short-circuit some invalid pathing choices quickly
				if (!pToPlot->isWater() && !pSelectionGroup->canMoveAllTerrain() && !pToPlot->isCanMoveSeaUnits())
				{
					if (!pToPlot->isCity()) 
					{
						return FALSE;
					}
				}
		#endif
				//	Can't cross diagonally across 'land'
				if (pFromPlot->isWater() && pToPlot->isWater())
				{
					if (!(GC.getMapINLINE().plotINLINE(pFromPlot->getX_INLINE(), pToPlot->getY_INLINE())->isWater()) && !(GC.getMapINLINE().plotINLINE(pToPlot->getX_INLINE(), pFromPlot->getY_INLINE())->isWater()))
					{
						if( !(pSelectionGroup->canMoveAllTerrain()) )
						{
							return FALSE;
						}
					}
				}
			}
			break;
		case DOMAIN_LAND:
			if ( (iFlags & MOVE_NO_LAND_UNITS_ACROSS_WATER) != 0 && pToPlot->isWater() )
			{
				return FALSE;
			}
			break;
		default:
			break;
		}

		//	Need to handle ZOCs
		//	ZOCs don't apply into cities of the unit owner
		TeamTypes	eTeam = pSelectionGroup->getHeadTeam();

		if ( pToPlot->getPlotCity() == NULL || pToPlot->getPlotCity()->getTeam() != eTeam )
		{
			//Fort ZOC
			PlayerTypes eDefender = pFromPlot->controlsAdjacentFort(eTeam);
			if (eDefender != NO_PLAYER)
			{
				const CvPlot* pZoneOfControl = pFromPlot->isInFortControl(true, eDefender, eOwner);
				const CvPlot* pForwardZoneOfControl = pToPlot->isInFortControl(true, eDefender, eOwner);
				if (pZoneOfControl != NULL && pForwardZoneOfControl != NULL)
				{
					if (pZoneOfControl == pToPlot->isInFortControl(true, eDefender, eOwner, pZoneOfControl))
					{
						return false;
					}
				}
			}
			
			//City ZoC		
			if (pFromPlot->isInCityZoneOfControl(eOwner) && pToPlot->isInCityZoneOfControl(eOwner))
			{
				return false;
			}
		}
		//Promotion ZoC
		if (pFromPlot->isInUnitZoneOfControl(eOwner) && pToPlot->isInUnitZoneOfControl(eOwner))
		{
			return false;
		}
	}

	if ( isTerminus )
	{
		//	Need to prevent false failures when the terminal
		//	plot contains an enemy unit (and we don't have the flag to allow movement through enemies
		//	(which should not prevent attacking enemies in the terminal plot)
		return true;
	}

	if ( pbValidAsTerminus != NULL )
	{
		*pbValidAsTerminus = true;
	}

	//	KOSHLING MOD - none of the rest of the calculation depends on pFromPlot, 
	//	so we can cache the results from one request for each pToPlot.
	bool bCheckNonInvisibleDanger = !(iFlags & MOVE_IGNORE_DANGER) &&
									pSelectionGroup->AI_isControlled() &&
									bMoveTerminationChecksOnly &&
									!(pSelectionGroup->canFight()) &&
									!(pSelectionGroup->alwaysInvisible());

	if ( bMoveTerminationChecksOnly && !bCheckNonInvisibleDanger )
	{
		return true;
	}

	unsigned int iEntityId = 0;

	CheckSum(iEntityId, pSelectionGroup->getHeadUnit()->getID());
	CheckSum(iEntityId, (int)eOwner);
	CheckSum(iEntityId, iFlags);

	bool bResult;
	if ( !pToPlot->HaveCachedPathValidityResult( (void*)iEntityId, bCheckNonInvisibleDanger, bResult ) )
	{
		bResult = pathValidInternal(pToPlot, bCheckNonInvisibleDanger, pSelectionGroup, iFlags);

		pToPlot->CachePathValidityResult( (void*)iEntityId, bCheckNonInvisibleDanger, bResult );
	}

	if ( !bCheckNonInvisibleDanger && pbToNodeInvalidity != NULL )
	{
		*pbToNodeInvalidity = !bResult;
	}

	return bResult;
}


also that part in cvplot.cpp containing m_iCachePathEpoch and m_iGlobalCachePathEpoch could be somehow connected

Code:
bool CvPlot::HaveCachedPathValidityResult(void* entity, bool bIsAlternateResult, bool& cachedResult)
{
	if ( m_iCachePathEpoch == m_iGlobalCachePathEpoch )
	{
		if ( bIsAlternateResult )
		{
			if ( m_cachedPathAlternateValidityEntity == entity )
			{
				cachedResult = m_cachedPathAlternateValidity;
				return true;
			}
			else
			{
				return false;
			}
		}
		else
		{
			if ( m_cachedPathValidityEntity == entity )
			{
				cachedResult = m_cachedPathValidity;
				return true;
			}
			else
			{
				return false;
			}
		}
	}
	else
	{
		return false;
	}
}
But to tell the truth, it's beyond my ability to understand if the problem is here and how to solve it. :(
 
I'm moving the conversation to the new Multi-player bugs thread for this version as it impacts things there.

To respond to you on your frustration (which trust me - I feel the same way when trying to do this stuff!) PERHAPS the same fix could apply here that we used on the combat issue - making the caching only be effective IF it involves units that are not isHuman().
 
I'm moving the conversation to the new Multi-player bugs thread for this version as it impacts things there.

To respond to you on your frustration (which trust me - I feel the same way when trying to do this stuff!) PERHAPS the same fix could apply here that we used on the combat issue - making the caching only be effective IF it involves units that are not isHuman().

I know, I was thinking about it but I'm not sure how NOT to cache in that part of the code.
 
I know right? Like what on earth is a terminus in the first place... I'd REALLY have to spend hours trying to sort out what's taking place here at all. I might just have to anyhow - don't like not understanding how the pathing works...
 
I know right? Like what on earth is a terminus in the first place... I'd REALLY have to spend hours trying to sort out what's taking place here at all. I might just have to anyhow - don't like not understanding how the pathing works...

Well, I've imported the code in AND over months so I've seen it developing, but most of it it's really obscure to me. Koshling's ability is far far beyond my comprehension. :(
 
Well, to skip that caching part in

bool ContextFreeNewPathValidFunc

what if I do something like (changes in red)

Code:
bool ContextFreeNewPathValidFunc(CvSelectionGroup* pSelectionGroup, int iFromX, int iFromY, int iToX, int iToY, int iFlags, bool isTerminus, bool bMoveTerminationChecksOnly, int iPathTurns, bool* pbToNodeInvalidity, bool* pbValidAsTerminus)
{
	PROFILE_FUNC();

	CvPlot* pFromPlot;
	CvPlot* pToPlot;
	PlayerTypes eOwner = pSelectionGroup->getHeadOwner();

	pFromPlot = GC.getMapINLINE().plotSorenINLINE(iFromX,iFromY);
	FAssert(pFromPlot != NULL);
	pToPlot = GC.getMapINLINE().plotSorenINLINE(iToX, iToY);
	FAssert(pToPlot != NULL);

	if ( pbValidAsTerminus != NULL )
	{
		*pbValidAsTerminus = false;
	}

	if ( !bMoveTerminationChecksOnly )
	{
		switch(pSelectionGroup->getDomainType())
		{
		case DOMAIN_SEA:
			{
				PROFILE("pathValid domain sea");

		#if 0
				//	Optimisation short-circuit some invalid pathing choices quickly
				if (!pToPlot->isWater() && !pSelectionGroup->canMoveAllTerrain() && !pToPlot->isCanMoveSeaUnits())
				{
					if (!pToPlot->isCity()) 
					{
						return FALSE;
					}
				}
		#endif
				//	Can't cross diagonally across 'land'
				if (pFromPlot->isWater() && pToPlot->isWater())
				{
					if (!(GC.getMapINLINE().plotINLINE(pFromPlot->getX_INLINE(), pToPlot->getY_INLINE())->isWater()) && !(GC.getMapINLINE().plotINLINE(pToPlot->getX_INLINE(), pFromPlot->getY_INLINE())->isWater()))
					{
						if( !(pSelectionGroup->canMoveAllTerrain()) )
						{
							return FALSE;
						}
					}
				}
			}
			break;
		case DOMAIN_LAND:
			if ( (iFlags & MOVE_NO_LAND_UNITS_ACROSS_WATER) != 0 && pToPlot->isWater() )
			{
				return FALSE;
			}
			break;
		default:
			break;
		}

		//	Need to handle ZOCs
		//	ZOCs don't apply into cities of the unit owner
		TeamTypes	eTeam = pSelectionGroup->getHeadTeam();

		if ( pToPlot->getPlotCity() == NULL || pToPlot->getPlotCity()->getTeam() != eTeam )
		{
			//Fort ZOC
			PlayerTypes eDefender = pFromPlot->controlsAdjacentFort(eTeam);
			if (eDefender != NO_PLAYER)
			{
				const CvPlot* pZoneOfControl = pFromPlot->isInFortControl(true, eDefender, eOwner);
				const CvPlot* pForwardZoneOfControl = pToPlot->isInFortControl(true, eDefender, eOwner);
				if (pZoneOfControl != NULL && pForwardZoneOfControl != NULL)
				{
					if (pZoneOfControl == pToPlot->isInFortControl(true, eDefender, eOwner, pZoneOfControl))
					{
						return false;
					}
				}
			}
			
			//City ZoC		
			if (pFromPlot->isInCityZoneOfControl(eOwner) && pToPlot->isInCityZoneOfControl(eOwner))
			{
				return false;
			}
		}
		//Promotion ZoC
		if (pFromPlot->isInUnitZoneOfControl(eOwner) && pToPlot->isInUnitZoneOfControl(eOwner))
		{
			return false;
		}
	}

	if ( isTerminus )
	{
		//	Need to prevent false failures when the terminal
		//	plot contains an enemy unit (and we don't have the flag to allow movement through enemies
		//	(which should not prevent attacking enemies in the terminal plot)
		return true;
	}

	if ( pbValidAsTerminus != NULL )
	{
		*pbValidAsTerminus = true;
	}

	//	KOSHLING MOD - none of the rest of the calculation depends on pFromPlot, 
	//	so we can cache the results from one request for each pToPlot.
	bool bCheckNonInvisibleDanger = !(iFlags & MOVE_IGNORE_DANGER) &&
									pSelectionGroup->AI_isControlled() &&
									bMoveTerminationChecksOnly &&
									!(pSelectionGroup->canFight()) &&
									!(pSelectionGroup->alwaysInvisible());
[COLOR="Red"]	if (!GET_PLAYER(eOwner).isHuman())
	{[/COLOR]
	if ( bMoveTerminationChecksOnly && !bCheckNonInvisibleDanger )
	{
		return true;
	}

	unsigned int iEntityId = 0;

	CheckSum(iEntityId, pSelectionGroup->getHeadUnit()->getID());
	CheckSum(iEntityId, (int)eOwner);
	CheckSum(iEntityId, iFlags);

	bool bResult;
	if ( !pToPlot->HaveCachedPathValidityResult( (void*)iEntityId, bCheckNonInvisibleDanger, bResult ) )
	{
		bResult = pathValidInternal(pToPlot, bCheckNonInvisibleDanger, pSelectionGroup, iFlags);

		pToPlot->CachePathValidityResult( (void*)iEntityId, bCheckNonInvisibleDanger, bResult );
	}

	if ( !bCheckNonInvisibleDanger && pbToNodeInvalidity != NULL )
	{
		*pbToNodeInvalidity = !bResult;
	}
	return bResult;
[COLOR="Red"]	}
	else
	{	
	bool bResult = pathValidInternal(pToPlot, bCheckNonInvisibleDanger, pSelectionGroup, iFlags);
	return bResult;	
	}[/COLOR]	
}

or something like that (I still don't know where to place that isHuman check)?
I know there could be a cached part inside bool bCheckNonInvisibleDanger, but maybe this is a start to split between human/AI pathing and pathValidInternal should involve no caching from what I can see.
I'm going to try this and see how it goes, at least it gets compiled. :)
 
Close, but for safety I'd say put that if only directly around this:
Code:
unsigned int iEntityId = 0;

	CheckSum(iEntityId, pSelectionGroup->getHeadUnit()->getID());
	CheckSum(iEntityId, (int)eOwner);
	CheckSum(iEntityId, iFlags);

	bool bResult;
	if ( !pToPlot->HaveCachedPathValidityResult( (void*)iEntityId, bCheckNonInvisibleDanger, bResult ) )
	{
		bResult = pathValidInternal(pToPlot, bCheckNonInvisibleDanger, pSelectionGroup, iFlags);

		pToPlot->CachePathValidityResult( (void*)iEntityId, bCheckNonInvisibleDanger, bResult );
	}
That way pbToNodeInvalidity will still be set when requested and only the caching is disabled for human players.

So something like this:
Code:
	if ( bMoveTerminationChecksOnly && !bCheckNonInvisibleDanger )
	{
		return true;
	}

	bool bResult;

	if (pSelectionGroup->isHuman())
	{
		bResult = pathValidInternal(pToPlot, bCheckNonInvisibleDanger, pSelectionGroup, iFlags);
	}
	else
	{
		unsigned int iEntityId = 0;

		CheckSum(iEntityId, pSelectionGroup->getHeadUnit()->getID());
		CheckSum(iEntityId, (int)eOwner);
		CheckSum(iEntityId, iFlags);

		if (!pToPlot->HaveCachedPathValidityResult((void*)iEntityId, bCheckNonInvisibleDanger, bResult))
		{
			bResult = pathValidInternal(pToPlot, bCheckNonInvisibleDanger, pSelectionGroup, iFlags);

			pToPlot->CachePathValidityResult((void*)iEntityId, bCheckNonInvisibleDanger, bResult);
		}
	}

	if ( !bCheckNonInvisibleDanger && pbToNodeInvalidity != NULL )
	{
		*pbToNodeInvalidity = !bResult;
	}

	return bResult;
 
Well, I'm testing this change from previous post and no OOS so far; but we've not reached Renaissance yet, when this bug started to become annoying in the last game (after we've started exploring with Caravels).
In the meantime, I'm also working on Pitboss. First thing, if I start Pitboss and load either AND or C2C, the result is the same: Pitboss crashes as soon as I get to the server screen (after you've selected options and players), both if I start a new game or if I load an existing one. Minidump points me toward

Code:
	CvPlot* lookatPlot = gDLL->getInterfaceIFace()->getLookAtPlot();

inside void CvGame::update() in CvGame.cpp; so here's what I've tried (changes in red, code is almost the same in AND and C2C, I've taken out only a few lines regarding Super Forts).

Code:
void CvGame::update()
{

	MEMORY_TRACE_FUNCTION();

#ifdef LOG_AI
	gPlayerLogLevel = getBugOptionINT("Autolog__BBAILevel", 0);
	gTeamLogLevel = gPlayerLogLevel;
	gCityLogLevel = gPlayerLogLevel;
	gUnitLogLevel = gPlayerLogLevel;

	if ( gPlayerLogLevel > 0 )
	{
		GC.setXMLLogging(true);
	}
#endif

	startProfilingDLL(false);
[COLOR="Red"]	if ( !gDLL->IsPitbossHost() )
	{[/COLOR]
	CvPlot* lookatPlot = gDLL->getInterfaceIFace()->getLookAtPlot();
	
	if ( lookatPlot != NULL )
	{
		//	Sample th BUG setting in the main thread on entry to game update here (it requires a Python call
		//	so we don't want it happening in background, or more frequently than once per turn slice)
		bool bPagingEnabled = getBugOptionBOOL("MainInterface__EnableGraphicalPaging", true);
		GC.setGraphicalDetailPagingEnabled(bPagingEnabled);

		if ( m_bWasGraphicsPagingEnabled != bPagingEnabled)
		{
			for(int iI = 0; iI < GC.getMapINLINE().numPlotsINLINE(); iI++)
			{
				CvPlot*	pPlot = GC.getMapINLINE().plotByIndexINLINE(iI);
				if ( pPlot != NULL )
				{
					if ( m_bWasGraphicsPagingEnabled )
					{
						pPlot->setShouldHaveFullGraphics(true);
					}
					else
					{
						pPlot->setShouldHaveFullGraphics(false);
					}
				}
			}
		}

		m_bWasGraphicsPagingEnabled = bPagingEnabled;

		if ( GC.getGraphicalDetailPagingEnabled() )
		{
			if ( (m_iLastLookatX != lookatPlot->getX_INLINE() || m_iLastLookatY != lookatPlot->getY_INLINE()) )
			{
				int pageInRange = GC.getGraphicalDetailPageInRange();
				CvPlot::notePageRenderStart((pageInRange*2+1)*(pageInRange*2+1));

				for(int iX = -pageInRange; iX <= pageInRange; iX++)
				{
					for(int iY = -pageInRange; iY <= pageInRange; iY++)
					{
						CvPlot* pPlot = plotXY(lookatPlot->getX_INLINE(),lookatPlot->getY_INLINE(),iX,iY);

						if ( pPlot != NULL )
						{
							pPlot->setShouldHaveFullGraphics(true);
						}
					}
				}

				m_iLastLookatX = lookatPlot->getX_INLINE();
				m_iLastLookatY = lookatPlot->getY_INLINE();
			}

			CvPlot::EvictGraphicsIfNecessary();
		}
	}
[COLOR="Red"]	}[/COLOR]
	//OutputDebugString(CvString::format("Start profiling(false) for CvGame::update()\n").c_str());
	PROFILE_BEGIN("CvGame::update");

	if ( !m_plotGroupHashesInitialized )
	{
		PROFILE("CvGame::update.OneTimeInit");

		//	First opportunity after a load or save is here since sadly the DLL structure
		//	gives us no load/save-completed event
		CvTaggedSaveFormatWrapper&	wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();

		//	Close will free any resources and display any warnings if we've just finished loading/saving
		wrapper.close();

		GC.getLoadedInitCore().checkVersions();

		for(int iI = 0; iI < MAX_PLAYERS; iI++)
		{
			if ( GET_PLAYER((PlayerTypes)iI).isAlive() )
			{
				GET_PLAYER((PlayerTypes)iI).RecalculatePlotGroupHashes();
			}
		}

		m_plotGroupHashesInitialized = true;

		gDLL->getEngineIFace()->clearSigns();
		gDLL->getEngineIFace()->setResourceLayer(GC.getResourceLayer());

		//	Remove and re-add the GW on load
		processGreatWall(false, true, false);
		processGreatWall(true, true);
	}

	{
		PROFILE("CvGame::update.ViewportInit");

		//	Some processing that is done in viewport tranisitions has to occur
		//	over several messaging timeslices (empirically - the game engine
		//	gets things wrong if you don't give it a chnace to process messages
		//	in between).  To enact that we use a state machine which performs state
		//	transitions on each call to CvGame::update().
[COLOR="Red"]	if ( !gDLL->IsPitbossHost() )
	{[/COLOR]		
		GC.getCurrentViewport()->processActionState();
[COLOR="Red"]	}[/COLOR]		
		//	On each time slice put back the signs if they have been temporarily removed
		//	(as happens across a save or a map/viewport switch)
		GC.reprocessSigns();
		//	If a rebuild of the graphics is needed and about a second has gone by since a new refresh was last
		//	requested perform the rebuild (this amalgamates all the rebuild requests pertaining to a move or
		//	a sequence of moves)
		if ( m_lastGraphicUpdateRequestTickCount != -1 && (GetTickCount() - m_lastGraphicUpdateRequestTickCount) > 500 )
		{
			m_lastGraphicUpdateRequestTickCount = -1;

			gDLL->getEngineIFace()->RebuildAllPlots();
			gDLL->getInterfaceIFace()->setDirty(GlobeLayer_DIRTY_BIT, true);
		}
	}

again:
	if (!gDLL->GetWorldBuilderMode() || isInAdvancedStart())
	{
		PROFILE("CvGame::update.Turn");

		sendPlayerOptions();
		// sample generic event
		CyArgsList pyArgs;
		pyArgs.add(getTurnSlice());
		CvEventReporter::getInstance().genericEvent("gameUpdate", pyArgs.makeFunctionArgs());

		if (getTurnSlice() == 0)
		{
			PROFILE("CvGame::update.AutoSave");

			gDLL->getEngineIFace()->AutoSave(true);
		}

		if (getNumGameTurnActive() == 0)
		{
			if (!isPbem() || !getPbemTurnSent())
			{
				doTurn();
			}
		}

		updateScore();

		updateWar();

		updateMoves();

		updateTimers();

		updateTurnTimer();

		AI_updateAssignWork();

		testAlive();

/************************************************************************************************/
/* REVOLUTION_MOD                                                                 lemmy101      */
/*                                                                                jdog5000      */
/*                                                                                              */
/************************************************************************************************/
[COLOR="SeaGreen"]		if ((getAIAutoPlay(getActivePlayer()) <= 0) && !(gDLL->GetAutorun()) && GAMESTATE_EXTENDED != getGameState())[/COLOR]
/************************************************************************************************/
/* REVOLUTION_MOD                          END                                                  */
/************************************************************************************************/
		{
			if (countHumanPlayersAlive() == 0)
			{
				setGameState(GAMESTATE_OVER);
			}
		}

		changeTurnSlice(1);

		if (NO_PLAYER != getActivePlayer() && GET_PLAYER(getActivePlayer()).getAdvancedStartPoints() >= 0 && !gDLL->getInterfaceIFace()->isInAdvancedStart())
		{
			gDLL->getInterfaceIFace()->setInAdvancedStart(true);
			gDLL->getInterfaceIFace()->setWorldBuilder(true);
		}
	}

	reportQueuedEvents();
	doQueuedUIActivity();

	//OutputDebugString(CvString::format("Stop profiling(false) after CvGame::update()\n").c_str());
	CvPlayerAI& kActivePlayer = GET_PLAYER(getActivePlayer());
[COLOR="Red"]	if ( !gDLL->IsPitbossHost() )
	{[/COLOR]		
	if ( (!kActivePlayer.isTurnActive() || kActivePlayer.isAutoMoves()) && !kActivePlayer.hasBusyUnit() && !isNetworkMultiPlayer() &&
		 getBugOptionBOOL("MainInterface__MinimizeAITurnSlices", false) )
	{
		updateTimers();

		goto again;
	}
[COLOR="Red"]	}[/COLOR]
	PROFILE_END();
	stopProfilingDLL(false);

}

First I've tried placing that isPitbossHost only around the line that was supposed to be wrong as per minidump suggestion. But I was getting other errors/crashes. So those other 2 isPitbossHost checks were the least I could to to make it work. But as before, I'm not sure it's a good way to make it work. But now Pitboss doesn't crash anymore and I'm able to connect. Problem is that the game OOS immediately on connection, both on a new game or when loading a save. I'm looking for a way to debug that OOS but I've seen a suspicious getActivePlayer instance in the code above (in green). I call it suspicious because IIRC Pitboss is Player -1 and I'm wondering if that check is responsible for OOS. So, do you have any advice?
 
Nice seeing work on Pitboss.

First advice: Move the last of your red additions one line up, as GET_PLAYER(-1) that that would result in is a bad reference. You don't do anything with the reference so it does not crash, but for clarity it would be better not to call GET_PLAYER on the pitboss player.

Second advice: You are right that the green line is problematic as it accesses memory before the array again (with the pitboss player -1). A network game without human players does not make sense so the autoplay checks should be disabled in a network game with something like that:
Code:
if ((isNetworkMultiPlayer() || ((getAIAutoPlay(getActivePlayer()) <= 0) && !(gDLL->GetAutorun()))) && GAMESTATE_EXTENDED != getGameState())
 
Nice seeing work on Pitboss.

First advice: Move the last of your red additions one line up, as GET_PLAYER(-1) that that would result in is a bad reference. You don't do anything with the reference so it does not crash, but for clarity it would be better not to call GET_PLAYER on the pitboss player.

Second advice: You are right that the green line is problematic as it accesses memory before the array again (with the pitboss player -1). A network game without human players does not make sense so the autoplay checks should be disabled in a network game with something like that:
Code:
if ((isNetworkMultiPlayer() || ((getAIAutoPlay(getActivePlayer()) <= 0) && !(gDLL->GetAutorun()))) && GAMESTATE_EXTENDED != getGameState())

Done, thank you. But the game goes OOS right from the start anyway when using Pitboss, so there's something else somewhere else. :) Unfortunately I don't get any OOS log on the pitboss server; but I've found a post by you and Alrik2002 where you were speaking of logging for pitboss server so I'll add that piece of code into the logger and hopefully I can come up with something. :) I guess we're making progress anyway.
 
Ok, I'm testing this pathing bugfix but I'm not yet sure I have a final result. I had an OOS with a caravel but I'm not 100% sure if it's the same OOS or if it's something else. I'll test further. Something I've encountered from time to time is another OOS on calling

"AI Diplo Declare War Trade"

I had other similar issues so there must be something inside

void CvPlayerAI::AI_doDiplo()

which is a huge function to scan for errors. :(

There's a single line with that string anyway, and it's

Code:
if (GC.getGameINLINE().getSorenRandNum(iDeclareWarTradeRand, "AI Diplo Declare War Trade") == 0)

But I have no clue where to go from there.
 
45°38'N-13°47'E;13085583 said:
Ok, I'm testing this pathing bugfix but I'm not yet sure I have a final result. I had an OOS with a caravel but I'm not 100% sure if it's the same OOS or if it's something else. I'll test further. Something I've encountered from time to time is another OOS on calling

"AI Diplo Declare War Trade"

I had other similar issues so there must be something inside

void CvPlayerAI::AI_doDiplo()

which is a huge function to scan for errors. :(

There's a single line with that string anyway, and it's

Code:
if (GC.getGameINLINE().getSorenRandNum(iDeclareWarTradeRand, "AI Diplo Declare War Trade") == 0)

But I have no clue where to go from there.
I have seen that one before, but it is a hard one as that function depends on a LOT.
 
I have seen that one before, but it is a hard one as that function depends on a LOT.

Yes, I've noticed. Can you tell me if when I get an error pointing me to a specific line like this case, should I look at the code before that line or after? Or is there no way to know if the error is before or after that line? Thank you as usual. :)
 
45°38'N-13°47'E;13086219 said:
Yes, I've noticed. Can you tell me if when I get an error pointing me to a specific line like this case, should I look at the code before that line or after? Or is there no way to know if the error is before or after that line? Thank you as usual. :)
The root cause is before the line to which the difference in the random log points.
 
The root cause is before the line to which the difference in the random log points.

Well, that cuts out 200 over 2800 lines of code... for AI_doDiplo only, of course. :D
 
Top Bottom