Fantasy Grounds Merchandise
View RSS Feed


A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script Revisited

Rate this Entry
In a previous post I detailed how I reduced duplicated lua script in my fledgling extension. Play testing the extension revealed a design error. The issue: Windows cannot resolve a folder path into a ruleset .pak file. (For background information, the reader might consider reviewing the prior post and in particular the first few comments attached to it.)

An obvious remedy for the problem is to insert .pak into the path as it is specified in the extension.xml file. I had doubts, but I tried it anyway.
    <script name="EffectManager5E" file="../../rulesets/5E.pak/scripts/manager_effect.lua" />
As I had surmised, the attempted quick fix failed.

Detour: As damned points out in a comment attached to the prior post, the 5E manager_effect.lua file is loaded twice. A few words about when FG loads its files. The Ruleset Modification Guide’s Extensions page provides more details. Specific to the manager_effect.lua files used by my extension, as FG comes up the CoreRPG’s file is loaded first. 5E’s file is loaded “over the top” of it, replacing the Core. Then the extension’s file is loaded “over the top” of 5E’s providing the extension’s functionality for “EffectManager“. My extension.xml file loads the 5E file a second time and assigns it to “EffectManager5E”, giving the extension access to the functionality in both the 5E and the extension manager_effect.lua files. But this only works in an unzipped 5E ruleset folder.

Theory: I’m working on the premise that my methodology for reducing the amount of duplicated code is sound so my extension’s manager_effect.lua file will remain relatively untouched. But, since the code cannot force a reload of the 5E manager_effect.lua file because the path to it cannot be resolved within a .pak file, my extension must “copy” the 5E EffectManager to EffectManager5E before it is overwritten during the load process.

As I step through the code development, I will be working with the unzipped 5E folder. When finished I’ll replace the unzipped 5E folder with the 5E.pak file to ensure proper function.

The extension.xml file cannot solely be relied upon to do things in the order that’s required. Instead, most machinations will be done in lua. A “maker” script file will supervise the construction of the EffectManager and the EffectManager5E objects. When execution proceeds to the “maker” script, both the 5E and the extension manager_effect.lua files must already be loaded. To prevent possible overwrite, load the extension’s lua into a “scratch” object. Here is the new <base> of the extension.xml file:
    <includefile source="./strings/strings_5e.xml" />
    <includefile source="./ct/ct_host.xml" />

    <script name="MakeEffectManagers" file="./scripts/manager_effect_maker.lua" />
    <script name="EffectManagerExt" file="./scripts/manager_effect.lua" />
Since there are a total of three manager_effect.lua files that are loaded, two of which are of concern, I need a way of discerning which file is being referred to at run time in the code. I can achieve this by temporarily adding the following function to the extension’s manager_effect.lua file, just after onInit( ):
function tom()
  Debug.console("TWD|manager_effect.lua|tom()|status", "EXT Arrived at tom");
My code will call this function each step of the way. If an error is reported, the function doesn’t exist in that manager. But, getting the above message means the manager has the extension code.

I created a manager_effect_maker.lua file in the scripts folder, put the following code in it, saved all files and invoked FG:
function onInit()
  Debug.console("TWD|manager_effect_maker.lua|onInit()|status", "Initializing Maker");

  Debug.console("TWD|manager_effect_maker.lua|onInit()|status", "Maker calling tom()...");
When the tom function was called, an error was reported in the console, indicating EffectManager currently is not extension code.
Runtime Notice: s'TWD|manager_effect_maker.lua|onInit()|status' | s'Maker calling tom()...'
Script Error: [string "./scripts/manager_effect_maker.lua"]:11: attempt to call field 'tom' (a nil value)
That is what was expected since the last code assigned to EffectManager at that point in the load process was that of 5E. Reassigning EffectManager to EffectManager5E should preserve 5E’s manager_effect.lua. I replaced the above code with the following, saved the file and reloaded the rulesets:
  Debug.console("TWD|manager_effect_maker.lua|onInit()|status", "Making 5E manager...");
  EffectManager5E = EffectManager;
  Debug.console("TWD|manager_effect_maker.lua|onInit()|status", "Maker calling 5E tom()...");
The console informed me the 5E manager was created and when tom( ) was called from the 5E manager an error occurred because the function wasn’t found. That confirms the EffectManage5E refers to 5E code!

Now to reassign the EffectManager to the extension’s code file. Again I replaced code, all of it except the EffectManager5E assignment statement. Here are the onInit( ) function’s contents:
  EffectManager5E = EffectManager;

  Debug.console("TWD|manager_effect_maker.lua|onInit()|status", "Making Ext manager...");
  EffectManager = EffectManagerExt;
  Debug.console("TWD|manager_effect_maker.lua|onInit()|status", "Maker calling Ext tom()...");
After saving the file and reloading the ruleset, the console’s messages were:
Runtime Notice: s'TWD|manager_effect_maker.lua|onInit()|status' | s'Maker calling Ext tom()...'
Runtime Notice: s'TWD|manager_effect.lua|tom()|status' | s'EXT Arrived at tom'
Note that the call to tom( ) succeeded this time, indicating the use of the extension’s manager_effect.lua code.

One final check, “it’s the only way to be sure”. I removed all the code except the EffectManager5E assignment and the EffectManager reassignment, then added these two Debug.console( ) calls to dump each of the objects:
  Debug.console("TWD|manager_effect_maker.lua|onInit()|5E manager", EffectManager5E);
  Debug.console("TWD|manager_effect_maker.lua|onInit()|Ext manager", EffectManager);
Save and reload. Console output (abbreviated):
Runtime Notice: s'TWD|manager_effect_maker.lua|onInit()|5E manager' | { s'addEffect' = fn, s'removeEffect' = fn, s'unlock' = fn, ...
Runtime Notice: s'TWD|manager_effect_maker.lua|onInit()|Ext manager' | { s'addEffect' = fn, s'removeEffect' = fn, s'unlock' = fn, ...
I copied the console to the clipboard, opened Notepad and pasted the clipboard. Using Notepad’s search capability I looked for tom. It only appeared once in the contents, in the dump of EffectManager; a final confirmation EffectManager5E refers to the 5E lua and EffectManager refers to the extension’s lua.

Since, FG was up, I brought up the Combat Tracker, deleted any effects, brought up the Effects Manager and tried to paralyze the Orc. A slightly cryptic error message occurred:
Script Error: [string "./scripts/manager_effect.lua"]:113: attempt to index global 'EffectManager5E' (a nil value)
So, what exactly has a nil value? The EffectManager5E? To confirm I placed the Debug.console( ) command that dumps the 5E manager (see above) into the addEffect( ) function of the extension’s manager_effect.lua file. As before, save…paralyze. The console reported the EffectManager5E was okay in the onInit( ) of the "maker" scripts, but somehow nil in the EffectManager. Curious! Another oddity, the EffectManager was not nil since the addEffect( ) function ran.

Evidently the EffectManager5E is being set to nil, after "maker" creates it but before it’s utilized by the EffectManager. This predicament often occurs with global variables. So I’ll isolate it in the code, I’ll make it local to each script, and use a function that EffectManager must call to gain access to EffectManager5E. Here is manager_effect_maker.lua in its entirety:
local EffectManager5E = nil;

function onInit()
  EffectManager5E = EffectManager;
  EffectManager = EffectManagerExt;

function get5eEffectManager()
  return EffectManager5E;
As stated above the EffectManager must have access to the functionality of EffectManager5E. In fact it is needed by nearly every function the EffectManager contains. The EffectManager’s onInit( ) function will obtain the EffectManager5E object and save it locally, that way obtaining access need only be done once. Here is the onInit( ) function plus the local variable definition in the extension’s manager_effect.lua file:
local EffectManager5E = nil;

function onInit()
  OOBManager.registerOOBMsgHandler(OOB_MSGTYPE_APPLYEFF, handleApplyEffect);
  OOBManager.registerOOBMsgHandler(OOB_MSGTYPE_EXPIREEFF, handleExpireEffect);

  EffectManager5E = MakeEffectManagers.get5eEffectManager();
Save…paralyze, and it works!! At least with the unzipped 5E ruleset. To test it with the 5E.pak file I removed the unzipped 5E ruleset from the rulesets folder and renamed the 5E.pak.orig to 5E.pak (.orig? Confused? See this blog). Reload the rulesets…paralyze. Success!!

Now to play test the extension!

Until next time keep on role playing.

Submit "A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script Revisited" to Digg Submit "A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script Revisited" to Submit "A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script Revisited" to Google Submit "A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script Revisited" to Facebook Submit "A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script Revisited" to Twitter



Log in

Log in