OOS problems and how to fix them

45°38'N-13°47'E;13039444 said:
I guess you were talking about this one
http://forums.civfanatics.com/showpost.php?p=12497887&postcount=30
There was an OOS fix by Koshling in rev5588 a few days later, but I guess it was on other multithreading issues, and I'm almost sure I've imported all the relevant code from that rev into AND dll. I'll double check again.

I've checked the code and I've imported everything from rev5588 into AND, except the part on "deferred vote" because that part was causing an issue displaying resolutions on the Victory Screen / Resolution screen and UN or AP Members screen. I couldn't get around it; as a consequence I removed those lines, but I don't think this has anything to do with our current OOS. Everything else was imported correctly.
 
Visibility is possible. That is an OOS problem we had earlier. It happens when not all plots are properly considered for visiibility processing.
And AND currently does have a different calculation there.

Is it something very recent? I'm almost certain I've imported everything from C2C; can you point me at the source file in C2C? I can check myself what's different with our code. Thank you.

Edit: Nevermind, I think I got it, it's in cvunit.cpp and .h, I guess you're referring to

Code:
int CvUnit::visibilityRange(const CvPlot* pPlot) const
in rev6113

I haven't imported that part of the code because I thought it was part of Super Forts, which I've left out of AND. I see what I can do. Thank you again AIAndy, I'll let you know if this works.
 
No, it doesn't work. I've updated visibility code in the dll (I've left out only the part regarding improvement visibility which was made for SuperForts if I'm not mistaken). But I still have OOS when moving some caravel; almost always when my opponent moves her caravels; actually always until now in the last 50 turns or so (turn 290/600 now). The only different thing that comes to my mind is that she has proven the world round and hence she has 1 more movement, it's the only difference I guess between her caravels and mine. I wonder why she goes OOS while I don't logs display her caravel in a different position to what I see, while my caravels don't have this problem.
 
Is it only the last space of movement that causes the problem?

No, I don't think so, because sometimes we were able to clear the OOS by moving the caravel one tile back. So OOS happened before last movement in this case.
 
I've had a look at previous C2C revisions; the only spot were I know something has changed for sea pathing is in rev3789

Optimized sea path generation
Optimized explorer plot search
Optimized handling of long moves generally

a function has been added

Code:
void	CvPath::trimBefore(const CvPlot* pPlot)
{
	CvPathNode*	pNode = m_startNode;

	while( pNode != NULL )
	{
		if ( pNode->m_plot == pPlot )
		{
			m_startNode = pNode;
			break;
		}

		pNode = pNode->m_firstChild;
	}
}

and also this part of code in

bool CvPathGenerator::generatePath

has been changed from

Code:
bool	CvPathGenerator::generatePath(const CvPlot* pFrom, const CvPlot* pTo, CvSelectionGroup* pGroup, int iFlags, int iMaxTurns)
{
	CvPathNode*	root;

	PROFILE_FUNC();

	m_bFoundRoute = false;

	m_generatedPath.Set(NULL);
	m_iSeq++;

	unsigned int iGroupMembershipChecksum = 0;
	CLLNode<IDInfo>* pUnitNode;
	CvUnit* pLoopUnit;

	pUnitNode = pGroup->headUnitNode();

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

		CheckSum(iGroupMembershipChecksum, pLoopUnit->getID());
	}

	if ( m_currentGroupMembershipChecksum != iGroupMembershipChecksum || m_iFlags != iFlags )
	{
		CvSelectionGroup::setGroupToCacheFor(pGroup);

		if ( !pGroup->AI_isControlled() )
		{
			m_useAIPathingAlways = getBugOptionBOOL("MainInterface__UseAIPathing", false);
		}
	}

	bool bRequiresWar;

	if ( !m_TerminusValidFunc(pGroup, pTo->getX_INLINE(), pTo->getY_INLINE(), iFlags, bRequiresWar) )
	{
		return false;
	}

	if ( bRequiresWar )
	{
		iFlags |= MOVE_TERMINUS_DECLARES_WAR;
	}

#ifdef TRACE_PATHING
	OutputDebugString(CvString::format("Generate path from (%d,%d) to (%d,%d)\n", pFrom->getX_INLINE(), pFrom->getY_INLINE(), pTo->getX_INLINE(), pTo->getY_INLINE()).c_str());
#endif

	if ( m_iTurn != GC.getGameINLINE().getGameTurn() || m_currentGroupMembershipChecksum != iGroupMembershipChecksum || m_iFlags != iFlags || pFrom != m_pFrom )
	{
		m_nodeAllocationPool->reset();
		m_plotInfo->reset();
		m_iTurn = GC.getGameINLINE().getGameTurn();

		m_pReplacedNonTerminalNode = NULL;
		m_pBestTerminalNode = NULL;
	}

to

Code:
bool	CvPathGenerator::generatePath(const CvPlot* pFrom, const CvPlot* pTo, CvSelectionGroup* pGroup, int iFlags, int iMaxTurns)
{
	CvPathNode*	root;

	PROFILE_FUNC();

	m_bFoundRoute = false;

	m_iSeq++;

	unsigned int iGroupMembershipChecksum = 0;
	CLLNode<IDInfo>* pUnitNode;
	CvUnit* pLoopUnit;

	pUnitNode = pGroup->headUnitNode();

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

		CheckSum(iGroupMembershipChecksum, pLoopUnit->getID());
	}

	if ( m_currentGroupMembershipChecksum != iGroupMembershipChecksum || m_iFlags != iFlags )
	{
		CvSelectionGroup::setGroupToCacheFor(pGroup);

		if ( !pGroup->AI_isControlled() )
		{
			m_useAIPathingAlways = getBugOptionBOOL("MainInterface__UseAIPathing", false);
		}
	}

	bool bRequiresWar;

	if ( !m_TerminusValidFunc(pGroup, pTo->getX_INLINE(), pTo->getY_INLINE(), iFlags, bRequiresWar) )
	{
		m_generatedPath.Set(NULL);
		return false;
	}

	if ( bRequiresWar )
	{
		iFlags |= MOVE_TERMINUS_DECLARES_WAR;
	}

#ifdef TRACE_PATHING
	OutputDebugString(CvString::format("Generate path from (%d,%d) to (%d,%d)\n", pFrom->getX_INLINE(), pFrom->getY_INLINE(), pTo->getX_INLINE(), pTo->getY_INLINE()).c_str());
#endif
[COLOR="Red"]
	bool bSameGroup = (m_iTurn == GC.getGameINLINE().getGameTurn() && m_currentGroupMembershipChecksum == iGroupMembershipChecksum && m_iFlags == iFlags);

	//	Optimize the case where we'e just stepping along the previously calculated path (as continueMission() does)
	if ( bSameGroup && m_generatedPath.lastPlot() == pTo )
	{
		if ( m_generatedPath.containsNode(pFrom) )
		{
			bool bValid = true;

			m_generatedPath.trimBefore(pFrom);

			//	Validate we can still follow this path (visibility may have changed)
			for(CvPath::const_iterator itr = m_generatedPath.begin(); itr != m_generatedPath.end(); ++itr)
			{
				if ( itr.plot() != pFrom && !moveToValid(pGroup, itr.plot(), iFlags) )
				{
					bValid = false;
					break;
				}
			}

			if ( bValid )
			{
				return true;
			}
		}
	}

	m_generatedPath.Set(NULL);

	if ( !bSameGroup || pFrom != m_pFrom )[/COLOR]
	{
		m_nodeAllocationPool->reset();
		m_plotInfo->reset();
		m_iTurn = GC.getGameINLINE().getGameTurn();

		m_pReplacedNonTerminalNode = NULL;
		m_pBestTerminalNode = NULL;
	}

if this can be of any help. I guess changes have been made in other files too in that revision.
 
That could well be the culprit, as it causes path reusage on the computer of the player that planned a move and then executed it a bit later in the simultaneous turn.
And I guess it is not certain that that would be entirely the same path calculated on the other computer if something happened in the meantime.
As a solution I would suggest to add a check to
if ( bSameGroup && m_generatedPath.lastPlot() == pTo )
that pGroup is not controlled by a human player.
 
We have a potential fix coming for ANOTHER OOS? VERY cool! Guys... this thread is an absolute miracle!
@AIAndy: Could you take care of that adjustment for us this time? I've got some other things demanding attention here!
 
We have a potential fix coming for ANOTHER OOS? VERY cool! Guys... this thread is an absolute miracle!
@AIAndy: Could you take care of that adjustment for us this time? I've got some other things demanding attention here!
I committed the source code change now (without compiling the DLL).
 
Awesome that OOS now has a dedicated thread.

Should I post my future OOS logs here instead of there?

@AIAndy: If you haven't already, could you take a look at a particular OOS we had? It's logs are found in the link; [OOS-2, OOS-3, OOS-7, OOS-8, OOS-12-13] are the logs representing this OOS.
It's reproducible so the cause should not be too hard to find.
 
Great! I can test probably tomorrow if it works. But I have noted there's another part of the code almost identical to the one you changed about 1000 lines before your change, so I guess we have to add a isHuman check there too, correct?

Edit: sorry I can't be more precise but I'm not near my pc now.
 
I committed the source code change now (without compiling the DLL).

Cool! That works for me as it'll thus just get integrated into the next build. Thanks!:D
 
I've made some test and that position OOS is still there. :(
But I got another idea if it can help. From the logs I've seen that it's kinda like one of the players is lagging behind of one or more moves. I mean, my pc shows my opponents caravel in a certain position, while my opponent's pc shows the same caravel further along the projected path for that turn. If my opponent is able to move back to the position logged on my pc, that OOS is cleared. Same happens if the caravel is mine. What I've been thinking of is: position of a unit is definitely a global issue, while probably remaining moves are a local issue. In fact, the OOS is cleared moving that unit back because then position is the same for both pc,but moves left should then be different on both pc because on mine that caravel hasn't moved beyond that critical point,while on my opponent's pc that caravel has moved back and forth. So my point is: could there be a spot in the code where movements left and positions are mixed up? Since one is something global while the other is something local this could definitely lead to OOS. What do you think?
 
The # of moves differs at the end of the turn but since it resets during the beginning of the turn sequence its not keeping the game out of synch if they're on the same position. The # of moves total doesn't seem to differ in the displays from one computer to the next from what I can tell. So the error must be somewhere between plots. Perhaps one computer continues after a pause while another does not based on the difference in a getActivePlayer check (which should be found and changed to getOwner check for that unit) somewhere during the move sequence between plots.
 
The # of moves differs at the end of the turn but since it resets during the beginning of the turn sequence its not keeping the game out of synch if they're on the same position. The # of moves total doesn't seem to differ in the displays from one computer to the next from what I can tell. So the error must be somewhere between plots. Perhaps one computer continues after a pause while another does not based on the difference in a getActivePlayer check (which should be found and changed to getOwner check for that unit) somewhere during the move sequence between plots.

Ok, I'm hunting down those ActivePlayer entries, starting from CvPlot and CvPathgenerator and connected cpp files. Here's what I've found which COULD be connected to our problem (keep in mind that I don't know if this use of getActivePlayer is legit or not):

in CvPlot.cpp

Code:
[COLOR="Red"]void CvPlot::updateCenterUnit()[/COLOR]
{
	PROFILE_FUNC();

	CvUnit* pOldCenterUnit = getCenterUnit();
	CvUnit* pNewCenterUnit;

	if ( m_bInhibitCenterUnitCalculation || !shouldHaveFullGraphics() )
	{
		//	Because we are inhibitting recalculation until all the adjusting code has run
		//	(rather than have it update multiple times) the currently cached center unit
		//	might become an invalid pointer in the interim.  To protect against this we unconditionally
		//	set the recorded center unit to NULL (without marking the plot dirty which would force a repaint).
		//	This makes the interim safe state, and the correct value will be calculated once the
		//	inhibitted section is exited
		m_pCenterUnit = NULL;
		return;
	}
	else
	{
		pNewCenterUnit = NULL;
	}

	if (!isActiveVisible(true))
	{
		pNewCenterUnit = NULL;
	}
	else
	{
		pNewCenterUnit = getSelectedUnit();

		if (pNewCenterUnit != NULL && !pNewCenterUnit->atPlot(this))
		{
			pNewCenterUnit = NULL;
		}

		if (pNewCenterUnit == NULL)
		{
			pNewCenterUnit = getBestDefender(GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR](), NO_PLAYER, NULL, false, false, true);
		}

		if (pNewCenterUnit == NULL)
		{
			pNewCenterUnit = getBestDefender(GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]());
		}

		if (pNewCenterUnit == NULL)
		{
			pNewCenterUnit = getBestDefender(NO_PLAYER, GC.getGameINLINE().[COLOR="Red"]getActivePlaye[/COLOR]r(), gDLL->getInterfaceIFace()->getHeadSelectedUnit(), true);
		}

		if (pNewCenterUnit == NULL)
		{
			pNewCenterUnit = getBestDefender(NO_PLAYER, GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR](), gDLL->getInterfaceIFace()->getHeadSelectedUnit());
		}

		if (pNewCenterUnit == NULL)
		{
			pNewCenterUnit = getBestDefender(NO_PLAYER, GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]());
		}
	}

	if ( pNewCenterUnit != pOldCenterUnit )

etc.etc (probably not relevant)

and further down:

Code:
[COLOR="Red"]void CvPlot::updateFlagSymbol()[/COLOR]
{
	PROFILE_FUNC();

	if ( !shouldHaveFullGraphics() )
	{
		return;
	}

	PlayerTypes ePlayer = NO_PLAYER;
	PlayerTypes ePlayerOffset = NO_PLAYER;

	CvUnit* pCenterUnit = getCenterUnit();

	//get the plot's unit's flag
	//The plot check is to account for units in the delayed-death cycle
	if (pCenterUnit != NULL && pCenterUnit->plot() == this)
	{
		//OutputDebugString(CvString::format("Updating flag symbol for (%d,%d).  Center unit is %08lx\n", m_iX, m_iY, pCenterUnit).c_str());
		ePlayer = pCenterUnit->getVisualOwner();
	}

	//get moving unit's flag
	if (gDLL->getInterfaceIFace()->getSingleMoveGotoPlot() == this)
	{
		if(ePlayer == NO_PLAYER)
		{
			ePlayer = GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]();
		}
		else
		{
			ePlayerOffset = GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]();
		}
	}

Then I've found somethine else a bit suspicious to my untrained eyes in CvSelectionGroup.cpp:

Code:
[COLOR="Red"]void CvSelectionGroup::updateMission[/COLOR]()
{
	FAssert(getOwnerINLINE() != NO_PLAYER);

	if (getMissionTimer() > 0)
	{
		changeMissionTimer(-1);

		if (getMissionTimer() == 0)
		{
			if (getActivityType() == ACTIVITY_MISSION)
			{
				continueMission();
			}
			else
			{
				if (getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]())
				{
					if (gDLL->getInterfaceIFace()->getHeadSelectedUnit() == NULL)
					{
						gDLL->getInterfaceIFace()->changeCycleSelectionCounter(1);
					}
				}
			}
		}
	}
}

I call it suspicious because looking at

Code:
[COLOR="Red"]bool CvSelectionGroup::startMission[/COLOR]()
{
	PROFILE_FUNC();

	CLLNode<IDInfo>* pUnitNode;
	CvUnit* pLoopUnit;
	bool bDelete;
	bool bAction;
	bool bNuke;
	bool bNotify;
	bool bResult;

	FAssert(!isBusy());
	FAssert(getOwnerINLINE() != NO_PLAYER);
	FAssert(headMissionQueueNode() != NULL);

	[COLOR="Red"]if (!GC.getGameINLINE().isMPOption(MPOPTION_SIMULTANEOUS_TURNS))[/COLOR]
	{
		if (!GET_PLAYER(getOwnerINLINE()).isTurnActive())
		{
			if (getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]())
			{
				if (IsSelected())
				{
					gDLL->getInterfaceIFace()->changeCycleSelectionCounter(1);
				}
			}

			return false;
		}
	}

you can see that there's a call for getActivePlayer but there's also a check for MPOPTION_SIMULTANEOUS_TURNS. Shouldn't there be a similar check for UpdateMission too?
Then there's a call for getActivePlayer in bool CvSelectionGroup::continueMission(int iSteps)

Code:
	if ((getNumUnits() > 0) && (headMissionQueueNode() != NULL))
	{
		if (bAction)
		{
			if (bDone || !canAllMove())
			{
				if (plot()->isVisibleToWatchingHuman())
				{
					updateMissionTimer(iSteps);

					if (showMoves())
					{
						if (GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]() != NO_PLAYER)
						{
							if (getOwnerINLINE() != GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]())
							{
								if (plot()->isActiveVisible(false) && !isInvisible(GC.getGameINLINE().getActiveTeam()) && plot()->isInViewport())
								{
									gDLL->getInterfaceIFace()->lookAt(plot()->getPoint(), CAMERALOOKAT_NORMAL);
								}
							}
						}
					}
				}
			}
		}

		if (bDone)
		{
			if (!isBusy())
			{
				if (getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]())
				{
					if (IsSelected())
					{
						if ((headMissionQueueNode()->m_data.eMissionType == MISSION_MOVE_TO) ||
// BUG - Sentry Actions - start
#ifdef _MOD_SENTRY
							(headMissionQueueNode()->m_data.eMissionType == MISSION_MOVE_TO_SENTRY) ||
#endif
// BUG - Sentry Actions - end
							(headMissionQueueNode()->m_data.eMissionType == MISSION_ROUTE_TO) ||
							(headMissionQueueNode()->m_data.eMissionType == MISSION_MOVE_TO_UNIT))
						{
							gDLL->getInterfaceIFace()->changeCycleSelectionCounter((GET_PLAYER(getOwnerINLINE()).isOption(PLAYEROPTION_QUICK_MOVES)) ? 1 : 2);
						}
					}
				}

and few lines later

Code:
		else
		{
			if (canAllMove())
			{
				//	If the recursion fails that is not an overall failure, since this step did
				//	something
				continueMission(iSteps + 1);
			}
			else if (!isBusy())
			{
				if (getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]())
				{
					if (IsSelected())
					{
						gDLL->getInterfaceIFace()->changeCycleSelectionCounter(1);
					}
				}
			}
		}
	}

	return !bFailed;
}

Further down there's an entry in CvSelectionGroup::updateMissionTimer(int iSteps)

Code:
		if (isHuman() && (isAutomated() || (GET_PLAYER((GC.getGameINLINE().isNetworkMultiPlayer()) ? getOwnerINLINE() : GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]()).isOption(PLAYEROPTION_QUICK_MOVES))))
		{
			iTime = std::min(iTime, 1);
		}
	}

And some more entries here

Code:
void CvSelectionGroup::clearMissionQueue()
{
	FAssert(getOwnerINLINE() != NO_PLAYER);

	deactivateHeadMission();

	m_missionQueue.clear();

	if ((getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]()) && IsSelected())
	{
		gDLL->getInterfaceIFace()->setDirty(Waypoints_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
	}
}

here

Code:
bool CvSelectionGroup::insertAtEndMissionQueue(MissionData mission, bool bStart)
{
	PROFILE_FUNC();

	FAssert(getOwnerINLINE() != NO_PLAYER);

	m_missionQueue.insertAtEnd(mission);

	if ((getLengthMissionQueue() == 1) && bStart)
	{
		if ( !activateHeadMission())
		{
			return false;
		}
	}

	if ((getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]()) && IsSelected())
	{
		gDLL->getInterfaceIFace()->setDirty(Waypoints_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
	}

	return true;
}

here

Code:
CLLNode<MissionData>* CvSelectionGroup::deleteMissionQueueNode(CLLNode<MissionData>* pNode)
{
	CLLNode<MissionData>* pNextMissionNode;

	FAssertMsg(pNode != NULL, "Node is not assigned a valid value");
	FAssert(getOwnerINLINE() != NO_PLAYER);

	if (pNode == headMissionQueueNode())
	{
		deactivateHeadMission();
	}

	pNextMissionNode = m_missionQueue.deleteNode(pNode);

	if (pNextMissionNode == headMissionQueueNode())
	{
		activateHeadMission();
	}

	if ((getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]()) && IsSelected())
	{
		gDLL->getInterfaceIFace()->setDirty(Waypoints_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(SelectionButtons_DIRTY_BIT, true);
		gDLL->getInterfaceIFace()->setDirty(InfoPane_DIRTY_BIT, true);
	}

	return pNextMissionNode;
}

and finally here

Code:
void CvSelectionGroup::deactivateHeadMission()
{
	FAssert(getOwnerINLINE() != NO_PLAYER);

	if (headMissionQueueNode() != NULL)
	{
		if (getActivityType() == ACTIVITY_MISSION)
		{
			setActivityType(ACTIVITY_AWAKE);
		}

		setMissionTimer(0);

		if (getOwnerINLINE() == GC.getGameINLINE().[COLOR="Red"]getActivePlayer[/COLOR]())
		{
			if (IsSelected())
			{
				gDLL->getInterfaceIFace()->changeCycleSelectionCounter(1);
			}
		}
	}
}

As I've said, I've no idea if one or all of those entries are correct or not. I've only pointed out where getActivePlayer is used, possibly in conjuction with units movement or position. Does it help any of you in understanding where the problem could be?
 
Those all look like local graphics changes to me which is why they use the active player to make a local (async) statement. So no, I don't think those are responsible for the problem.

Is the movement amount displayed when you hover over the problematic units the same on both computers?

If the ships move further on the computer of the one moving the ships than on the other computer this might be a false cancelling of the mission before the end of the path is reached.
 
Those all look like local graphics changes to me which is why they use the active player to make a local (async) statement. So no, I don't think those are responsible for the problem.

Is the movement amount displayed when you hover over the problematic units the same on both computers?

If the ships move further on the computer of the one moving the ships than on the other computer this might be a false cancelling of the mission before the end of the path is reached.

Well, I'm back again testing this OOS problem. It looks like you might be right about a false cancelling of the mission. Here's an example of what happened (see screenshot): my wife's ship was north of that barbarian ship, she was coming from north and stopped there; she had 2 movements left so she commanded her ship to move 2 tiles south, right there where you can see it in the screenshot near Nicaea (ship moved one tile southwest, then one tile southeast, the city wasn't in sight before that move, although its border were). That's when the OOS happened. Randomlogs are identical, while OOS logs show

Code:
Player 1, Unit ID: 65553, Galley
X: 8, [COLOR="Red"]Y: 34[/COLOR]
Damage: 0
Experience: 0
Level: 1
Promotions:

on my pc and

Code:
Player 1, Unit ID: 65553, Galley 1 (Bibracte)
X: 8, [COLOR="Red"]Y: 32[/COLOR]
Damage: 0
Experience: 0
Level: 1
Promotions:

on hers. As you can see, it looks like the ship moved on her pc only. Y: 34 is the correct position shown in the screenshot (taken on her pc), but while on her pc that position is correct, logs indicate that on my pc that ship was always 1 tile north of the barbarian ship. Probably when it happened other times that OOS was always caused by presence of other units (barbarian or not) or by presence of storms.

I'm wondering if the problem could be in cvreachableplotset.cpp since there are some entries in the SVN logs explaining that changes have been made to sea pathing in that file.
 

Attachments

  • Civ4ScreenShot0000.JPG
    Civ4ScreenShot0000.JPG
    125.4 KB · Views: 58
I have experienced this OOS many times. It always happens when an interrupted go-to order is reissued to the same destination. if you change the destination or move tile by tile it doesn't happen. I have both experienced it with boats and land units, and with/without any cultural borders involved.
Seems like the other computer skips the path calculation bit thinking it has already done it earlier when the same destination is ordered the second time, which would mean that it also cancels the second order due to the old path goes through the obstacle that made it cancel the first time.

Hope the information helps but don't take my speculations too seriously as I'm no programmer.
 
I have experienced this OOS many times. It always happens when an interrupted go-to order is reissued to the same destination. if you change the destination or move tile by tile it doesn't happen. I have both experienced it with boats and land units, and with/without any cultural borders involved.
Seems like the other computer skips the path calculation bit thinking it has already done it earlier when the same destination is ordered the second time, which would mean that it also cancels the second order due to the old path goes through the obstacle that made it cancel the first time.

Hope the information helps but don't take my speculations too seriously as I'm no programmer.
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.
 
Top Bottom