Don’t modify tuples that others may already have references to #101
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Short Version
Due to a bug in Cyan’s code, setting individual entries of a multi-value SDL variable from Python (ptSDL.setIndex()) has only ever worked by chance. All current uses of it happened to work with Python 2.3, but some break with Python 2.7. Symptoms fixed by the attached commit include broken private chat functionality in the neighborhood egg room and inability to enter Teledahn buckets.
Long Version
As D'Lanor discovered, entering an egg room cubicle with the H-uru client (Python 2.7) causes the following error log, and chat is not made private as it should be:
It turns out this backtrace is a bit misleading, the error does not happen in PtDebugPrint(). What fails is the middle one of these lines:
SDL["intSDLChatMembers"] is a tuple (8-tuple in this case), and SDL.setIndex() (plPythonSDLModifier::SetItemIdx()) modifies that tuple directly. This is a big no-no, because tuples are supposed to be immutable. The C API to modify tuples only exists because there must be a way to build tuples in the first place, but once they’re out in the wild and someone else might already have references to them, it mustn’t be used anymore. This is Cyan’s code and unchanged between CWE, CWE-ou, and H-uru/Plasma.
The attempt to modify the tuple can have three outcomes, according to my observations. Which one is taken is deterministic but unpredictably dependent on various seemingly unrelated circumstances:
With CWE-ou with Python 2.3 (and probably also the original Cyan MOULa client), normally outcome 1 happens. I can easily provoke outcome 3 however by adding a variable that keeps a reference to the tuple – the added PtDebugPrint confirms that the SDL setting has not taken effect:
There may be a way to provoke outcome 2 too, I haven’t tried that.
With H-uru/Plasma with Python 2.7, normally outcome 2 happens. I can easily provoke outcome 3 however by various seemingly innocuous changes to the Python code, e.g. adding another PtDebugPrint (yes, this is after where the error occurs):
There may be a way to provoke outcome 1, but I doubt it – the fact that the tuple has reference count 2 in this case probably comes from differences in Python’s garbage collection between 2.3 and 2.7.
The way to fix this is making plPythonSDLModifier::SetItemIdx() create a new tuple rather than modify an existing one. I have checked all uses of ptSDL.setIndex() and unless I missed anything they don’t mind if it replaces the tuple. I have also verified that the egg room SDL setting works now (I didn’t do any multiplayer testing to see if it has the intended effect though), and found that it also fixes entering the Teledahn buckets. The other two uses of ptSDL.setIndex(), the Gahreesen gear niche and remembering the last opened page of every book on the Relto bookshelf, already appear to work without the fix (tuple refcount is 1).
I’ve also found the following comment that seems to refer to this issue:
tldnBucketBrain.py:417: