Difference between revisions of "Papyrus FAQs"

From the Fallout4 CreationKit Wiki
Jump to navigation Jump to search
imported>Digitalparanoid
imported>Digitalparanoid
Line 307: Line 307:


=== When I call Activate() on a stack of items dropped by the player, only one item in the stack is returned. ===
=== When I call Activate() on a stack of items dropped by the player, only one item in the stack is returned. ===
This is a known issue, but it is unknown whether there will be a fix. If there is a fix, there will be an alternate function.
According to SmkViper:
<pre>
We don't add or change parameters to existing functions after they've been
released (because it would require all scripts using that function to be changed
or recompiled). That's not to say new functions cannot be added, but no promises.
Because of the heavy use of Activate in scripts, I'm very wary of changing its
behavior, but I'll look into it. An alternative solution might be to add an
ActivateAll function instead of "fixing" Activate.
</pre>
=== Why do the FindAllReferences* functions appear to return references in the player's inventory? ===
=== Why do the FindAllReferences* functions appear to return references in the player's inventory? ===
=== Does the FindAllReferencesWithKeyword function accept a Formlist as a parameter? ===
=== Does the FindAllReferencesWithKeyword function accept a Formlist as a parameter? ===

Revision as of 12:31, 11 May 2016

Resources

Why doesn't the Creation Kit's script editor have syntax highlighting?

The Creation Kit allows you to open scripts with an external text editor from the context menu, so you should use an external text editor if you want syntax highlighting and other features.

Where can I find a list of functions and their syntax that I can use in my scripts?

You should refer to the wiki and ultimately the source scripts, like Actor.psc, ObjectReference.psc, and Utility.psc. These are the "classes" that your scripts typically "extend."

Where can I find examples of how to accomplish <something> or how to use a function?

Using a free third-party program named Agent Ransack, you can search through all of the base scripts almost instantly for examples, especially with a SSD.

To whom do I send fan/hate mail about Papyrus?

SmkViper created Papyrus and actively helps scripters in the official Fallout 4 Creation Kit forum.

First Steps

Where do I find the source scripts?

  1. In the Data\Scripts directory, there is a ZIP archive named Base.zip.
  2. Extract that file using 7-Zip, WinZip, or Windows' native ZIP handler to the same folder with paths.

Why can't the Creation Kit find the source scripts?

You should have the following folders in the Data\Scripts directory:

  • Data\Scripts\Source\Base
  • Data\Scripts\Source\User

In the Base folder, you should find all of the scripts you've extracted from Base.zip. The User folder is where all of your source scripts should and will be placed.

Where should compiled scripts be placed?

Compiled scripts do not belong anywhere in the Fallout 4\Data\Scripts\Source folder or subfolders. All compiled scripts should be placed in one of the following directories:

  • Data\Scripts
  • Data\Scripts\Fragments\*
  • Data\Scripts\<namespace>\*

Compiling

How else can I compile scripts?

  • You can compile scripts through the Script Manager. In the Script Manager, right-click the script and compile only that script.
  • You can compile scripts outside the Creation Kit by calling the compiler directly from the command shell.

If I change the source script, do I need to recompile that script?

Yes. If you make changes to a .psc (Papyrus Source) file and never recompile, the game will always use the older .pex (Papyrus Executable) file, and your changes will never take effect.

When I try to compile scripts outside the Creation Kit, my scripts always fail to compile. I'm sure my syntax is correct!

For the sake of simplicity, use fireundubh's Windows batch script (.bat file) for compiling scripts outside the Creation Kit.

  1. Save compile_fo4.bat to C:\Scripts. Create the directory if needed.
  2. Update the paths at the top of the .bat script as needed.
  3. Add C:\Scripts to your system's PATH environment variable. (Control Panel > System > Advanced System Settings > Environment Variables)

With this script, you can run the following commands from the command shell:

compile_fo4                       // compile all scripts in the current directory recursively and output the compiled scripts to the specified output directory  
compile_fo4 script.psc            // compile a single script in the current directory and output the compiled script to the specified output directory  
compile_fo4 script.psc --test     // the --test parameter will delete the compiled script after successfully compiling the script (useful for testing whether a script compiles)  
compile_fo4 C:\path\to\script.psc // compile a single script using the absolute path to the script and output the compiled script to the specified output directory  
compile_fo4 project.ppj           // compile the specified project file in the Projects directory  

What are all of the parameters for PapyrusCompiler.exe? What do they mean?

You can run PapyrusCompiler.exe on the command line without any parameters to get a list of all of the parameters and what they mean. That list has been replicated here.

Papyrus Compiler Version 2.8.0.4 for Fallout 4
Copyright (C) ZeniMax Media. All rights reserved.
Usage:
PapyrusCompiler <object, folder, or project> [<arguments>]

  object     Specifies the object to compile. (-all is not specified, with a
             .psc or no extension)
  folder     Specifies the folder to compile. (-all is specified)
  project    Specifies the project to compile. (Has a .ppj extension)
  arguments  One or more of the following (overrides project, if any):
   -debug|d
    Turns on compiler debugging, outputting dev information to the screen.
   -release|r
    Turns on release-mode processing, removing debug code from the script.
   -final
    Turns on final-mode processing, removing beta code from the script.
   -optimize|op
    Turns on optimization of scripts.
   -output|o=<string>
    Sets the compiler's output directory.
   -import|i=<string>
    Sets the compiler's import directories, separated by semicolons.
   -flags|f=<string>
    Sets the file to use for user-defined flags.
   -all|a
    Invokes the compiler against all psc files in the specified directory
    (interprets object as the folder).
   -norecurse
    Used with -all to tell the compiler not to recurse into subdirectories.
   -ignorecwd
    Tells the compiler to ignore the current working directory when searching
    for files. Otherwise it is implied to be first in the import list.
   -quiet|q
    Does not report progress or success (only failures).
   -noasm
    Does not generate an assembly file and does not run the assembler.
   -keepasm
    Keeps the assembly file after running the assembler.
   -asmonly
    Generates an assembly file but does not run the assembler.
   -?
    Prints usage information.

What is "debug code" and "beta code"?

Two new properties have been introduced in the Fallout 4 version of Papyrus: DebugOnly and BetaOnly. These properties can be declared at the function and script levels.

  • When release-mode processing is enabled, the compiler will remove DebugOnly functions from the compiled script. The source file will remain untouched.
  • When final-mode processing is enabled, the compiler will remove BetaOnlyfunctions from the compiled script. The source file will remain untouched.

These properties are useful when you want to run code during development that you don't want to run when your mod is in the wild.

Why doesn't my script output any debug messages to the Papyrus log?

The compiler is processing your script in release mode. The Debug script has the DebugOnly property, so all Debug functions (e.g., Debug.Notification, Debug.Trace) will be removed from your script when compiled.

The batch script provided above, and the Papyrus Project template provided below, enables release-mode and final-mode processing. To use DebugOnly and/or BetaOnly code, you'll need to modify these files as needed.

None of my scripts are running! / The Papyrus log indicates they're missing.

Add to %USERPROFILE%\Documents\My Games\Fallout4\Fallout4Custom.ini:

[Archive]    
bInvalidateOlderFiles=1

This will allow the game to load "loose files," which includes your scripts. Also, ensure that the paths to your compiled scripts are correct.

What is a namespace? How do I configure my mod to use a namespace?

For our purposes, a namespace is just a subfolder where your scripts are located for a specific mod in the User folder. Namespaces help you stay organized.

To set up a namespace, follow these instructions:

  1. While in the Creation Kit: go to File > Preferences and find the Scripts tab.
  2. In the box labeled Default namespace, enter the name of your namespace without any spaces or special characters.
  3. Click Apply and then Close.

From now on, any scripts you create with the Creation Kit will be placed in that namespace folder, and the ScriptName declaration in each script will be prefixed with the name of your namespace.

When I try to compile namespaced scripts outside the Creation Kit, my scripts always fail to compile. Help!

Namespaced scripts and scripts with paths, usually fragment scripts, should be compiled using a Papyrus Project file, which is a plain text XML file with the .ppj extension that the Papyrus compiler natively understands.

PapyrusProject.xsd in the Fallout 4\Papyrus Compiler directory defines the XML schema for Papyrus Project files. Here is an example of a Papyrus Project file:

<PapyrusProject xmlns="PapyrusProject.xsd" Flags="Institute_Papyrus_Flags.flg" Output="D:\Steam\steamapps\common\Fallout 4\Data\Scripts" Optimize="true" Release="true" Final="true">  
  <Imports>  
    <Import>D:\Steam\steamapps\common\Fallout 4\Data\Scripts\Source\Base</Import>  
    <Import>D:\Steam\steamapps\common\Fallout 4\Data\Scripts\Source\User</Import>  
  </Imports>  
  <Scripts>  
  <Script>AutoLoot\Fragments\Terminals\TERM_dubhAutoLootMenuAdvance_0100272C.psc</Script>  
  ...  
  </Scripts>  
</PapyrusProject>

Logging

How do I enable the Papyrus log?

You can enable the Papyrus log by adding the following lines to %USERPROFILE%\Documents\My Games\Fallout4\Fallout4Custom.ini:

[Papyrus]
fPostLoadUpdateTimeMS=500.0000
bEnableLogging=1
bEnableTrace=1
bLoadDebugInformation=1

Can the Papyrus log help me figure out why Fallout 4 is crashing?

The Papyrus log is not a crash log. There is a warning at the top of the log now that states this quite clearly. The Papyrus log is not likely to point at what was going on near the crash because if a crash occurs, the game will be unable to write to the log.

Advanced

Are While loops safe to use for persistent or continuous effects?

According to SmkViper:

Saves operate generally the same way they did with Skyrim. Any running threads
are saved so they can be resumed when the save is loaded. This means any while
loops running will be saved into the save game, because of course they need to
continue running when the save is loading. However because they are saved, any
patch to the function that you release in a mod can't be applied to that thread
until it exits the function (which means the while loop has to end).

While loops also keep the thread alive, which consumes some memory, as the
thread and all its stack data and calling functions stack data need to stick
around. If you end up with >100 threads running at once for over a few seconds,
Papyrus assumes something is wrong and starts spitting out stack dumps to the
log so you can find and fix the problem. If the while loops aren't sleeping (via
Wait or some other latent call) they can contribute to script lag as the VM has
to take time to process the thread in addition to everything else it's running.

Timers replace OnUpdate partially in response to the problems with continual
update event registrations (they are equivalent to single update registrations).
And because it's sometimes really handy to have multiple timers going at once on
different intervals.

So yes, if you need something running over a long period of time, you should be
using timer events.

All that being said, F4's version of Papyrus is much more aggressive about
cleaning out dead data if things go... missing. Not that any self-respecting
modder would do that.

How do I write save-safe scripts then?

According to SmkViper:

Removing mods and continuing with a save that relies on that mod is not and
never has been supported. Proceed at your own risk. Demons may fly out of your
monitor and spray you with silly string. (You think I'm kidding - have you heard
of Bob the modder? No? Exactly.)

However, here are some evolving guidelines that can be followed to reduce the incidence of uninstallation issues and/or shift the blame to user error:

  1. Instead of using a While loop, use an OnTimer() event. The OnTimer() event is Fallout 4's version of Skyrim's OnUpdate(). Refer to the question and answer below for more information.
  2. Avoid endless While loops that cannot terminate after the plugin has been removed. For example, a While Player.HasPerk(kPerk) loop can terminate if kPerk references a perk unique to the plugin. Removing the plugin removes the perk, and so when the player loads a save, the thread can then exit. However, if kPerk references a vanilla perk, HasPerk() will always evaluate to True and the While loop will continue iterating over instructions that may require objects which no longer exist, and therefore keep the thread alive, consuming memory, contributing to script lag, and producing errors. Refer to the above question for more information about how While loops and saves interact.
  3. Always finish your code as fast as possible. This ultimately means making optimizations to both your scripts and your systems. For example, if you need a While loop to iterate through a large formlist or array, chunk the formlist or array, and in between each chunk, give the loop an opportunity to exit. You might also want to ensure that there are enough conditions in place that your script can run only when needed, do its job, and get out when the job's done.
  4. Provide the user with the ability to "uninstall" your mod while in the game. This can be done by offering users a "Shutdown" option in a holotape menu that forcibly exits all continuously running scripts. Some users may not take the time to take advantage of this feature, so it remains important to ensure that you've taken the necessary steps to write save-safe scripts.
  5. Finally, warn users that uninstalling mods and continuing with saves that rely on those mods is unsupported and unsafe.

How would I emulate a Break statement in a While loop?

You can emulate a Break statement like so:

Bool bBreak = False  
While (<primary_condition>) && !bBreak    
  // do stuff    
  If <secondary_condition>    
    bBreak = True    
  EndIf      
EndWhile

The alternative is to use Orvid's Caprica compiler, which allows you to use the For, ForEach, Break, and Continue statements.

Please remember that Caprica is an unofficial, alternative compiler that is not supported by Bethesda Softworks. Any questions or issues regarding Caprica should be directed to Orvid.

Are scripts saved in save games?

According to SmkViper:

Scripts are not saved in your save game. Script variable values and currently running functions are saved so they can be resumed.

What's the alternative to OnUpdate, RegisterForUpdate, and RegisterForSingleUpdate?

You can use the OnTimer event alongside the StartTimer and CancelTimer functions.

Are arrays limited to 128 elements?

According to SmkViper:

There is still an internal 128 limit on array items. Attempting to make an array larger than that will spit out an error at runtime.

How do I create a dynamic array?

According to SmkViper:

Arrays are now dynamic. To create an empty array, pass 0 as the size. You are
now allowed to pass in a variable or calculation for an array size, and you can
call add and remove on the array to add and remove items.

How do I create key-value pairs?

According to SmkViper:

You can basically fake a map using structs and the new FindStruct function. Just
make one struct variable your key, and the other the value, and then run Find on
the key. (Unlike maps, duplicates are permitted since it's just an array.)

When I call Activate() on a stack of items dropped by the player, only one item in the stack is returned.

This is a known issue, but it is unknown whether there will be a fix. If there is a fix, there will be an alternate function.

According to SmkViper:

We don't add or change parameters to existing functions after they've been
released (because it would require all scripts using that function to be changed
or recompiled). That's not to say new functions cannot be added, but no promises.

Because of the heavy use of Activate in scripts, I'm very wary of changing its
behavior, but I'll look into it. An alternative solution might be to add an
ActivateAll function instead of "fixing" Activate.

Why do the FindAllReferences* functions appear to return references in the player's inventory?

Does the FindAllReferencesWithKeyword function accept a Formlist as a parameter?

Can structs store arrays, other structs, and var types? Can arrays store arrays?

Can CallFunction() be used for reflection?

How do I use custom events instead of CallFunction()?

I can't figure out what scripts govern the supply lines! Halp.