Lists and GetScriptVar

Started by eduran, February 02, 2014, 10:06:49 AM

Previous topic - Next topic

eduran

# core1.crpl
# ------------------------------------------
$name:"core1"
once
GetCoresWithVar("name" "core2") pop ->otherCore
CreateList ->List
0 1 2 3 4 5 6 7 PrependStackToList(<-list)
1 ->notaList
endonce
Trace2("list=" <-list)
Trace2("notalist=" <-notaList)

# core2.crpl
# ------------------------------------------
$name:"core2"
once
GetCoresWithVar("name" "thisisanametoo") pop ->otherCore
delay(1)
GetScriptVar(<-otherCore "core1.crpl" "list") ->thisisalist
GetScriptVar(<-otherCore "core1.crpl" "notalist") ->notalist
Trace2("list:" <-thisisalist)
delay(5)
RemoveListElement(<-thisisalist 0)
RemoveListElement(<-thisisalist 1)
0 ->notAlist
endonce


Two simple scripts. Core 1 initializes a list and a normal variable. Core 2 reads both and makes changes to the versions stored on its side. As expected, changing 'notalist' from 1 to 0 has no effect on core 1 (the tracelog still shows 'notalist=1'). But removing elements from 'thisisalist' on core 2 also changes 'list' on core 1 (tracelog shows 'list=[0, 3, 4, 5, 6, 7] after the delay). Is that working as intended? What would I have to do to get a seperate copy of 'list' onto core 2?

Grayzzur

The old pass-by-value vs pass-by-reference thing. Individual variables are passed by value. The list ID is a pointer to the list, passed by reference. Both cores now have a pointer to the same list. Changes would affect both.

You could have core2 make a complete copy of the list. Dump all its elements to the stack and build a new list. That assumes the list is full of normal variables, and isn't holding additional list references. That gets into shallow vs deep copying -- if it's a list of lists, do you copy the list references, or recursively copy every sub list?


#Shallow Copy
ClearStack
<-List1 GetListCount 0 do
<-List1 I GetListElement
loop
CreateList ->List2
<-List2 PrependStackToList

"Fate. It protects fools, little children, and ships named 'Enterprise.'" -William T. Riker

knucracker

Yeah, there is 'int', 'float', 'string', and 'list'.  Those are the data types in crpl.  The first three are immutable primitives and always passed by value.  The list is passed by reference.  Each of these types is basically backed by the .net/mono equivalent data structure or type.

So that part is all on purpose and I use it myself in the Credits mission.  Game.crpl in the credits mission creates a list call 'occupiedMap' in its Awake function.  That list is used by several other scripts attached to other cores.  It's a list used to store where the ghosts are on the map.  It allows other ghosts to know when a spot on the map is occupied so they can't move over it.  That's why they act a little bit like the ghosts in the old Gauntlet video game.

Anyway, having the list in one place and then manipulated by different scripts is nice but it has some caveats.  The first is that every script always attempts to persist every heap variable.  For instance, if you say
"abc" ->myVar
in a script, then when the game is saves myVar will get persisted into the save game file.  The variable myVar is on the heap ( which in basically a hashmap held by a crpl tower for each script).  So if you have a large list in one spot and you want to read and write it from many other spots, then you most likely don't want all of those users of the list to persist it.  This means that when you use the list in these other scripts, you must either delete (the -- operator) them from the heap immediately after use (assuming they were put on the heap) or you must mark the local heap entry as something that will NotPersist.   (NotPersist is a CRPL command)


GetScriptVar(<-otherCore "core1.crpl" "list") ->thisisalist
#thisisalist is on the heap.  This script will persist thisisalist when the game is saved.  We probably don't want this.

#One way to prevent persistence is to remove it from the heap once we are done
--thisisalist

#Another way to prevent persistence is to tell this script to not persist any heap variable names "thisisalist"
#Note that this call might be more appropriate inside an :Awake call
NotPersist("thisisaslist")


So that's one caveat I wanted to mention.  As for your original question...
The only way to make a copy is to explicitly create a new list and copy everything to it.  That works, but is O(n) in CRPL time... so a large list could eat up a healthy amount of CRPL commands within a game frame.  So, I will add a couple function for the next build to clone an array.  I'll make a shallow and a deep version.  It will still be an O(n) operation but that will be in game engine time and will only count as 1 CRPL command.


Clean0nion

So is -- a CRPL command that performs the same as NotPersist?

knucracker

No, -- means to remove the item from the heap.  NotPersist("name") simply tells the save game routine to ignore any heap entry called "name".  So you can keep a variable on the heap and use it, but it won't get persisted if you have specified NotPersist.

Grayzzur

So, "--" deallocates the variable and makes it unusable in the script immediately?

NotPersist allows it to still be used in the script, but it won't be saved via savegame? Or will it not even survive until the next game tick?
"Fate. It protects fools, little children, and ships named 'Enterprise.'" -William T. Riker

eduran

Very interesting, that makes some of the stuff I am doing with my movement script a lot easier.

Quote from: virgilw on February 02, 2014, 02:33:36 PM
So if you have a large list in one spot and you want to read and write it from many other spots, then you most likely don't want all of those users of the list to persist it.
I am not entirely clear about why I don't want that. Is it just about wasted memory (the same list is stored in the save game multiple times) or are there other problems?

Clean0nion

Quote from: eduran on February 02, 2014, 04:28:58 PM
Very interesting, that makes some of the stuff I am doing with my movement script a lot easier.

Quote from: virgilw on February 02, 2014, 02:33:36 PM
So if you have a large list in one spot and you want to read and write it from many other spots, then you most likely don't want all of those users of the list to persist it.
I am not entirely clear about why I don't want that. Is it just about wasted memory (the same list is stored in the save game multiple times) or are there other problems?
I think he means that if you have ten cores all editing a list, then that list ight be unusable by another core which expects the list to stay the same.

Grayzzur

Quote from: Clean0nion on February 02, 2014, 04:31:13 PM
Quote from: eduran on February 02, 2014, 04:28:58 PM
Very interesting, that makes some of the stuff I am doing with my movement script a lot easier.

Quote from: virgilw on February 02, 2014, 02:33:36 PM
So if you have a large list in one spot and you want to read and write it from many other spots, then you most likely don't want all of those users of the list to persist it.
I am not entirely clear about why I don't want that. Is it just about wasted memory (the same list is stored in the save game multiple times) or are there other problems?
I think he means that if you have ten cores all editing a list, then that list ight be unusable by another core which expects the list to stay the same.

If you have ten cores editing the same list, one can't expect it to stay the same. If it needs it's own copy, then it needs a copy. However, having copies by default... you'd have 10 copies of a potentially very large list eating memory. Taking extra time to save 10 copies to the savegame. Ten copies eating up space in your save game file. Ten copies taking that much longer to load the game back in. Wasted time, memory and disk space. Unless you truly need a distinct copy, in which case you forcibly make the copy yourself.

Ten doesn't seem like so much. But then the next guy comes along and makes a map that has a hundred. Or a thousand copies. Oops.
"Fate. It protects fools, little children, and ships named 'Enterprise.'" -William T. Riker

knucracker

-- removes the variable from the heap immediately.  It is like the variable is never set.  NotPersist only affects save games.  It doesn't affect if the variable is on the heap.  When a game persists (saves) it goes through all scripts on all cores and writes out the contents of the heap for each script.  If it finds that a var is in the NotPersist list, it skips it.

As for why you might not want to persist a list, take an example where you have one list that you intend to use globally.  It is large (say the size of the map cells) and you will read and write to this list from many cores (think ghosts in the Credits mission).  Each ghost gets a reference to the one list in its Awake call.  But each ghost definitely doesn't need to save the list just because it holds a reference to the list in its heap.