A Neophyte Tackles the FG Extension - Reducing Copied Ruleset Script
by
, June 16th, 2016 at 00:33 (5751 Views)
This time I’ll look at reducing the size of the manager_effect.lua file in my extension. Recall from last time, I copied that lua file, in its entirety, to my extension then added a single line of code to change the default behavior of an effect’s initial visibility. Having nearly 1500 lines of duplicate code troubles me.
Detour: Why is duplicating code bad practice? For one it bloats the size of your program, and for certain languages that can cause decreased execution speed. But, in my mind, even worse, it creates a maintenance nightmare. If one simply copies code, later on if new functionality is being added, one must remember where those copies are in order to change them. If one is new to the code, one doesn’t even know the copies exist. An example: say you’ve written a program that manages elevator buttons. The building has five floors and you just copy the code five times, once for each button. Now suppose your customer wants lit buttons. That’s five places in code you have to modify. Later on you get a contract to use your program in a building with three elevators servicing 350 floors. That’s 1050 copies of that code! What if, your new customer later wants flashing button lights?
In many of my prior posts I’ve typically only reported processes that produced positive results. This time I’ll include those that yield negative or failed results. I’m doing this to illustrate my tactics as I try to solve a problem.
Each time, after I perform code modifications I’ll do the following: I’ll save the edited files, reload the ruleset in FG, bring up the Combat Tracker, remove all existing effects, bring up the Effects Manager and paralyze the Orc. I’ll abbreviate this as: “save … and paralyze”.
There are nearly 40 functions in the copied manager_effect.lua file. Previously, I added one line of code to one of those functions. All the other functions in the extension are identical to their corresponding function in the 5E ruleset. Recall how an extension works, code in the extension overrides code in the ruleset. If the ruleset code is still accessible, the extension might be able to call it, thereby eliminating the need for the duplicate code.
Theory: The easiest way to eliminate duplicate code is to simply delete all functions in the extension that are identical to those in the ruleset.
Since I know how to invoke the addEffect( ) function I can use it as a test case. I’ll delete it from manager_effect.lua in my extension. If this is a workable solution the code in the 5E ruleset will execute in place of the missing extension function. I’ll place a Debug.console( ) call in the 5E’s addEffect( ) function, delete addEffect( ) from the extension‘s lua file and “save … and paralyze”. The console reports an error ‘…addEffect a nil value’. I believe this somewhat cryptic message means there is no addEffect( ) function to execute. Fail! I undo my edits and resave the file.
Theory: I’ll keep the function definitions in my extension lua file but forward execution to the ruleset. To do this I’ll replace the guts of the function with a call to the 5E function.
To force access to the 5E functions I’ll add a <script> file reference in my extension.xml file, that “creates“ a “5E Effects Manager“. Notice that the reference to the 5E manager precedes the other reference. I’ve done this because I don’t want any initialization done by my extension to be overridden or changed by the 5E manager’s initialization.
As the test case, I’ll add a line of code to the extension’s addEffect( ) function that utilizes the new “5E Effect Manager”:Code:<base> <!-- As of FG v3.2.0 the following should no longer be necessary --> <script name="EffectManager5E" file="../../rulesets/5E/scripts/manager_effect.lua" /> <script name="EffectManager" file="./scripts/manager_effect.lua" /> </base>
I saved the files, and during ruleset reload an error was thrown to the console: “..end expected..” I believe the message means that the lua interpreter recognized that my “return” statement prevents any of the rest of the code in the addEffect( ) function from executing. My quick hack failed! So I’ll comment out the rest of the function all the way down to, but not including, end. After I “save … and paralyze”, it works!Code:function addEffect(sUser, sIdentity, nodeCT, rNewEffect, bShowMsg, sEffectTargetNode) Debug.console("TWD|manager_effect.lua|addEffect()|status", "EXT Adding"); rNewEffect.nGMOnly = 1; return EffectManager5E.addEffect(sUser, sIdentity, nodeCT, rNewEffect, bShowMsg, sEffectTargetNode); -- VALIDATE if not nodeCT or not rNewEffect or not rNewEffect.sName then . . .
It appears as though I have found a solution. I have almost 40 “forwarding” functions to implement, but it will reduce the size of my lua file from ~1500 lines to ~160 and remove all duplicated code. I am going to do this a few functions at a time, just in case there are unforeseen affects.
I am going to leave the onInit( ) function alone because I don’t want the extension to initialize then call 5E to initialize over the top of it. After implementing the forwarding calls in five functions I “save … and paralyze” and it fails - the initial visibility comes up “VSBL” not “GM”! I restore the functions with undos.
Okay then, one function at a time. The first one, handleApplyEffect( ) causes a fail! An undo and a look at the function’s code… The last line is an addEffect( ) call! Here is my guess as to what is happening. The extension’s handleApplyEffect( ) is called, the execution is forwarded to the 5E ruleset function then the addEffect( ) in the ruleset is called instead of the one in my extension, the default visibility isn’t changed. I’ll verify this by putting Debug.console( ) calls in the onInit( ), handleApplyEffect( ) and addEffect( ) functions in both the extension and the ruleset, plus put the forwarding call back in.
Sure enough, that’s it. Note the initializers occur in the proper order, 5E first then the extension (and they occur as FG is loading up). When I paralyze, the extension forwards execution to 5E and then 5E calls its own addEffect( ) not the one in the extension!Runtime Notice: Reloading ruleset
Runtime Notice: s'TWD|manager_effect.lua|onInit()|status' | s'5E Initializing'
Runtime Notice: s'TWD|manager_effect.lua|onInit()|status' | s'EXT Initializing'
Runtime Notice: s'TWD|manager_effect.lua|handleApplyEffect()|statu s' | s'EXT Handling'
Runtime Notice: s'TWD|manager_effect.lua|handleApplyEffect()|statu s' | s'5E Handling'
Runtime Notice: s'TWD|manager_effect.lua|addEffect()|status' | s'5E Adding'
The handleApplyEffect( ) cannot forward execution to the ruleset. It has to keep the duplicated code. A quick search of the file for addEffect( shows that there are no more of those calls to worry about.
After implementing the forwarding calls, a few at a time, in the remaining functions, a test of the extension by paralyzing the Orc yields the desired default visibility behavior.
I have attached my manager_effect.lua (as a .txt) file for your perusal.
Some explanation about the methodology and the forwarding call statements: First: Did I accomplish what I set out to do? Most assuredly. I eliminated almost 1000 lines of duplicate code! Second: By eliminating the duplicated code using this methodology, should the 5E ruleset’s manager_effect.lua file be modified in a later version of FG it is very unlikely that I will have to “fix” my extension to be compatible with the new version. Three exceptions are: (a) if more arguments are added to any of the functions, (b) if new functions are added, or (c) if an addEffect( ) call is added anywhere in the 5E lua file. Third: Lua is very forgiving, a reduction of arguments won’t matter. Also, if one paid close attention to the code that was being replaced by the forwarding function statements, one would have noted that many of the functions did not return values to their callers. Lua’s forgiveness ignores returned values if they’re not used and returns nil if there’s nothing to return. I’ve used consistency in coding the forwarding statements for ease of maintenance.
I haven’t noticed anyone else using this methodology in other extensions, but then again I haven’t looked at every extension out there. I would really be appreciative of comments by other programmers and especially by the developers at Smiteworks, as to the general reusability of this methodology in FG extensions.
I can’t wait to play test my extension!
Until next time keep on role playing.