Signs going away

God-Emperor

Deity
Joined
Jul 18, 2009
Messages
3,551
Location
Texas
As nearly everyone is aware, signs have a tendency to disappear after a while whether added manually or by the "event signs" BUG option or the "personalized map" game option.

I have not fixed this, but I have noticed something.
EDIT: OK, so a few posts down (#5) I have a partial fix. It is just a Python adjustment.


The cvInternalGlobals::reprocessSigns function apparently does nothing at all. It looks like it is doing something, but the net result is nothing.

This is the function:
Code:
void cvInternalGlobals::reprocessSigns(void)
{
	if ( m_bSignsCleared )
	{
		PYTHON_ACCESS_LOCK_SCOPE

		CyArgsList argsList;
		CyPlot* pyPlot = new CyPlot(NULL);
		argsList.add(gDLL->getPythonIFace()->makePythonObject(pyPlot));
		argsList.add(NO_PLAYER);
		argsList.add("");
		PYTHON_CALL_FUNCTION(__FUNCTION__, PYCivModule, "AddSign", argsList.makeFunctionArgs());
		delete pyPlot;

		m_bSignsCleared = false;
	}
}

So what this does one of two things.

First, reprocessSigns may do nothing due to m_bSignsCleared being false. The only ways it is set to true is by the function right before this one: cvInternalGlobals::clearSigns. That is not called in many places, just on initializing things, viewport changes, and in CvInitCore::write. That last one is possibly the important one since it may be called during an autosave. If that is the case, then the signs are probably disappearing every time an autosave is done or the viewport moves (if using viewports). In theory, I think it is then relying on reprocessSigns to put them back, which should be happening in the second thing that can happen:

Second, reprocessSigns can call the AddSign function in the CvAppInterface.py file (since that is the one which PYCivModule points to).

When calling the AddSign function the arguments it passes are a CyPlot that does not point to a real plot, the NO_PLAYER value (which is the integer -1), and an empty text string.

In CvAppInterface the AddSign function just calls the AddSign function in the EventSigns.py file, passing it the same data.

This is that function:
Code:
def addSign (pPlot, ePlayer, szCaption):
	""" Wrapper for CyEngine.addSign() which stores sign data. 
	If -1 is passed for ePlayer, the sign is assumed to be a landmark that everyone can see.
	"""
	#BugUtil.debug("EventSigns.addSign(pPlot = %s, ePlayer = %s, szCaption = %s)" % (str(pPlot), str(ePlayer), szCaption))
	[B][COLOR="DarkRed"]if not pPlot or pPlot.isNone():
		BugUtil.warn("EventSigns.addSign() was passed an invalid plot: %s" % (str(pPlot)))
		return False[/COLOR][/B]
	if gSavedSigns == None:
		BugUtil.warn("EventSigns.addSign() gSavedSigns is not initialized!")
		return False
	gSavedSigns.storeSign(pPlot, ePlayer, szCaption)
	gSavedSigns.displaySign(pPlot, ePlayer)
	SdToolKit.sdSetGlobal(SD_MOD_ID, SD_VAR_ID, gSavedSigns)
	return True

The important part is what I have set to be bold red text. Since the PyPlot which is passed does not point to a real plot the call to pPlot.isNone will be True so this first highlighted "if" will take effect and all it does it add a message to a log file if the BUG message logging is set to show warnings and then exit the function, returning the value False.

So even if m_bSignsCleared is true reprocessSigns still does nothing (well, it wastes a little time since it calls a Python function which calls another one which checks a value and then maybe logs something and exits).

It looks to me like this was supposed to do something else, by nobody ever got around to having it do whatever it is supposed to do. Probably because figuring out how to make it do what it should do when a viewport moves takes a lot of messing around since it saves signs by plot location (a tuple of (x,y) coordinates is the index) and what actual plot each location really is changes when the viewport moves.

It is possible that the fix could be as simple as having the AddSign in EventSigns.py check for this specific set of arguments and if found regenerate the signs from the "gSavedSigns" global's data (something like "gSavedSigns.processSigns(g_bShowSigns)", if gSavedSigns is not null). But I think it may be more complicated because there may also be some issue with saving the data, otherwise all the signs should probably show up for at least the first turn every time you load a save.

Or this might all be a red herring...

I may be fiddling with this a bit more to at least check my possible simple fix.
 
To me it looks like the save is clearing the data about the event signs not saving it, but that is just from testing not looking at the code.
 
All the signs come back when you reload the game, even the ones from events (oh don't know about manually added ones - I don't think I've added any).

And the placenames (for Personalized Map) are still on the mouseover. Could a routine be written for the end of every save that 'retrieves' the text from wherever the mouseover is getting it from, and recreates it as a sign?
 
I guess I've kind of solved the problem in AND a couple of months ago, at least for signs added automatically (while I've never been able to place a sign myself, I don't know why it doesn't work); at least I've solved it if Viewports are off, I think it doesn't work when you're using Viewports. I've simply removed a line in

Code:
void CvInitCore::write(FDataStreamBase* pStream)
{
	CvTaggedSaveFormatWrapper&	wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();

	//	Need to clear signs across a save to stop the game engine persisting them, since it only sees
	//	those within the current viewport (and in viewport coordinates).  They are actually persisted via
	//	Python anyway
[COLOR="Red"]//	GC.clearSigns();[/COLOR]
 
45°38'N-13°47'E;13138918 said:
I've simply removed a line in

Code:
void CvInitCore::write(FDataStreamBase* pStream)
{
	CvTaggedSaveFormatWrapper&	wrapper = CvTaggedSaveFormatWrapper::getSaveFormatWrapper();

	//	Need to clear signs across a save to stop the game engine persisting them, since it only sees
	//	those within the current viewport (and in viewport coordinates).  They are actually persisted via
	//	Python anyway
[COLOR="Red"]//	GC.clearSigns();[/COLOR]

That would work if it wasn't for viewports.

It turns out that the comment in that code is only partially correct. The signs placed by the landmarks and event signs and the personalized map are done via the Python and saved there, but get wiped off the map by the GC.clearSigns() in the code above when an autosave is done and nothing ever puts them back except loading a save. The signs that are placed by hand via Alt-S do not use the Python and therefore do not get saved and are just completely wiped out of existence when that GC.clearSigns() is done (or any of the other ways of calling it or a direct call to "gDLL->getEngineIFace()->clearSigns()", of which there are not very many but more than none). Those manually added signs are done via the "sign map mode" and appear to be handled by the game engine with no involvement from the DLL or Python.

So I have a partial fix. No commenting out that line in the CvInitCore::write necessary, but it only fixes the signs other than those added via Alt-S.

The trick is that the cvInternalGlobals::reprocessSigns does end up using the addSigns function in EventSigns.py, passing NULL as the plot, NO_PLAYER as the player, and an empty string as the text. So this alteration to the addSigns function will spot a call that matches this and cause a refresh of the signs to be done.
Code:
def addSign (pPlot, ePlayer, szCaption):
	""" Wrapper for CyEngine.addSign() which stores sign data. 
	If -1 is passed for ePlayer, the sign is assumed to be a landmark that everyone can see.
	"""
	#BugUtil.debug("EventSigns.addSign(pPlot = %s, ePlayer = %s, szCaption = %s)" % (str(pPlot), str(ePlayer), szCaption))
	# G-E fix: if not pPlot or pPlot.isNone():
	if not pPlot:
		BugUtil.warn("EventSigns.addSign() was passed an invalid plot")
		return False
	if gSavedSigns == None:
		BugUtil.warn("EventSigns.addSign() gSavedSigns is not initialized!")
		return False
	if pPlot.isNone(): # G-E fix
		if ePlayer == -1 and len(szCaption) == 0:
			global g_bForceUpdate
			g_bForceUpdate = True
			return True
		BugUtil.warn("EventSigns.addSign() was passed an invalid plot: isNone = True")
		return False
	gSavedSigns.storeSign(pPlot, ePlayer, szCaption)
	gSavedSigns.displaySign(pPlot, ePlayer)
	SdToolKit.sdSetGlobal(SD_MOD_ID, SD_VAR_ID, gSavedSigns)
	return True
Using this code, the signs labeling forests/peaks/jungles/bays/etc. from the Personalized Map game option did not disappear. They occasionally do have a slight flicker at the start of a turn where an autosave happens. It mgith be possible to avoid that by directly doing a "gSavedSigns.processSigns(g_bShowSigns)" instead of setting g_bForceUpdate to True (which does the sign processing in the onBeginActivePlayerTurn event handler instead if doing it immediately), but I didn't check.

Assuming the viewport related part of the comment is correct (and it probably is) then removing the call to GC.clearSigns is likely to mess things up if you use viewports. It looks like the only really good way to save the user's signs may be to either
1) Tell the Python to check all the signs before deleting them in the GC.clearSigns function by calling the AddSigns Python with some specific set of arguments that means "check the signs to find any that you don't already know about". Then add the code to the addSigns in EventSigns.py to do this, of course. It may also be necessary to convert some of the remaining direct calls to gDLL->getEngineIFace()->clearSigns() into calls to GC.clearSigns.
or
2) store them in the DLL with global (non-viewport) coordinates, and put (just) those into the save in there and also restore any that are in the current viewport in cvInternalGlobals::reprocessSigns.

Well, there is probably a 3rd option. If this can be done, and it should be possible, it would probably be the best solution: It may be possible to add a key press handler for Alt-S which works entirely in Python to handle adding a sign. It would require having it highlight plots as the mouse moves over them and pick a plot which is clicked on (this ought to be doable using code similar to the dot mapping tool in BUG), then show a pop-up that gets the text, and finally apply it by calling the EventSigns.addSign code after which it tells the DLL that it has handled the input (by returning True from the event handler) so that it never actually uses the "sign mode" of the UI since it never processes the Alt-S anywhere but the Python.

Um... I just noticed that the bug CvOverlayScreenUtils.py file has some commented out stuff, and some stuff that isn't commented out, it calls the "as-yet unused sign overlay". That stuff was supposed to be a little more complex, apparently allowing for selection of an icon as well as text, but it is unfinished.

So: The second one may be slightly quicker, but the first one stores them all together using one method instead of saving them in two parts in two different ways and is probably better than the first one. The third one would be best.
 
Um... I kinda follow all this (and it strikes me as being overly complex in the initial setup which would be imo the cause of the problem. If the sign was saved as part of the plot data and saved along with a setting of what player has visible access to the sign (with NO_PLAYER being all players) I'd think it'd be much simpler but then sometimes more advanced setups like these lose me as to the reason for it being done in the manner it is.)

So... since I'm not totally following what needs to be done in the dll and DH has done his side of things in python, I'd like to request that the coding as it should be imported into the dll be posted so I can simply update it into place.
 
Top Bottom