Difference between revisions of "Talk:GetFormFromFile - Game"

From the CreationKit Wiki
Jump to navigation Jump to search
imported>JustinOther
Line 146: Line 146:
:See also: [[Arrays_(Papyrus)#Creating_a_FormID_Array|FormID Array]]
:See also: [[Arrays_(Papyrus)#Creating_a_FormID_Array|FormID Array]]
:- [[User:JustinOther| JustinOther]] ([[User talk:JustinOther|talk]])
:- [[User:JustinOther| JustinOther]] ([[User talk:JustinOther|talk]])
== Inconsistent Behavior ==
I have observed on multiple occasions that this method may return None for existing objects. For example, the following line of script sometimes works and sometimes produces "Error: Cannot call Disable() on a None object, aborting function call" in the Papyrus log:
<nowiki>(Game.GetFormFromFile(0xC8FCE, "Skyrim.esm") as ObjectReference).Disable()</nowiki>
It may be worth noting that this specific reference is temporary, not persistent.

Revision as of 03:53, 28 January 2023

GetModLoaded

In the example on the page, the form ID 0x800 is used to test whether or not a mod is loaded. In my experience, a mod's first form has form ID 0x800 only if you're creating it without having loaded any masters.

If any other files have been loaded, then the form ID is usually 0xD62. Perhaps it would be better to re-write the example as

If bDLCArray[aiIndex] != (Game.GetFormFromFile(0x00000800, sDLCArray[aiIndex]) \
			|| (Game.GetFormFromFile(0x00000D62, sDLCArray[aiIndex]))

Of course, even that might not work if the mod creator had deleted the very first object he/she created in the mod. --Fg109 01:37, 27 June 2012 (EDT)


As it turns out, Dawnguard has an 0x00000800, but D62 does seem to be pretty consistent. Good call, Fg109. At that, the function can be condensed and, in most cases, remain effective. I ended up adding an Int array to take out the guesswork.
JustinOther (talk)

Appropriate Use

Since I first started scripting with Papyrus, I've tried to treat "Keep the script blind to information in the data files" as a rule, or at least best practice, exclusively using Properties as the script's interface with data files and avoiding the use of functions such as GetForm. To date, I have seen no good reason to change this behaviour.

While at first glance it seems as though this rule should also lead me to entirely neglect this function, I can see one legitimate use for it: Enabling compatibility with mods without setting them as masters. The advantage of this is that only a single data file is required for any combination of loaded mods that would otherwise have been masters, and I expect it also decreases dependency on load order.

Is anyone able to think of any other ways in which this function could be used that couldn't be replicated with Properties alone without downsides? At the moment, just like I feel all use of GetForm should be discouraged, I feel as though all use of this function aside from the one I mentioned above should also be discouraged.

-- Cipscis (talk) 23:14, 21 August 2012 (EDT)

So far, I've not found any other reason to use these over Properties directly pointed at the other plugins' forms than to avoid said master dependency. It does work regardless of load order, so a mod can reach 'down' as well as 'up'. So far, I've only used it with FormID arrays to add Dawnguard compatibility in order to avoid adding it as a secondary/tertiary master. I agree though, that it's best to not use GetForm/GetFromID/GetFormFromFile unless it's actually necessary. In those cases, I've no qualms using it.
JustinOther (talk)

Bug when 2 mods try to access the same Quest of a missing file via GetFormFromFile

Note: This bug has been proven by Mardox not to be with GetFormFromFile in this Bethesda CK forum post
The bug exists when the 2 mods refer to a non-existent Script at run-time.
I.e. Simply declaring a variable as a Script that doesn't exist at run-time is sufficient to produce the bug.

The test

Mod A, B and C are independent of each other. I.e. they have no dependency between each other.

However, Mods B and C adds functionality to A if A is active and installed.

I.e. Mod A is not a requirement but Mod B and C's adds features to A if it is active.

All this supposedly can be achieved by using Game.GetFormFromFile (Int formId, String ESPFilename).

Here's a ZIP of my tests: File:KuerteeGetFormFromFileTest.zip

Results

When all three are active and installed

All good. Mod A received the function calls from Mods B and C. All-three-mods-present.jpg

When A is not active and not installed

Quest B runs fine as shown with the 'questA' variable set to None. Status-of-quest-b-with-a-missing.jpg

However, Quest C simply failed to load - as shown with the error "No Papyrus Scripts attached". Status-of-quest-c-with-a-missing.jpg

Conclusion

GetFormFromFile is bugged.

The first mod that tries to access that missing Mod with GetFormFromFile is fine and works with no problems.

However, the second mod (and possibly any other) that tries to access that missing mod will always fail.

True-life case

My mods Battle Fatigue and Injuries (BFaI) and Professions affect a third mod, Eat and Sleep (EaS) mod.

Each mod is designed to be installed, activated and to work on their own.

However, if EaS is active, then BFaI and/or Professions calls functions in Eat and Sleep.

If one of either BFaI or Profession is installed WITHOUT EaS, then that installed mod works.

However, if both BFaI AND Professions are installed WITHOUT EaS, the one or the other fails.

Of course, if EaS is installed, any combination of the two works.

Script codes

Quest A

Scriptname kuGFFFAQS extends Quest  

Function showMessage (String fromWhere)
	Debug.Notification ("Quest in A received message from " + fromWhere)
EndFunction

Quest B

Scriptname kuGFFFBQS extends Quest  

kuGFFFAQS questA

Event OnInit ()
	RegisterForSingleUpdate (1)
EndEvent

Bool done
Event OnUpdate ()
	If !done
		questA = Game.GetFormFromFile (0x000012c5, "GetFormFromFileA.esp") as kuGFFFAQS
		If questA
			questA.showMessage ("B")
			done = True
		EndIf
		RegisterForSingleUpdate (1)
	EndIf
EndEvent

Quest C

Scriptname kuGFFFCQS extends Quest  

kuGFFFAQS questA

Event OnInit ()
	RegisterForSingleUpdate (1)
EndEvent

Bool done
Event OnUpdate ()
	If !done
		questA = Game.GetFormFromFile (0x000012c5, "GetFormFromFileA.esp") as kuGFFFAQS
		If questA
			questA.showMessage ("C")
			done = True
		EndIf
		RegisterForSingleUpdate (1)
	EndIf
EndEvent

--Kuertee (talk) 06:01, 3 December 2012 (EST)

No need to bother with hex-dec conversion.

(...) For this to work, the Int[] elements must match known existing FormIDs in the corresponding plugins converted from Hex to Dec (easily done with Windows' calculator in programmer mode)

This is inaccurate because there is nothing preventing one from initializing an int (array) with a hex digit as in any other programing / scripting language.


E.G:

int[] dec = new int[1]
int[] hex = new int[1]

dec = 10
hex = 0xa

--Tox2ik (talk) 20:52, 17 January 2013 (EST)

Int array elements cannot be set to hex as only numerals are accepted, thus the need to convert to dec. You can initialize an Int property or variable in hex (in your source code) and the compiler converts it for you, but Int[] elements do need to be converted to dec if they're to be predefined.
See also: FormID Array
- JustinOther (talk)

Inconsistent Behavior

I have observed on multiple occasions that this method may return None for existing objects. For example, the following line of script sometimes works and sometimes produces "Error: Cannot call Disable() on a None object, aborting function call" in the Papyrus log:

(Game.GetFormFromFile(0xC8FCE, "Skyrim.esm") as ObjectReference).Disable()

It may be worth noting that this specific reference is temporary, not persistent.