So it seems that the only potential advantage of an IDInfo data member over a CvCity pointer is that the invalidation of the IDInfo member can be delayed a little bit when a CvCity is destroyed. It mustn't be delayed long enough for another CvCity to be created because that city's FFreeList ID could clash with the destroyed city. I guess the code is currently taking advantage of this by not clearing the working city directly in CvCity::kill. Instead, setWorkingCityOverride(NULL) is called directly, which in turn calls CvPlot::updateWorkingCity, which then clears m_workingCity. Except that it doesn't - which is the bug.
I feel that this whole semi-delayed update idea (if that even was the idea) is ill-conceived and that IDInfo data members should be replaced with CvCity*. IDInfo will suggest to programmers that they don't need to update upon kill, and that's -largely- incorrect. Also leads to unnecessary overhead for lookup (though much of that can be optimized away). And just for the sake of muddling update and reset into a single function. CvCity:kill already directly calls set-NULL on the other two IDInfo members (m_plotCity, m_workingCityOverride), should be easy enough to replace those with CvCity*. And a set function can be added for m_workingCity as well, to be called by CvCity::kill before setWorkingCityOverride:
Not yet tested; I thought I'd first check whether someone here sees a problem with this approach.
We also have ...
Code:
IDInfo m_combatUnit;
IDInfo m_transportUnit;
... at CvUnit, and one IDInfo at each CvCityAI and CvSelectionGroupAI:
IDInfo m_routeToCity;
IDInfo m_missionAIUnit;
When a unit is killed, it can't easily remove itself as everyone else's combat unit because that would require looping through all units, which is somewhat costly. My impression is that the code guarantees that m_combatUnit gets cleared at the end of combat resolution, so there is probably no problem here, i.e. also no problem with replacing IDInfo with a CvUnit*. m_transportUnit is handled by CvUnit::kill – conveniently, all units that have the dying unit as their transport unit get killed themselves.
m_routeToCity gets updated once per player turn (in CvCityAI::AI_doTurn):
No
pBestCity == AI_getRouteToCity
check, so this update should succeed even when there has been a collision of FFreeList IDs. However, a collision could perhaps lead to incorrect AI behavior for some portion of a game turn. Probably not possible, but, to be on the safe side and to be consistent, CvCity::kill should arguably just loop through all cities of the dying city's owner and explicitly reset the route-to-city.
m_missionAIUnit is a tough case. Seems to get used exclusively for MISSION_MOVE_TO_UNIT in BtS (K-Mod has a couple of additional uses), and only seems to get updated (overwritten) when a new mission is pushed. Meaning that, when a unit dies, AI units that were moving toward that unit will rely for several turns on the FFreeList ID not having been reassigned. Seems that the only clean remedy is a costly loop over all (friendly?) selection groups whenever a unit dies (which, of course, isn't such a frequent occurrence) or a bidirectional link, i.e. units being aware of all groups that are flocking to them. That might even perform worse due to increased memory use. Given that a clash of IDs is going to be very rare and that the consequences aren't catastrophic, perhaps it's best to leave this case alone - apart from some warnings in comments.