Elixer - Cross-Mod Compatible Utility Library for Modders
remi
remedy [blu.knight] Join Date: 2003-11-18 Member: 23112Members, Super Administrators, Forum Admins, NS2 Developer, NS2 Playtester
Allow me to introduce... Elixer!
https://github.com/sclark39/NS2Elixer
This is my special utility file which I use for replacing local functions, debugging, and general niceties. It is in use already by CompMod and NS2+ and has been for about a month. It has built in functionality to be able to keep more than one version of the library in memory and switch them out on demand, so as long as you put this in a unique path in your mod, it will never conflict with other mods.
To use, throw this at the top of your client, predict, and server files:
Here is a simple example of how to use the ReplaceUpValue function
The CopyUpValues allows you to replace a function without copy pasting every local variable and function it references. Just run
There are a lot more examples of the use of this in the NS2Plus code. (You will notice the utility function is in a CHUD subfolder so as to not conflict with CompMod.)
https://github.com/Mendasp/NS2Plus
Feel free to ask in this thread about specific examples of how to hook particular methods/functions/local functions, or how to deal with mod compatibility issues. Also please post in this thread if you include the file in your mod to help sort out any potential (but unlikely) conflicts! I will post in this thread when I update it with a new version, but because of how it saves old versions in memory if you have the utility file set up correctly, it won't require any action on your part.
And again, here's the repo for the utility library itself
https://github.com/sclark39/NS2Elixer
https://github.com/sclark39/NS2Elixer
This is my special utility file which I use for replacing local functions, debugging, and general niceties. It is in use already by CompMod and NS2+ and has been for about a month. It has built in functionality to be able to keep more than one version of the library in memory and switch them out on demand, so as long as you put this in a unique path in your mod, it will never conflict with other mods.
-- Function List Elixer.UseVersion( versionNum ) EPrint( fmt, ... ) EPrintDebug( fmt, ... ) EPrintCallHook( class, name ) Class_AddMethod( className, methodName, method ) upvalues( func ) PrintUpValues( func ) GetUpValue( func, upname, options ) LocateUpValue( func, upname, options ) SetUpValues( func, source ) CopyUpValues( dst, src ) ReplaceUpValue( func, localname, newval, options ) AppendToEnum( tbl, key ) list ( ... ) set( tbl )
To use, throw this at the top of your client, predict, and server files:
Script.Load( "lua/youruniquesubdir/Elixer_Utility.lua" ) Elixer.UseVersion( 1.71 )
Here is a simple example of how to use the ReplaceUpValue function
ReplaceUpValue( GUIMinimap.Update, "UpdateStaticBlips", NewUpdateStaticBlips, { LocateRecurse = true; CopyUpValues = true; } ) ReplaceUpValue( GUIMinimap.Initialize, "kBlipInfo", kBlipInfo, { LocateRecurse = true } )
The CopyUpValues allows you to replace a function without copy pasting every local variable and function it references. Just run
PrintUpValues( GetUpValue( GUIMinimap.Update, "UpdateStaticBlips" ) )to get the list of the local variables you will need, declare them above your new function, and use the CopyUpValues option to automatically copy them over while replacing. If you want to do this while replacing class methods, you can do the same but use CopyUpValues function directly.
There are a lot more examples of the use of this in the NS2Plus code. (You will notice the utility function is in a CHUD subfolder so as to not conflict with CompMod.)
https://github.com/Mendasp/NS2Plus
Feel free to ask in this thread about specific examples of how to hook particular methods/functions/local functions, or how to deal with mod compatibility issues. Also please post in this thread if you include the file in your mod to help sort out any potential (but unlikely) conflicts! I will post in this thread when I update it with a new version, but because of how it saves old versions in memory if you have the utility file set up correctly, it won't require any action on your part.
And again, here's the repo for the utility library itself
https://github.com/sclark39/NS2Elixer
Comments
Excellent work regardless
But, most of these functions are meant to only be used on load of the mod and so they would only run once. Speed is not paramount at that point. The only one that gets run more than once really is EPrint, but that's just a Shared.Message and string.format wrapper function.
The only other overhead would be the memory cost, but that's not much of a concern for a desktop game and a bunch of lua functions shouldn't break the bank.
The file itself is only 6.5kb, so it also shouldn't impact the size of your mod much at all.
So all in all I'd rate the overhead cost as nil.
Example of usage from NS2+ to grab values needed from UpdateStaticBlips from within the call it makes to GUIItemSetColor:
A heads-up regarding this example: When you run this from your initial client class, it may fail as it cannot find the GUIMinimap class. When I converted my mod from class overriding to hooking, this had me confused for a while - I assumed it was some issue with missing Script.Load() imports I needed to define, but I could not get it to work, no matter what imports I tried.
Then, I noticed that the code actually worked fine when my mod was re-run by hotloading, ie when the map and GUI were already initialized. So the GUIMinimap class simply isn't available at the time when the mod is initialized first. The way I got this to work is hooking into an event that is triggered after all initialization is done - I use Player.OnInitialized(), and it seems to work great this way:
Note that the "if(<class name>)" check is just a safeguard, and may be redundant.
The "originalMinimapUpdate" field is defined outside of any functions, as it serves to prevent duplicate method overwriting/hooking. There may be more elegant solutions, but this seems to do the job.
The trick i use is to hook into OnThink and then wait until the needed gui class exist in the global table then i create my hook and remove the OnThink hook.
What ns2+ does is creating the gui hooks after the "LoadComplete" event has been called by the client.
Both are valid tactics to go with. In the future upcoming cdt patches might hand you even more powerfull solutions to do gui hooking
The reason why this works well is it maintains the same load order as defined by the *.entry files (because the hook callbacks are called in the order they were registered).