Fantasy Grounds Fridays Pre
View RSS Feed

Minty23185Fresh

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.
Code:
    <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:
Code:
  <base>
    <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" />
  </base>
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( ):
Code:
function tom()
  Debug.console("TWD|manager_effect.lua|tom()|status", "EXT Arrived at tom");
end
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:
Code:
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()...");
  EffectManager.tom();
End
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:
Code:
  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()...");
  EffectManager5E.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:
Code:
  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()...");
  EffectManager.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:
Code:
  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:
Code:
local EffectManager5E = nil;

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

function get5eEffectManager()
  return EffectManager5E;
end
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:
Code:
local EffectManager5E = nil;

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

  CombatManager.setCustomInitChange(processEffects);
 
  EffectManager5E = MakeEffectManagers.get5eEffectManager();
end
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 del.icio.us Submit "A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script Revisited" to StumbleUpon 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

Comments

FG Spreadshirt Swag

Log in

Log in