Odessa Posted February 26, 2015 Posted February 26, 2015 To test your fear that arrays do not get cleaned up without Ar_Null, I wrote a test plugin which created a big array full of strings via UDF once per frame: scn CreateBloat array_var arr int i Begin Function { } let arr := Ar_List "GARBAGE BE HERE", "GARBAGE, GARBAGE, GARBAGE", "AND MORE AND MORE" while (i += 1) < (GetNumLoadedMods) Ar_Append arr, (GetNthModName i) loop End --- scn BloatQuestScript ; once per frame! int i Begin GameMode call CreateBloat let i += 1 Print "BLOAT multiplied by " + $i + "!!!" End I made 2 save games, one after 4150 calls, and one after 8181 calls. They were both identical and 1656 bytes. Also poked around with Ar_DumpID and could find no evidence of 8181 arrays lying around. Then I decided to check object (token) scripts, adding one of these tokens to the player every frame: scn BloatTokenScript array_var arr Begin OnAdd let arr := GetLoadedTypeArray 42 ; # Every NPC form from every plugin, 4507 total in my case RemoveMe End I let it run for ~1300 frames, then saved (which took a long time), the nvs file was 70.1 Megabytes. GetItemCount for the token showed 0, but random poking with Ar_DumpID showed all those NPC arrays still existed. I tried re-saving after both loading and restarting game, but it did not clean them up. I then added "let arr := Ar_Null" to the object script, and this solved the issue. --- Conclusion: You do not need to use Ar_Null in UDF scripts, but you definitely do need to use it in other object scripts, and probably Effect too (untested).
prideslayer Posted February 28, 2015 Posted February 28, 2015 That's definitely good to know, thanks Odessa. Feel like doing the same thing regarding strings and sv_construct / sv_destruct? I believe there is a similar leakage problem there. I've been going nuts with ar_null and sv_destruct since Doc happened upon the issue. Bodymorph was making massive NVSE files as well, so I just started clearing them all and testing. Any clears that caused a crash, I removed, and left the rest in.
Guest tomm434 Posted March 12, 2015 Posted March 12, 2015 DoctsSax, in tutorial you say this about populating maps and stringmaps arrays much quickier Okay, but, ehm, what if we have more than 20 elements to add, or simply don't know how many we'll add? And then nothing =) So how can I populate maps array if I don't know how many references will I add to it? (in other words how do I populate maps array without "Ar_Map" command?)
prideslayer Posted March 12, 2015 Posted March 12, 2015 You can use ar_append for normal arrays. What's your use case?
Guest tomm434 Posted March 12, 2015 Posted March 12, 2015 yep Ar_append works for regular arrays. But what if I want to create map array for NPC and give each of them special key 9.5 "SunnyRef" 10.1 "PeteRef" 13.5 "GeckoManRef" 15.6 "PlayerRef" How Can I add another pair (number+reference) without Ar_map ?
DoctaSax Posted March 12, 2015 Author Posted March 12, 2015 DoctsSax, in tutorial you say this about populating maps and stringmaps arrays much quickier Okay, but, ehm, what if we have more than 20 elements to add, or simply don't know how many we'll add? And then nothing =) So how can I populate maps array if I don't know how many references will I add to it? (in other words how do I populate maps array without "Ar_Map" command?) Ah yeah, that was more of a segue into talking about loops. The idea would be that for instance you ar_map 2 maps, then use a loop to add the elements of one to another. If the keys or values aren't set beforehand you do as pride says there.
prideslayer Posted March 22, 2015 Posted March 22, 2015 If you erase an element from an array from within a foreach loop when that element is the one under iteration, that will most likely break the loop. With regular arrays, it doesn't always, but it does if you for instance pass the array as parameter to a UDF from within the loop. It'll always break with maps and stringmaps. The thing to do here is do your erasing when the loop is done, by for instance adding elements to erase to a new array during the loop and erase them from the original afterwards, or add the elements to keep to a new array and let the old one be a copy of that one afterwards.There is an alternative approach that can be faster and perhaps not as confusing to people trying to iterate through an array (maps and stringmaps only!) and remove things during the iteration. If you use ar_keys you can iterate through that instead and safely delete from the array you want to. This is unsafe: foreach item <- arMyArray if <somecondition> ar_erase arMyArray item["key"] endif loop But this is nearly identical (two extra lines, one changed line) and is safe let keys := ar_Keys arMyArray foreach item <- keys if <somecondition> ar_erase arMyArray item["value"] endif loop let keys := ar_Null NOTE: you can only do this with maps and stringmaps, not with normal arrays. It will break just like the unsafe way when used on a normal array.
Guest tomm434 Posted March 22, 2015 Posted March 22, 2015 yes I noticed that if you remove elements during foreach the first way - it doesn't break the loop completely but it doesn't work as intended - some elements are skipped.
prideslayer Posted March 22, 2015 Posted March 22, 2015 That's normal behavior for arrays and why doc says to not do that in the tutorial. If you have an array like [0,1,2,3,4] and you're in a for or foreach loop on 2 and delete it, the next one is going to be 4 and 3 will be skipped. You can get around this with normal arrays (not maps or stringmaps) by counting downward instead of upward. I'm going to edit that post now because I didn't think of normal arrays like that, which won't work correctly even using ar_keys -- that works only with maps and stringmaps which are mainly what I use.
DoctaSax Posted March 22, 2015 Author Posted March 22, 2015 I should point out that with regular arrays erasing during a loop can work out pretty well automatically, but better safe than sorry. For maps & stringmaps, the ar_keys method looks pretty handy.
prideslayer Posted March 22, 2015 Posted March 22, 2015 I should point out that with regular arrays erasing during a loop can work out pretty well automatically, but better safe than sorry. For maps & stringmaps, the ar_keys method looks pretty handy. I haven't had a problem doing it this way as long as I count backwards.. e.g. from ar_size downto 0. That way if I delete 6, it doesn't matter if 7...10 get reindexed. When you count upward, that's when you can get screwed up.
DoctaSax Posted March 22, 2015 Author Posted March 22, 2015 Sure, a while loop counting back should be safe. Still, even a foreach can work out, going forward, but it's not reliable, so why take the chance, right.
Odessa Posted March 23, 2015 Posted March 23, 2015 Here's a handy 2 liner to loop through a regular array and delete all elements matching a certain condition: while (Ar_Erase My_Array, i + (Ar_BadNumericIndex * eval !(SomeCondition))) || TestExpr My_Array[i += 1 * SomeCondition] loop (You'll have to reset i to 0 if you want to repeat it)
Guest tomm434 Posted March 23, 2015 Posted March 23, 2015 Odessa, how do you I that? What if I want to delete all actors who are males? How do I specify the reference in "* eval SomeCondition" part?
Odessa Posted March 23, 2015 Posted March 23, 2015 It was a joke, although it should work. I wouldn't recommend using something that looks so obscure. TestExpr returns false if an NVSE error is generated. Ar_Erase returns the count removed, (0 if you call it with a bad index, which is the case if the condition is met) GetIsSex needs a ref var, so I think you'd need the uglier looking: while (TestExpr MyRef := My_Array[i]) || Ar_Erase My_Array, (i + (Ar_BadNumericIndex * eval !(MyRef.GetIsSex "Male"))) || TestExpr My_Array[i += 1 * MyRef.GetIsSex "Male"] loop Might need to adjust the parentheses to get it to assign variables in right order.
prideslayer Posted March 23, 2015 Posted March 23, 2015 Maybe it's time for a geckscript obfuscation contest along the lines of the C & perl ones?
prideslayer Posted March 23, 2015 Posted March 23, 2015 No argument here, but what kind of coward gives up without trying?!
Guest tomm434 Posted May 1, 2015 Posted May 1, 2015 Any way to check if particular dimensional array is constructed? There is a multidimensional array aaSCMainQuest.SlaveList[0] aaSCMainQuest.SlaveList[1] aaSCMainQuest.SlaveList[2] I need to check if aaSCMainQuest.SlaveList[0] is constructed. if I do simple check if aaSCMainQuest.SlaveList[0] endif It doesn't compile (no such variable) When I did this code (in hope that Ar_size will return -1 for array that is not initialized) let Size := Ar_size aaSCMainQuest.SlaveList[0] it throws error in console that array is not initialized (error in script etcs..). Makes reading debug log much harder.
DoctaSax Posted May 1, 2015 Author Posted May 1, 2015 Perhaps try if eval -1 < ar_size aaSCMainQuest.SlaveList[0] if that doesn't work out, you'll probably have to pass it to a temporary array var first
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now