Jump to content

How to completely delete an object (reference) from the game.


lee3310

Recommended Posts

For the records, i was able to properly dispose of almost all the persistent dead refs using those functions:

If GardenOfEden2.SetTemporaryReference(AllDeadRefs.GetAt(Count), true)
	if GardenOfEden2.RemoveAllPersistentPromoters(AllDeadRefs.GetAt(Count))
		GardenOfEden2.SetEditorPersistence(AllDeadRefs.GetAt(Count), False)
	Else
		GardenOfEden2.SetEditorPersistence(AllDeadRefs.GetAt(Count), False, true)
	EndIf
EndIf

I said almost cause i was down to 61 not 0 (from 10231), the remnants were held by a MGEF script that has an Actor variable pointing at the ref (could be fixed by editing the script itself and clearing the var).
So problem solved, all attached instances were removed along the refs and the game can finally save and load correctly as far as i can tell.
PS
More details can be found here.

Edited by lee3310
Link to comment
Share on other sites

 

The game sometimes don't remove references that should be removed, causing them to exist (persist..) in the save forever.

Some questions. Does the above also mean that a ref may get stuck even if totally clean and not referenced by anything? I.e. may be fully unpredictable and buggy occassion. Things mentioned in topic had a sort of valid reasons, as i see.

 

"Ref1.SetLinkedRef(Ref2) ( regardless of akKeyword ) should add Ref1 to the persistent promoters of Ref2."

Does a ref become persistent on its turn, when SetLinkedRef is called on it, then keeps as such until SetLinkedRef(none)?

Link to comment
Share on other sites

 

 

The game sometimes don't remove references that should be removed, causing them to exist (persist..) in the save forever.

Some questions. Does the above also mean that a ref may get stuck even if totally clean and not referenced by anything? I.e. may be fully unpredictable and buggy occassion. Things mentioned in topic had a sort of valid reasons, as i see.

 

The bug I mentioned causes persistence to stuck on a reference forever, which prevents it from getting auto cleaned up.

 

The issue affects inventory items as well object references. Certain RemoveItem() scenarios, if the inventory item has an internal linkage to a ( IsDeleted() && IsCreated() ) persistent reference ( these are the items that act "as if they had" a RefID; "picked up" persistent refs ), the picked up object reference that is "bound" to the item may never get cleaned up. Haven't investigated the exact cause.. though I remember ( RemoveItem(akOtherContainer = SomeRef) ) and ( Ref = Inventory.DropObject(Ref.GetBaseObject() then Ref.Disable(), Ref.Delete() ) fixed it.

 

As for object references, I've seen ActiveMagicEffects stuck on the "effect actor" ( GetTargetActor() ) if the actor was in a script property by the time OnEffectFinish is received, preventing the VM from discarding the created script instance and the effect from getting properly dispelled. Didn't matter if the actor received OnUnload or OnDeath afterward. So [PP] and the actor couldn't be removed normally.

 

In both cases, there's no "real" reason to keep persistence on the reference but the game fails to remove the ref.. whether it's by design or a bug, I don't know. It inevitably leads to save game bloating.

 

 

"Ref1.SetLinkedRef(Ref2) ( regardless of akKeyword ) should add Ref1 to the persistent promoters of Ref2."

Does a ref become persistent on its turn, when SetLinkedRef is called on it, then keeps as such until SetLinkedRef(none)?

 

Yes. If Ref2 wasn't persistent before calling Ref1.SetLinkedRef(Ref2), then it'll become persistent. Whether it looses [PP] on SetLinkedRef(none) depends on whether it received another persistence promoting event from any Form, like RandomQuestAlias.ForceRefTo(Ref2). If it didn't, then unlinking should remove persistence. You can verify it with this small script:

Function Link(ObjectReference Ref1, ObjectReference Ref2) global
	Ref1.SetLinkedRef(Ref2)
EndFunction

Function Unlink(ObjectReference Ref1, ObjectReference Ref2) global
	Ref1.SetLinkedRef(None)
EndFunction

Choose non-persistent refs as Ref1 and Ref2. Preferably not ones that have EnableParent. De/reselect Ref2 in the console after calling Link and Unlink. ( Waiting a few seconds may help, persistence handling is not instantaneous in the game ).

Link to comment
Share on other sites

 

 

 

The game sometimes don't remove references that should be removed, causing them to exist (persist..) in the save forever.

Some questions. Does the above also mean that a ref may get stuck even if totally clean and not referenced by anything? I.e. may be fully unpredictable and buggy occassion. Things mentioned in topic had a sort of valid reasons, as i see.

 

 

The bug I mentioned causes persistence to stuck on a reference forever, which prevents it from getting auto cleaned up.

 

 

The issue affects inventory items as well object references. Certain RemoveItem() scenarios, if the inventory item has an internal linkage to a ( IsDeleted() && IsCreated() ) persistent reference ( these are the items that act "as if they had" a RefID; "picked up" persistent refs ), the picked up object reference that is "bound" to the item may never get cleaned up. Haven't investigated the exact cause.. though I remember ( RemoveItem(akOtherContainer = SomeRef) ) and ( Ref = Inventory.DropObject(Ref.GetBaseObject() then Ref.Disable(), Ref.Delete() ) fixed it.

 

As for object references, I've seen ActiveMagicEffects stuck on the "effect actor" ( GetTargetActor() ) if the actor was in a script property by the time OnEffectFinish is received, preventing the VM from discarding the created script instance and the effect from getting properly dispelled. Didn't matter if the actor received OnUnload or OnDeath afterward. So [PP] and the actor couldn't be removed normally.

 

In both cases, there's no "real" reason to keep persistence on the reference but the game fails to remove the ref.. whether it's by design or a bug, I don't know. It inevitably leads to save game bloating.

 

 

I'd say there seem be reasons still, at least seeming technical clues for engine to become worried about. Although in cases like script-scope runtime variable in PowerArmorFX effect that would be very unfortunate and unexpected behavior indeed.

But for a specific example, an object placed by Explosion (no Persists flag) and having a script to delete itself on timer (well, unfortunately, no choice here, because AutoFade doesn't really work, while keeping in mind orphaned scripts instances possiblitiy it should be better to avoid embedded VMAD), the script uses Self statement, but not filled into a variable. Or similar "independent" temporary objects without current dependencies. How high is chance to stick forever just by a sudden oversight of cleanup processor?

 

 

 

hereami, on 22 Oct 2023 - 02:22 AM, said:

 

"Ref1.SetLinkedRef(Ref2) ( regardless of akKeyword ) should add Ref1 to the persistent promoters of Ref2."

Does a ref become persistent on its turn, when SetLinkedRef is called on it, then keeps as such until SetLinkedRef(none)?

 

Yes. If Ref2 wasn't persistent before calling Ref1.SetLinkedRef(Ref2), then it'll become persistent.

 

Whether it looses [PP] on SetLinkedRef(none) depends on whether it received another persistence promoting event from any Form, like RandomQuestAlias.ForceRefTo(Ref2). If it didn't, then unlinking should remove persistence. You can verify it with this small script:

Function Link(ObjectReference Ref1, ObjectReference Ref2) global

Ref1.SetLinkedRef(Ref2)

EndFunction

Function Unlink(ObjectReference Ref1, ObjectReference Ref2) global

Ref1.SetLinkedRef(None)

EndFunction

Choose non-persistent refs as Ref1 and Ref2. Preferably not ones that have EnableParent. De/reselect Ref2 in the console after calling Link and Unlink. ( Waiting a few seconds may help, persistence handling is not instantaneous in the game ).

 

 

Well, i had to be more specific perhaps, Ref1 i meant, which holds the link. I've seen there's mention of Promoters array, and by the way, is it a global structure or local on each target (i.e. Ref2s)?

CreationKit insists on targeted REFR only to be persistent (Enable/Activate Parents and linkedRef), not Link holders themselves though.

Basically, question here - is it safe to Delete a Promoter and not worry about the linking data it holds to misbehave without explicit SetLinkedRef(NULL) (assuming Promoter is trouble-free by itself and not treated as persistent just because it became a PersistencePromoter)?

 

Thank you!

Link to comment
Share on other sites

Just checked the 569 SetLinkedRef to an ObjectReference statements in my Fallout 4 scripts.

 

They only ever create a linkedref from objects that have a cleanup trigger event like OnDeath or OnWorkshopObjectRemoved so the script can always force clean up of any created linkedrefs. Yes there are more than 569 SetLinkedref(None, ...) statements.

 

And zero save game bloat has ever been associated with my > 2.5 million script installs.

Link to comment
Share on other sites

 

 

 

 

The game sometimes don't remove references that should be removed, causing them to exist (persist..) in the save forever.

Some questions. Does the above also mean that a ref may get stuck even if totally clean and not referenced by anything? I.e. may be fully unpredictable and buggy occassion. Things mentioned in topic had a sort of valid reasons, as i see.

 

 

The bug I mentioned causes persistence to stuck on a reference forever, which prevents it from getting auto cleaned up.

 

 

The issue affects inventory items as well object references. Certain RemoveItem() scenarios, if the inventory item has an internal linkage to a ( IsDeleted() && IsCreated() ) persistent reference ( these are the items that act "as if they had" a RefID; "picked up" persistent refs ), the picked up object reference that is "bound" to the item may never get cleaned up. Haven't investigated the exact cause.. though I remember ( RemoveItem(akOtherContainer = SomeRef) ) and ( Ref = Inventory.DropObject(Ref.GetBaseObject() then Ref.Disable(), Ref.Delete() ) fixed it.

 

As for object references, I've seen ActiveMagicEffects stuck on the "effect actor" ( GetTargetActor() ) if the actor was in a script property by the time OnEffectFinish is received, preventing the VM from discarding the created script instance and the effect from getting properly dispelled. Didn't matter if the actor received OnUnload or OnDeath afterward. So [PP] and the actor couldn't be removed normally.

 

In both cases, there's no "real" reason to keep persistence on the reference but the game fails to remove the ref.. whether it's by design or a bug, I don't know. It inevitably leads to save game bloating.

 

 

I'd say there seem be reasons still, at least seeming technical clues for engine to become worried about. Although in cases like script-scope runtime variable in PowerArmorFX effect that would be very unfortunate and unexpected behavior indeed.

But for a specific example, an object placed by Explosion (no Persists flag) and having a script to delete itself on timer (well, unfortunately, no choice here, because AutoFade doesn't really work, while keeping in mind orphaned scripts instances possiblitiy it should be better to avoid embedded VMAD), the script uses Self statement, but not filled into a variable. Or similar "independent" temporary objects without current dependencies. How high is chance to stick forever just by a sudden oversight of cleanup processor?

 

I haven't encountered similar problems with Object Reference scripts. If ( Explosion >> Placed Object >> "SomeObject" ) has an attached Object Reference script to it, unless another Form makes the ref persist, it should be completely deleted with Disable() and Delete(). "Placed Object Persists" makes the ref non temporary like ( Quest Alias >> Created Ref >> Temp [unchecked] ) but that only means they are excluded from the auto cleanup, they can be fully deleted with Delete().

 

 

 

Well, i had to be more specific perhaps, Ref1 i meant, which holds the link. I've seen there's mention of Promoters array, and by the way, is it a global structure or local on each target (i.e. Ref2s)? CreationKit insists on targeted REFR only to be persistent (Enable/Activate Parents and linkedRef), not Link holders themselves though.

Basically, question here - is it safe to Delete a Promoter and not worry about the linking data it holds to misbehave without explicit SetLinkedRef(NULL) (assuming Promoter is trouble-free by itself and not treated as persistent just because it became a PersistencePromoter)?

 

Thank you!

 

It's reference data yes, in the class BSExtraData ( the list was written for Skyrim but PromotedRef hasn't been changed ( probably the offset of the Form array did but I don't know )... it's data type = 140 UInt ).

 

Many local object reference data is there, for example the EnableParent too. How's the actual ExtraData object treated by the code is not consistent.. I decompiled the EP ref for example ( data type = 54; native handle ID is stored somewhere there ) but SetEnableParent(someRef) didn't work out ( unlike GetEnableParent() ) because the game always loaded the one from the editor. TextDisplayData ("custom name") on the other hand is stored in save games as it was designed to be changed ingame ( even "forever", see Quest Alias >> DisplayName >> SomeMessage >> Clear Name When Removed [unchecked] ). Maybe EP does support it too but I haven't found where. ( Editing certain ExtraData incorrectly can easily lead to CTD upon saving/loading a save so the risks are quite high ).

 

As for the persistence of Ref1.. well, I haven't figured out why sometimes Ref1.SetLinkedRef(Ref2) causes Ref1 to become its own promoter. Sometimes it does, sometimes it doesn't. I wonder if ActorCause has anything to do with any of this.. I have some theories but one of them would need to be proven first and I haven't gotten around to it... few days ago I finally decided to play Starfield. :smile:

 

I think the game would reapply [PP] as it does automatically anyway but I haven't actually tested how removing persistence affects the link between distant references. Generally, I wouldn't recommend deleting Forms from the promoter array or clearing it completely either. It's only useful if one is 100% sure the reference "should be" removed ( has checked it thoroughly with DPPI, Fallrim Tools, etc. ) and would really like to remove it. Promoter form Ref1 is kept in the array of Ref2 to let the game know Ref2 is required to be persistent. If the cause of the persistence ceases ( e.g., all promoters get Delete() ), Ref2 ( in theory ) would no longer need to be persistent and would become non-persistent unless the game applied [EP] on it ( it's not just the Form flag REFR / ACHR >> Record Flags ) that can do that; developers designed ForcePersistent to make ref its own promoter ref but of course they also come up with other ideas to apply persistence... ). SetLinkedRef didn't break when I removed promoters but I can't state that manually taking over the array is safe..

 

 

Sorry for the long post mostly about "what is not safe" but it really isn't safe and even the author of Fallrim Tools ( who has most certainly more knowledge in what and how is stored in save games than me ) states editing save games is not "safe". Well, editing persistence isn't either. I wouldn't like to be cause of persistent CTDs due to RemoveAllPersistentPromoters(). :smile: I added it to the script extender so that lee3310 could remove their problematic persistent references. Could be used for the same purpose by anyone of course but carefully and the actual reference has to be thoroughly examined which is only available to the person who has the save game.

Edited by LarannKiar
Link to comment
Share on other sites

Lee3310/LarannKiar - do either of you have a program/script/mod that you have created to lists all the persistent objects (and type/providers etc)?

You have me very interested to see what mess my mods may be leaving behind (even though I am very rigourous at cleanup).

I suspect there is enough info in this discussion to make my own (using Garden of Eden functions) - but why re-invent the wheel?

 

Thanks!

Link to comment
Share on other sites

Interesting fact: bethesda have clearly recognised the issue of LinkRef promoting target persistence as that is now an explicit parameter in the Starfield version of Papyrus:

 

Fallout4

SetLinkedRef(ObjectReference apRef, Keyword apKeyword = None)

Starfield

SetLinkedRef(ObjectReference akLinkedRef, Keyword apKeyword, Bool abPromoteParentRefr) 

WARNING FOR FUTURE ADHD INATTENTIVE TYPE SEARCHES: that is a Starfield Papyrus function, not Fallout4

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...