View Full Version : Can I Really Programmatically COPY a Function from One .lua File to Another?
Minty23185Fresh
October 18th, 2017, 17:35
When one attempts to programmatically copy functions from one .lua object to another .lua object, it seems as though links or references are created back to the original code rather than duplicating the target code in the new object.
For example, when trying to copy myFunction from the global script in ManagerA to ManagerB, one might use this:
ManagerB.myCopiedFunction = ManagerA.myFunction;
However, myCopiedFunction acts like a reference back to myFunction. If myCopiedFunction happens to call other functions common to both files, the code in ManagerA is executed instead of that in ManagerB.
In a previous thread (https://www.fantasygrounds.com/forums/showthread.php?33475-Are-variables-passed-by-reference-by-value-or-it-depends) Moon Wizard helped me out with copyDeep() for use on complex variables. I have tried it and my own variant of it on functions, but without success.
Any suggestions?
Moon Wizard
October 18th, 2017, 17:43
There's no way to change that. It's a basic fundamental of variable scoping in Lua. Functions are just another kind of variable with code attached. Any references within a given scope use the variables that match within that scope.
The only way I know to override the behavior is to replace the function completely with your own code.
JPG
Bidmaron
October 18th, 2017, 18:16
If you would tell us what you are trying to do, we might be able to give you some ideas. For example, in the Table extension I am working on, I needed to be able to include some data that the built-in code didn't support, but I didn't want to wholesale replace the functionality if I could avoid it. So what I did was to override the table manager's processTableRoll to call my own code, which saved the data off to globals in my extension lua file, then I called the original processTableRoll. In order to get my data into the roll variable, I intercepted the action manager's performAction so that it took the cached global data from my lua file and placed it into the rRoll parameter table and then invoked the original performAction to do the normal roll processing. I also overrode the table manager's performRoll to process all the new data. In this case, the functionality was so different that it made no sense to dispatch back to the original routine (although I put in a hook to provide a "legacy" option that simply invokes the original code and bypasses mine entirely). After the roll completed, my performRoll takes the correct action. The onInit code of my extension to support all this is below:
function onInit()
--Debug.chat("Super Table onInit");
legacyCompleteRoll=TableManager.onTableRoll;
TableManager.onTableRoll=superOnTableRoll;
legacyPerformRoll=TableManager.performRoll;
TableManager.performRoll=superPerformRoll;
legacyPerformAction=ActionsManager.performAction;
ActionsManager.performAction=superPerformAction;
aCardinals={"zero","one","two","three","four","five","six","seven","eight","nine","ten"};
legacyProcessTableRoll=TableManager.processTableRo ll;
TableManager.processTableRoll=superProcessTableRol l;
legacyGetResults=TableManager.getResults;
TableManager.getResults=superGetResults;
Comm.registerSlashHandler("rollon", superProcessTableRoll);
end
For anyone who is curious, what I am struggling to do is restore synchronization to the rolls to eliminate the asynchronous roll problem that Myrdin discovered as he was working on the "Mother of all Treasure Tables" or some product that it appears he had to shut down because of the unreliability of the built-in tables functionality. It also adds the ability to import and export table files, hoping that we can have a forum post where we can make a repository of useful tables for sharing of non-copyrighted work. I am still working on it and testing it, though at the moment. Retaining the charm of the dice actually rolling while ensuring synchronous rolls turned out to be a lot harder of a task than I originally believed, but I think the technique I'm following seems to work fine.
lokiare
October 18th, 2017, 18:30
If you would tell us what you are trying to do, we might be able to give you some ideas. For example, in the Table extension I am working on, I needed to be able to include some data that the built-in code didn't support, but I didn't want to wholesale replace the functionality if I could avoid it. So what I did was to override the table manager's processTableRoll to call my own code, which saved the data off to globals in my extension lua file, then I called the original processTableRoll. In order to get my data into the roll variable, I intercepted the action manager's performAction so that it took the cached global data from my lua file and placed it into the rRoll parameter table and then invoked the original performAction to do the normal roll processing. I also overrode the table manager's performRoll to process all the new data. In this case, the functionality was so different that it made no sense to dispatch back to the original routine (although I put in a hook to provide a "legacy" option that simply invokes the original code and bypasses mine entirely). After the roll completed, my performRoll takes the correct action. The onInit code of my extension to support all this is below:
function onInit()
--Debug.chat("Super Table onInit");
legacyCompleteRoll=TableManager.onTableRoll;
TableManager.onTableRoll=superOnTableRoll;
legacyPerformRoll=TableManager.performRoll;
TableManager.performRoll=superPerformRoll;
legacyPerformAction=ActionsManager.performAction;
ActionsManager.performAction=superPerformAction;
aCardinals={"zero","one","two","three","four","five","six","seven","eight","nine","ten"};
legacyProcessTableRoll=TableManager.processTableRo ll;
TableManager.processTableRoll=superProcessTableRol l;
legacyGetResults=TableManager.getResults;
TableManager.getResults=superGetResults;
Comm.registerSlashHandler("rollon", superProcessTableRoll);
end
For anyone who is curious, what I am struggling to do is restore synchronization to the rolls to eliminate the asynchronous roll problem that Myrdin discovered as he was working on the "Mother of all Treasure Tables" or some product that it appears he had to shut down because of the unreliability of the built-in tables functionality. It also adds the ability to import and export table files, hoping that we can have a forum post where we can make a repository of useful tables for sharing of non-copyrighted work. I am still working on it and testing it, though at the moment. Retaining the charm of the dice actually rolling while ensuring synchronous rolls turned out to be a lot harder of a task than I originally believed, but I think the technique I'm following seems to work fine.
You could try wrapping the function:
// Inside ManagerB.lua
function myCopiedFunction(variables)
local result = ManagerA.myFunction(variables);
// Manipulate the result.
end
Bidmaron
October 18th, 2017, 18:34
That doesn't work in my case because processTableRoll builds the parameters it passes to the action manager, and there is no way to tell it to add the additional data without either wholesale replacing it or intercepting an intermediate call like I am doing with performAction, where I paste in the additional data my ultimate result handler (onTableRoll) will require. The OP already knows how to do what you are suggesting, iokaire. (from some of his earlier posts in other threads)
Minty23185Fresh
October 19th, 2017, 00:31
I am abhor copied code. It grates me, all the way to my core.
I am trying to avoid duplicating rulesets\5E\campaign\scripts\power_page.lua. There are some 25 or so functions in that file. I need to change about 3 or 4 lines in one of those functions. Because it is not global script I cannot use the typical substitute one function for another. (Moon Wizard and I discussed this here (https://www.fantasygrounds.com/forums/showthread.php?40476-Getting-a-handle-to-the-lt-script-gt-in-a-lt-windowclass-gt-(or-other-xml)).)
The script (.lua file) is called into use in ~line 242 of rulesets\5E\campaign\record_power.xml
<windowclass name="power_page">
<margins control="0,0,0,2" />
<script file="campaign/scripts/power_page.lua" />
Rather than just copying power_page.lua into my 5E Mystic Class support extension and modifying the 3 or 4 lines I have been exploring ways to preconstruct my file. For instance, have only the modified function in a file then, as the extension loads, programmatically copy the other 24 functions into it.
My windowclass would look like this:
<windowclass name="power_page" merge="join">
<script merge="delete" />
<script file="campaign/scripts/myextension_power_page.lua" />
myextension_power_page.lua might look like this:
local aGroups = {};
local aCharSlots = {};
local bCheckingUsage = false;
local bUpdatingGroups = false;
function updatePowerGroups()
-- mostly copied code
-- plus some modifications
end
Then my extension.xml would instigate a copy like this:
<base>
<script name="SrcFile" file="campaign/scripts/power_page.lua />
<script name="DesFile" file="campaign/scripts/myextension_power_page.lua />
<script name="CopyMgr" file="copier.lua />
....... plus the rest of the stuff .....
</base>
The copier.lua file would be more elegant than this, but it would essentially do this:
function onInit()
DesFile.onInit = SrcFile.onInit;
DesFile.onClose = DesFile.onClose;
DessFile.onAbilityChanged = SrcFile.onAbilityChanged;
.... plus 21 more ...
It would copy everything but function updatePowerGroups()
When the power_page xml triggered it would use myextension_power_page.lua with the modified updatePowerGroups() function plus the programmatically copied functions.
power page is used only once on the Actions tab of the character sheet so this sort of solution would not be horribly detrimental to execution speed.
But as mentioned at the beginning of the thread, the copied functions execute within power_page not in myextension_power_page. Since there is only one modified function, this might not be an issue except for the modular scope variables. the 24 functions manipulate the power_page modular, my function manipulates myextenstion_power_page modular. Causing some problems.
Bidmaron
October 19th, 2017, 01:35
I think you are screwed into wholesale copying any routine where you must do something different in a subordinate routine or where you need your own data not declared in the original. The only reason I didn’t have to do it is because I could intercept an intermediate routine and cram my custom data into the parameter table. As I understand what you are trying to do, that is not an option for you.
Nickademus
October 19th, 2017, 03:36
I recently went down this same road. I wish I had advice for you.
Minty23185Fresh
October 19th, 2017, 06:05
Thanks guys for trying to lend assistance. I appreciate the input. Sometimes somebody saying, that can't be done, is exactly what you need to quit flogging the dead unicorn. As I was getting ready to respond to your posts an idea popped into my head. I had considered it as a possibility before but never when down that goblin hole.
I am going to explore exploiting the super/self relationship.
I hope I have the "who is super" and "who is self" concept correct in my mind.
The ruleset code, power_page, is "super", I believe. I will replace the single function, updatePowerGroups(), with my extension script (using the same function name) but it executes in the extension, the "self," scope. I'll just need to ensure that I have modified all calls to functions in power_page.lua within my replacement function with "super." as in super.rebuildGroups() or super.updateHeaders(). That should drive execution back into the ruleset space. I get to have a .lua file with a single function in it, in my extension, that has hooks to all the unmodified functions in the ruleset.
My concern now are the modular scoped variables in power_page.lua:
local aGroups = {};
local aCharSlots = {};
local bCheckingUsage = false;
local bUpdatingGroups = false;
Again, many thanks. I'll let you know how it goes.
Minty23185Fresh
October 19th, 2017, 06:28
If the modular scope variables are a problem, if I recall correctly, they're only used in a hand full of functions. Duplicating 4 or 5 functions from power_page.lua into my .lua file, instead of 25, may be the best "win" I can get! :)
Bidmaron
October 19th, 2017, 12:29
It sounds like it. I don’t think the super and self references are in scope inside a lua code file only in an interface xml file. That is, only within scripts attached to controls. I could be wrong, but I cannot imagine the complexity in the code interpreter to try and derive the self-super context in a code file that could be called from an unlimited number of contexts.
I think what you are saying is the best you can hope for.
Nickademus
October 19th, 2017, 22:40
I could have sworn I used self. in one of my lua files...
Bidmaron
October 20th, 2017, 01:50
I admit I could be wrong. Maybe MW will chime in here? Or Trenloe?
Moon Wizard
October 20th, 2017, 02:02
Theoretically, the window class merge code does the same super/self variables and layering. However, the assumption right now is that scripts used in window class merges will completely replace all functions, so it hasn't been tested.
Regards,
JPG
Minty23185Fresh
October 20th, 2017, 05:10
....... the assumption right now is that scripts used in window class merges will completely replace all functions, so it hasn't been tested.
Not to be argumentative, especially with the master, but isn't that what we did in post 11 of this thread (https://www.fantasygrounds.com/forums/showthread.php?40476-Getting-a-handle-to-the-lt-script-gt-in-a-lt-windowclass-gt-(or-other-xml)/page2). In that post we replace the setCategoryHeader() function with one defined in the windowclass .xml.
I have since reworked it. Instead of replacing a function I supplement them.
Here are the first 6 lines of the power_group_header windowclass in the rulesets\5E\campaign\record_power.xml file:
<windowclass name="power_group_header">
<margins control="0,0,0,5" />
<script file="campaign/scripts/power_group.lua" />
<sheetdata>
<hsc name="group" />
<hnc name="level" />
There are seven functions in the power_group.lua file.
And then here are a few lines from my extension where I "supplement" the script:
<windowclass name="power_group_header" merge="join">
<script merge="join">
function setHeaderCategoryForPsionics(rGroup, sGroup, nLevel, sName)
name.setValue(sName);
level.setValue(nLevel);
group.setValue(sGroup);
setNode(rGroup.node);
end
</script>
</windowclass>
I realize that since I don't work for SmiteWorks that this is "anecdotal", and so it hasn't been verified.
(Please believe me, I am okay with this, it seems only prudent.)
But it does seem to be working the way I wish, as supplementing the script rather than complete replacement.
Minty23185Fresh
October 20th, 2017, 05:21
I could have sworn I used self. in one of my lua files...
I too have used self/super within the .lua file referenced in windowclasses. To this point just to list elements within super while developing code, rather than actually manipulating them, but I hope to graduate to that level of expertise. :)
Bidmaron
October 20th, 2017, 12:20
I don’t think you can use self and super in lua files not associated with controls. That is if you call a manager you create (e.g. MyManager.myRoutine) you cannot use self and super in that file. The lua has to be tied to a control, either as a script or referenced in a control. It is just like if you invoke a function in a different lua file you cannot get to locals declared in that lua file from where you invoked it. But I could be wrong....
Minty23185Fresh
October 20th, 2017, 15:23
I don’t think you can use self and super in lua files not associated with controls.....
I would completely agree. They would lack the layering context. It was easy enough to test too.
I was using some tiny "manager" scripts to try to derive a methodology for true function copying. That's what originally spawned this thread. I put some selfs and supers in the start up manager and in the managers called by managers. All selfs and supers were nil.
Not an unequivocal confirmation, but sufficient enough for me.
Bidmaron
October 20th, 2017, 17:17
Great. The world makes sense again. Good work!
Minty23185Fresh
October 20th, 2017, 21:35
... the assumption right now is that scripts used in window class merges will completely replace all functions, so it hasn't been tested.
Not to be argumentative, especially with the master, but isn't that what we did in post 11 ...
Never argue with the master! I believe I should recant. We didn't replace the function as we do in global, "manager type" scripts. We supplemented.
In both cases I mentioned above, the new script resides in the extension layer. If I were to iterate through the elements ("variables") of the extension the "replacement" function would be there and the "replaced" function would still exist in the ruleset layer. (That's what all my complaining was about in the other thread (https://www.fantasygrounds.com/forums/showthread.php?40476-Getting-a-handle-to-the-lt-script-gt-in-a-lt-windowclass-gt-(or-other-xml)).)
You can teach me new tricks, it just takes a lot time for stuff to sink in.
Powered by vBulletin® Version 4.2.1 Copyright © 2026 vBulletin Solutions, Inc. All rights reserved.