I've been working on Pyrel, a roguelike implemented in Python, off and on for a bit over a year now. And it's now about time for me to start working on implementing saving and loading of the game. Yeah, you'd have thought I'd get to this point earlier; oh well. I've run into a bit of a snarl and would appreciate insight from the many experienced programmers here.
I feel confident that I can write code that will serialize/deserialize game objects and the formal relationships between them (i.e. this object contains this other object, etc.). Where I run into problems is with function pointers, and worse, lambdas. For example, I have some game objects that are simply timers: they count down until their timer hits 0, then they invoke a function. So their game state looks something like this:
Language: python
"id": 10024 # Auto-incrementing; unique across objects
"duration": 10
"func": [bound method Foo.foo of [__main__.Foo instance at 0x1004a73f8]]
or, with lambdas:
Language: python
"func": [function [lambda] at 0x1004a1de8]
(Pardon, had to replace < with [ in the code because otherwise it broke when displayed)
In the former case, I should be able to map from 0x1004a73f8 back to a game object (since the serialization process needs to touch every object anyway, it's straightforward to get a mapping of object addresses to those objects), get the object's ID, and generate a serialization that says "Okay, you need to call the "foo" method of the object with ID 9782345". Then the deserialization process would have access to the recreated object with ID 9782345, and thus can look up its "foo" attribute, to recreate the function pointer. It'd be pretty dang hacky, but I think I could do it; I'd like to hear comments on this though.
In the latter case, as far as I can tell, I'm completely boned. Lambdas contain both bytecode
and context. Bytecode is verboten as far as I'm concerned -- as soon as you have any bytecode (or any text that you compile into code, etc.) in your savefile, you leave yourself open to malicious users distributing savefiles that do nasty things to unsuspecting players. Would this ever happen in practice? Probably not. But it's bad design anyway; for the same reason, Pyrel's datafiles are JSON instead of pure Python, even though the latter would be easier to load.
And the lambda's execution context is almost as bad -- a single lambda could easily contain basically the entire program context.
Am I missing anything here? I've just about convinced myself that I have to ban lambdas from any part of the program that needs to be serialized, but I'd love to be wrong. And I'm not especially happy with my proposed approach to [de]serialization of normal function pointers, but I can't think of a cleaner solution.
Feedback is welcome.