User:QQuix/On Dynamic Items and Savegame Bloating
Foreword
This is the result of some tests I did to determine/review the behavior of Dynamic and Non-Dynamic items.
For definitions, read Modding Terminology, particularly the topics “Dynamic Content” and “Dynamic Items”.
Most of what is here is not new, and is written one way or another across the CS WIKI.
Please, place any comments/suggestions/corrections in the Discussion/Talk page, as this page is still a work in progress.
Introduction
The main objective of the tests was to get a firsthand, comprehensive experience with dynamic items, motivated by the above mentioned article and the PlaceAtMe bloat potential. And, while doing it, keep an eye for fine details, exceptions and odd results that might show up.
The tests were focused on dynamic FormIDs and savegame bloating.
- NOTE: Most probably these kinds of tests have been done by other modders (Hamma?). Any info on equivalent tests would be appreciated.
Conclusions
(I am placing the conclusions up front for the benefit of those that don’t care to read the whole text)
FormID tests
- When a dynamic item is moved to a container, a Ref Variable containing its FormID is not invalidated immediately. It remains intact for the rest of the frame. It turns to Null in subsequent frames.
- TODO: check if it turns to Null right in the next frame.
- Functions using the null Ref Variable either are ignored or produce undesirable results. Didn’t experience any CDT with the few functions tested.
- TODO: Test more functions.
- A free FormID is reused only if it is after/higher than the last used one. Free FormIDs in the middle of the series are not reused. Example: Drop 3 items. Lets say they receive FormIDs 11,12 and 13. If you pick #13 up and drop it again, it will be 13 again, one higher than the last used (which is now #12). If you pick #12 up and drop it again, it will be FormID 14, one higher than the last used (#13).
- NOTE:I think I read somewhere that those ‘holes’ in the sequence would be reused under some unusual circumstance (maybe when running out of numbers??). Any link or info on the subject would be appreciated.
Bloating tests
- Once placed in the world, the effect on bloating is the same, regardless of how the item was created. Meaning: dropping 1000 quills from a container has the same effect as 1000 quills created with PlaceAtMe.
- Bloating is the same for different items (tests used Flawless Emeralds, Quills and Paintbrushes).
- Each item placed in the world added ~78 bytes to the savegame.
- Each item moved to a container reduced savegame size by ~23 bytes immediately (before cell reset).
- Waiting outside for cell reset, additionally reduced savegame size by ~11 bytes
- NOTE: Not sure these 11 bytes are related to the test scenario.
- NOTE: Will the remaining 44 bytes stay forever in the savegame?
- TODO: Test multiple Add+drop to/from the container.
- RemoveAllItems HAS NO EFECT in savegame size. (Surprise! Surprise!)
- TODO: Ttest with RemoveItem and RemoveMe.
- AddItem has no effect in savegame size.
Observations and Comments
The leftover bytes may be related to the following info on the Modding Terminology page:
However, under some circumstances the reference will not be removed from the save game. (Non-removal seems to be associated with having script record variables either attached to the reference and/or pointing at the reference.)
There is no way to do massive tests with thousands of items without “having script record variables pointing at the reference”. Unless, of course, having the PC pick up all those items one by one.
Therefore the leftover bytes may be related to this effect.
Common knowledge / Backgound
Non-Dynamic items
- Non-Dynamic items are items placed in the world in the CS.
- Formid starts with Mod# (NNxxxxxx).
- FormIDs are stable after adding to and dropping from a container, therefore FormIDs saved in Ref variables may be reused over time.
Dynamic items
- Dynamic items are items placed in the world during the course of the gameplay.
- Dynamic items may be created by dropping an item from a container (either placed in the container in the CS or added on the fly by AddItem) or may be created directly in the world by PlaceAtMe.
- Formid starts with FF (FFxxxxxx).
- FormIDs are dynamic and may change after adding to and dropping from a container, therefore FormIDs saved in Ref variables should not be reused over time.
General Methodology
The tests were done with Misc Items only.
FormID tests
Create an item, place it in the world, locate and save its FormID, remove the item to a container and play with the saved formID to see what happens.
Bloating tests
Create 2000 items, place them in the world, remove them to a container and destroy them, saving the game along the way to measure bloating.
Variations
Item Creation
For the test, items were created and placed in the world four different ways:
- Placed in the world in the CS.
- Placed in a container in CS and dropped in the world.
- Dynamically created in a container with AddItem and droped in the world.
- Dynamically created in the world with PlaceAtMe.
Item Destruction
Items were removed from the game 3 ways
- RemoveAllItems
- RemoveItem (not done yet)
- RemoveMe (not done yet)
Other variations
- Scripted (not done yet) and non-scripted items.
Test Results
Invalid FormIDs
By Invalid FormIDs, I mean a dynamic formID after the item is moved to a container
- Item added to an NPC in CS.
- Dropped in game (gets FormID=FF000001).
- FormID saved in a Ref variable >> Set xItem to [item found by GetFirst/NextRef loop].
- At this point xItem contains FormID FF000001 (valid).
- Added to an NPC and dropped again (gets FormID=FF000002).
- At this point xItem still contains FormID FF000001 (now invalid: does not represent an existing item).
Using a reference to the now inexistent item, IN THE SAME FRAME, does not cause any visible problem.
- Normal, correct returns from PrintToConsole, GetBaseObject, GetPos, SetPos, MoveToMarker:
- PrintToConsole “%i %n” xItem xItem - prints FF000001 and the correct item name.
- xItem.GetBaseObject – returns the correct Base Object.
- SetPos – Sets new XYZ positions as verified by subsequent GetPos.
- MoveToMarker – Sets new XYZ positions as verified by subsequent GetPos.
Using a reference to the now inexistent item, in following frames:
- xItem becomes a Null reference.
- PrintToConsole “%i %n” xItem xItem - prints 0 NULL.
- xItem.GetBaseObject – returns Null.
- xItem.SetPos – Sets new XYZ positions to the object where the script is running (as xItem==Null, assumes Self??).
- xItem.MoveToMarker – does not seem to do anything.
Conclusions
It seems that after an item is removed from the world as described above, all its data remains intact and can be manipulated by scripts in the same frame they are removed.
Using a reference to the now inexistent item, in following frames:
- The functions tested did not crash the game.
- Most of the time, it seems the function does nothing.
- But sometimes, have undesired results (as in the SetPos).
Bloating
Methodology
For each of the three types of dynamic items considered, the test consisted of:
- Placing 1.000 items in the world (either by dropping from a container or by PlaceAtMe, one item per frame)
- Placing 1.000 more items in the world (same)
- Moving 1.000 items to an NPC (Single frame GetNextRef loop to identify the item and then NPC.Activate Item)
- Moving 1.000 items to an NPC (same frame as above)
- Using RemoveAllItems on the NPC
- Going to an exterior cell
- Waiting 4 days
- Returning to the test cell
- Saving the game at the beginning and after each of the steps above
- NOTE: 2000 items seems to be the limit for my hardware to handle (down to about 3-5 FPS)
Test results
The table below shows the variation (in bytes) in savegame size after each step of the test.
The column “With RemoveAllItems” shows the results of the test as initially intended.
Since the RemoveAllItems didn’t seem to reduce savegame, I re-ran all tests without using it. The results are in the last column
With | Without | |
RemoveAllItems | REmoveAllItems | |
After adding 1000 items | +78.373 | |
After adding 2000 items | +78.416 | |
After moving 1000 items to container | -22.670 | -22.959 |
After moving 2000 items to container | -23.014 | -23.014 |
After RemoveAllItems | -18 | |
After going to exterior | +13.801 | +14.316 |
After Waiting 4 days | -21.218 | -24.561 |
After returning to cel | -5.265 | -2.968 |
Net result | +98.405 | +97.604 |
Test details
Tests were run 6 times, two times for each of the creation method mentioned earlier.
Individual runs did’t vary much from each other, so they are not included here.
The initial size of the savegame is 2.983.546 bytes.
All runs started from the same clean save.
Tests where done in an additional Divine Elegance basement. The “Going to exterior” step means leaving the basement, crossing DE cell and activating the exit door. Saves where made immediately after the exterior cell was loaded.
Conclusions
(already mentioned at the beginning)