Memory management, create and destroy

jaynabonne
29 Sept 2012, 00:12I have some questions that looking through the low-level source has not yet been able to answer - and I'd like to get the official word from someone who knows what the overall design is and how things are supposed to work.
The code I am writing could end up using lots of internal objects. While functions like NewObjectList and NewStringDictionary create anonymous entities, there is no such function for objects. The only ones I have found for generating objects are "create" (for basic, empty objects) and Clone (for duplicating existing objects). Both of these create named objects which exist in the master object list (I apologize for my terminology - I don't know the right words, so I'm just making it up as I go along.) This list is what's visible in the debugger under the Objects tab.
I have two cases that I'd like to know the ramifications for in terms of memory usage and keeping Quest's internal data structures sane. Keep in mind that I understand about garabage collected objects (which both Javascript and C# have), but I fear there could possibly be complications with things like the undo stack...
1) "create" - Let's say you do this:
and then you do it again:
The initial object "o" wasn't explicitly destroyed, but it is clearly replaced. You can see this by assigning the first "o" to another variable and then manipulating members.
Given my understanding of object references in general, etc, I would expect "t" to be separate from "o" above, and that indeed has proven to be the case. So that is handy, as object generation can simply use some standard name for every object creation and not end up polluting the "master object list." My questions about that are:
a) Is this safe to do generally?
b) When all references to the initial "o" go away (e.g. if "t" goes out of scope), will that "o" be garbage collected or will it be a leak?
If this is not safe to do, then I'm going to have to revisit some code I've written (like the path code).
2) "Clone" - With Clone, you don't specify a name - the cloned object gets a numbered name variant of the original object (though I haven't tried cloning a clone to see what happens to the name). If you keep Cloning objects, you will end up with all sorts of names in the master object list, and the objects won't go away, as they are still referenced there. So it seems that Cloned objects *must* be destroyed when it is desired that they no longer be used, to free up memory, etc. But I have wondered what happens in "destroy" and when it can be done. For example, if I do this (roughly):
I still get "o.message = Hello World!" as the object is still referenced by "o" and therefore existing (I hope!).
However, after looking at the cs code, I varied the test like this :
and got "Error running script: Unknown object or variable 'message'" as expected, because "destroy" walks all named object hierarchy and removes all references to a destroyed object before removing it from the master object list.
So given all that... is it safe, both from a memory management and "undo stack" point of view to do the former code, that is destroy a cloned object right away to remove the reference from the master list, but still retain, modify and use the disconnected object? I would think I'd at least want to null out the name attribute or something to avoid confusion. It would be more convenient to do it that way, as all the messy ownership issues could be handled at clone creation time (in fact, it could be wrapped up into a neat little function). But if it's not safe to do so, then I'll just switch to the idiom of calling "destroy" on all my objects when I'm done with them.
I hope this all makes sense. I just want to be sure I'm doing the right thing with my objects. In my code, objects will be coming and going all the time (goals, paths, conversation topics, etc), so I need to get this right. And at least the answer will be posted here in the forum in case anyone else decides to travel down this same dark path...
The code I am writing could end up using lots of internal objects. While functions like NewObjectList and NewStringDictionary create anonymous entities, there is no such function for objects. The only ones I have found for generating objects are "create" (for basic, empty objects) and Clone (for duplicating existing objects). Both of these create named objects which exist in the master object list (I apologize for my terminology - I don't know the right words, so I'm just making it up as I go along.) This list is what's visible in the debugger under the Objects tab.
I have two cases that I'd like to know the ramifications for in terms of memory usage and keeping Quest's internal data structures sane. Keep in mind that I understand about garabage collected objects (which both Javascript and C# have), but I fear there could possibly be complications with things like the undo stack...
1) "create" - Let's say you do this:
create ("o")
and then you do it again:
create ("o")
The initial object "o" wasn't explicitly destroyed, but it is clearly replaced. You can see this by assigning the first "o" to another variable and then manipulating members.
create ("o")
o.x = 1
t = o
create("o")
o.x = 2
msg ("o.x = " + o.x + ", t.x = " + t.x)
// yields o.x = 2, t.x = 1
Given my understanding of object references in general, etc, I would expect "t" to be separate from "o" above, and that indeed has proven to be the case. So that is handy, as object generation can simply use some standard name for every object creation and not end up polluting the "master object list." My questions about that are:
a) Is this safe to do generally?
b) When all references to the initial "o" go away (e.g. if "t" goes out of scope), will that "o" be garbage collected or will it be a leak?
If this is not safe to do, then I'm going to have to revisit some code I've written (like the path code).
2) "Clone" - With Clone, you don't specify a name - the cloned object gets a numbered name variant of the original object (though I haven't tried cloning a clone to see what happens to the name). If you keep Cloning objects, you will end up with all sorts of names in the master object list, and the objects won't go away, as they are still referenced there. So it seems that Cloned objects *must* be destroyed when it is desired that they no longer be used, to free up memory, etc. But I have wondered what happens in "destroy" and when it can be done. For example, if I do this (roughly):
<type name="foo">
<message>Hello world!</message>
</type>
<object name="prototype">
<inherit name="foo"/>
</object>
o = Clone(prototype)
destroy (o.name)
msg("o.message = " + o.message)
I still get "o.message = Hello World!" as the object is still referenced by "o" and therefore existing (I hope!).
However, after looking at the cs code, I varied the test like this :
...
game.o = Clone(prototype)
destroy (game.o.name)
msg("o.message = " + game.o.message)
and got "Error running script: Unknown object or variable 'message'" as expected, because "destroy" walks all named object hierarchy and removes all references to a destroyed object before removing it from the master object list.
So given all that... is it safe, both from a memory management and "undo stack" point of view to do the former code, that is destroy a cloned object right away to remove the reference from the master list, but still retain, modify and use the disconnected object? I would think I'd at least want to null out the name attribute or something to avoid confusion. It would be more convenient to do it that way, as all the messy ownership issues could be handled at clone creation time (in fact, it could be wrapped up into a neat little function). But if it's not safe to do so, then I'll just switch to the idiom of calling "destroy" on all my objects when I'm done with them.
I hope this all makes sense. I just want to be sure I'm doing the right thing with my objects. In my code, objects will be coming and going all the time (goals, paths, conversation topics, etc), so I need to get this right. And at least the answer will be posted here in the forum in case anyone else decides to travel down this same dark path...

jaynabonne
29 Sept 2012, 09:59And, of course, I left out a key part of my question: the objects I'm talking about here are internal objects only (data structures used in internal algorithms), not anything the player would see, touch, move, take or otherwise interact with. The latter type of objects *must* remain in the master object list; otherwise, they are not visible anywhere in the game world.
It's the internal ones I'm referring to, those "behind the scenes" objects that the user doesn't interact with directly but which scripts might want to create on the fly for their own personal data needs.
It's the internal ones I'm referring to, those "behind the scenes" objects that the user doesn't interact with directly but which scripts might want to create on the fly for their own personal data needs.
Alex
05 Oct 2012, 17:431. This sounds like a bug to me. Quest should give you an error if you try to create an object with the same name as one that already exists. The old object may or may not stick around. It will certainly not get written out when you save the game, so you can't rely on it continuing to exist. You can probably still access it before a save if you had some other object attribute pointing to it. If there was ever an object attribute pointing to it, it won't be garbage collected because it would still have something pointing to it via the undo logger.
2. Again not safe, as if the player saves to a .quest-save file the object won't be written out, so it won't be there on load.
This is basically why there is no such thing as an anonymous object - it needs to have a name to get written on save.
If you want to make use of lots of objects for storing data, I say go ahead - do some testing if you're worried about performance, but I can't see a problem with creating and destroying as many objects as you need. Destroying an object won't remove it from memory as it will still be in the undo stack. If anything can be regarded as actually purging destroyed objects, it's the process of saving and reloading a game.
2. Again not safe, as if the player saves to a .quest-save file the object won't be written out, so it won't be there on load.
This is basically why there is no such thing as an anonymous object - it needs to have a name to get written on save.
If you want to make use of lots of objects for storing data, I say go ahead - do some testing if you're worried about performance, but I can't see a problem with creating and destroying as many objects as you need. Destroying an object won't remove it from memory as it will still be in the undo stack. If anything can be regarded as actually purging destroyed objects, it's the process of saving and reloading a game.

jaynabonne
05 Oct 2012, 19:51Ok, this is starting to make more sense now - and the issues with saving are just the sort of thing I was worried about.
I'll move to generating unique names for all my objects. It means I'm going to have to explicitly clean them up (which is painful - but we do it in other languages), but I already have some mechanisms in place for cloned objects, so I'll just expand them to the others as well.
Thanks much for the reply!
I'll move to generating unique names for all my objects. It means I'm going to have to explicitly clean them up (which is painful - but we do it in other languages), but I already have some mechanisms in place for cloned objects, so I'll just expand them to the others as well.
Thanks much for the reply!

jaynabonne
10 Oct 2012, 13:11Just to round this thread out, I'm attaching what *I* have come to use based on the above. It's not a lot of code, but it's working well for me. It's a small "Utils" library with the following functions:
1) o = Utils_CreateObject("basename")
This creates an internal object. The object visibility is set to false. The "basename" parameter is used to generate a unique name for the object. The created object names will be like "basename0", "basename1", etc. I originally didn't have that (using just a default base name), and it made it much more difficult to detect and resolve "object leaks". Now by providing good base names for your objects, you'll be able to scan the object list in the debugger and know what things are much more easily.
The object is marked as needing to be destroyed. You should call Utils_DestroyObject(o) when you're done with it.
2) o = Utils_CloneObject(prototype)
This creates an object as a clone of a prototype object. Note that the prototype parameter is a direct object reference, not an object name in quotes. The prototype is passed to Clone to generate a copy.
If the resulting object has a "create" script defined, then it will be executed on the new object after cloning.
The object is marked as needing to be destroyed. You should call Utils_DestroyObject(o) when you're done with it.
3) Utils_DestroyObject(o)
This function destroys an object "o" if "o" has been created by either Utils_CreateObject or Utils_CloneObject. If it was not created by either of those functions (and so marked internally), then nothing will happen. This means, among other things, that you can pass any object to this function, and only those objects that should be destroyed will be. (There are ways to allow other objecs to be destroyed with this function. I have not found a need for that yet.)
If the object has a "destroy" script, then the script will be executed before the object is destroyed. This allows the object to clean up anything it needs to (e.g. other objects).
4) Utils_DestroyObjectList(list)
This function is used to destroy a list of objects created by Utils_CreateObject and/or Utils_CloneObject. It simply removes each object from the list in turn and calls Utils_DestroyObject on it. You could, in theory, pass any object list into this. If the objects were not created Utils_CreateObject or Utils_CloneObject, they will just be removed from the list.
Upon exit, the list is empty.
5) Utils_CallBaseMethod(o, "type", "method", params)
This function invokes the specified base method on an object. This is useful if you have object type hierarchies and have overridden script attributes in derived types and want to still be able to call the overridden script. "o" is the object to invoke the method on. "type" specifies the base type that has the script to be invoked. Note that, as types can not be directly manipulated, there must exist a prototype object for the base type (just an empty object with an "inherit" element will do). "method" is the script to invoke. "params" are the parameters to pass. Pass null for params if you have no parameters. Otherwise, params should be the standard parameters dictionary passed to a script.
This can especially be useful for cascading "create" and "destroy" calls up the type hierarchy. Actually it's useful for lots of things.
Conceptual notes: Mapping to standard object-oriented terminology, the prototype object is the equivalent of an instantiatable class. (You must still use types to implement derivation. But in order to Clone a type or call a base method on it, you must have a prototype object to be manipulated. If types in Quest are ever made visible like objects, then this requirement can go away.) The "create" script is akin to a constructor, and the "destroy" script is the equivalent of a destructor.
Implementing a "destroy" method in a prototype is straightforward - just add it to the type or prototype object. If you use Utils_CreateObject to create an object that needs a destroy method, you can add it after creation. (I finally discovered about a week ago the elusive "=>" operator to assign an inline script to an object. Very cool!)
Example:
With the "destroy" method added in as above, the two item objects will also be destroyed when the main object is destroyed.
That's about it. If anyone has any questions about this, needs more examples, encounters bugs, or comes up with suggestions, please let me know!
1) o = Utils_CreateObject("basename")
This creates an internal object. The object visibility is set to false. The "basename" parameter is used to generate a unique name for the object. The created object names will be like "basename0", "basename1", etc. I originally didn't have that (using just a default base name), and it made it much more difficult to detect and resolve "object leaks". Now by providing good base names for your objects, you'll be able to scan the object list in the debugger and know what things are much more easily.
The object is marked as needing to be destroyed. You should call Utils_DestroyObject(o) when you're done with it.
2) o = Utils_CloneObject(prototype)
This creates an object as a clone of a prototype object. Note that the prototype parameter is a direct object reference, not an object name in quotes. The prototype is passed to Clone to generate a copy.
If the resulting object has a "create" script defined, then it will be executed on the new object after cloning.
The object is marked as needing to be destroyed. You should call Utils_DestroyObject(o) when you're done with it.
3) Utils_DestroyObject(o)
This function destroys an object "o" if "o" has been created by either Utils_CreateObject or Utils_CloneObject. If it was not created by either of those functions (and so marked internally), then nothing will happen. This means, among other things, that you can pass any object to this function, and only those objects that should be destroyed will be. (There are ways to allow other objecs to be destroyed with this function. I have not found a need for that yet.)
If the object has a "destroy" script, then the script will be executed before the object is destroyed. This allows the object to clean up anything it needs to (e.g. other objects).
4) Utils_DestroyObjectList(list)
This function is used to destroy a list of objects created by Utils_CreateObject and/or Utils_CloneObject. It simply removes each object from the list in turn and calls Utils_DestroyObject on it. You could, in theory, pass any object list into this. If the objects were not created Utils_CreateObject or Utils_CloneObject, they will just be removed from the list.
Upon exit, the list is empty.
5) Utils_CallBaseMethod(o, "type", "method", params)
This function invokes the specified base method on an object. This is useful if you have object type hierarchies and have overridden script attributes in derived types and want to still be able to call the overridden script. "o" is the object to invoke the method on. "type" specifies the base type that has the script to be invoked. Note that, as types can not be directly manipulated, there must exist a prototype object for the base type (just an empty object with an "inherit" element will do). "method" is the script to invoke. "params" are the parameters to pass. Pass null for params if you have no parameters. Otherwise, params should be the standard parameters dictionary passed to a script.
This can especially be useful for cascading "create" and "destroy" calls up the type hierarchy. Actually it's useful for lots of things.
Conceptual notes: Mapping to standard object-oriented terminology, the prototype object is the equivalent of an instantiatable class. (You must still use types to implement derivation. But in order to Clone a type or call a base method on it, you must have a prototype object to be manipulated. If types in Quest are ever made visible like objects, then this requirement can go away.) The "create" script is akin to a constructor, and the "destroy" script is the equivalent of a destructor.
Implementing a "destroy" method in a prototype is straightforward - just add it to the type or prototype object. If you use Utils_CreateObject to create an object that needs a destroy method, you can add it after creation. (I finally discovered about a week ago the elusive "=>" operator to assign an inline script to an object. Very cool!)
Example:
o = Utils_CreateObject("container")
o.list = NewObjectList()
o.destroy => { Utils_DestroyObjectList(this.list) }
item = Utils_CreateObject("item")
item.data = "foo"
list add(o.list, item)
item = Utils_CreateObject("item")
item.data = "bar"
list add(o.list, item)
Utils_DestroyObject(o)
With the "destroy" method added in as above, the two item objects will also be destroyed when the main object is destroyed.
That's about it. If anyone has any questions about this, needs more examples, encounters bugs, or comes up with suggestions, please let me know!
Quantus
10 Jan 2016, 16:07I'm having an issue with cloned object removal. I have figured out a way to Move the object into a Void Room. This works to get rid of the object, but it causes other issues. So what should I do if I want to:
Spawn five clones of Monster and move to current room.
Set a monster verb called Slay.
Click Slay will slay the Monster(Remove object cloned monster).
I know that having the pre created monster with verb Slay will cause each clones Slay verb to only Remove object named Monster. I can't pre create a verb that will detect the name of the cloned monster.
I think your post here maybe my answer, but it's way over my head right now. I'll try it out though and see if I can comprehend it.
Spawn five clones of Monster and move to current room.
Set a monster verb called Slay.
Click Slay will slay the Monster(Remove object cloned monster).
I know that having the pre created monster with verb Slay will cause each clones Slay verb to only Remove object named Monster. I can't pre create a verb that will detect the name of the cloned monster.
I think your post here maybe my answer, but it's way over my head right now. I'll try it out though and see if I can comprehend it.

jaynabonne
10 Jan 2016, 17:50The Slay verb should use the variable "this", which refers to the current object/monster. That way, you don't have to know the name of the monster. (In other words, it's the only way to make the verb generic.)
Quantus
11 Jan 2016, 01:11You sir are a genius.
It works perfectly. Thank you! So to remove a cloned object with a verb you just put Remove Object and type the word: this
I could have read that already, but I didn't understand it. THE NAME OF THE VARIABLE IS: this
A clone is able to be removed by clicking on the verb for that cloned object which contains the command to Remove Object. You don't put the name of the object there, you put the variable name: this
The actual code looks like this:
RemoveObject (this)
Thanks again!
It works perfectly. Thank you! So to remove a cloned object with a verb you just put Remove Object and type the word: this
I could have read that already, but I didn't understand it. THE NAME OF THE VARIABLE IS: this
A clone is able to be removed by clicking on the verb for that cloned object which contains the command to Remove Object. You don't put the name of the object there, you put the variable name: this
The actual code looks like this:
RemoveObject (this)
Thanks again!

jaynabonne
11 Jan 2016, 21:02Glad it worked for you! 

Quantus
14 Jan 2016, 05:23Any idea what would cause this error:
Error running script: Error compiling expression 'this': Unknown object or variable 'this'
I'm still tinkering with the code, it works in most cases, but I'm getting this error sometimes.
Error running script: Error compiling expression 'this': Unknown object or variable 'this'
I'm still tinkering with the code, it works in most cases, but I'm getting this error sometimes.

jaynabonne
14 Jan 2016, 09:07A "this" variable only exists if you "do" a script, where you pass an object. If you "invoke" a script, it doesn't know what the object is, and there won't be a "this". Not sure if that's it. If you have a persistent problem, reply where it's happening, so we can get some context.
Quantus
15 Jan 2016, 04:17You helped again. Thanks!