Plug-In Addon Structure Question

Two (or more) addons.

One with a basic functional structure.

A second that I want to sort of plug into the first one based on class/spec/whatever.

The second one I want to structure AS a plug in.

I’m trying to figure out how to get info from the 2nd addon into the first one.

My first thought is to have the first addon (the “core” addon) poll a _G[<somethingVerySpecific>] table looking for entries that correspond to different plug-in addons and then pull the data from there.

It seems like I should be able to listen for the ‘ADDON_LOADED’ event and check with each one, but that might be too (the plug-in addon may not have populated the entry into the global table yet).

Am I close on this?

in the toc file of the second addon
## RequiredDeps: YourMainAddonName

if any of the mods listed there are not installed/enabled then your mod will not load

where there are other mods you want to load before yours but dont care if they arent installed/enabled you have this

## OptionalDeps: Ace3, AceGUI-3.0-SharedMediaWidgets, LibPeriodicTable-3.1, LibSharedMedia-3.0, LibDataBroker-1.1, LibDialog-1.0

if you want plugins for your mod then its better to have a register function for them to call. that way they tell you when theres a new plugin available and you manage it all.

their plugin will use the RequiredDeps so that your mod is always loaded before theirs (so the register function is available)

your main mod will be in the global namespace, unless you really want to try and hide it by using the addon table, so all your plugins can directly access and modify it (as can anyone else if they wanted to)

ie, your plugin mod can easily call a function, or access and alter any data structure, from your main mod - and vice versa.

theres a lot of ways you can do this and it really depends on what works better for you as to how youll code it.

theres a few mods out there that already do these sorts of things (both the spec change trigger and the plugin registration) - download them and take a look at how they do those bits, then copy and alter those bits to make it do what you want.

So, I get the TOC file thing.

Core Addon: I generally have these lines at the start of every addon I make:

local coreAddonName, coreAddonCommon = ...

coreAddonCommon.data = {}
coreAddonCommon.functions = {}

local d = coreAddonCommon.data
local f = coreAddonCommon.functions

If I have a function in the core addon like this:

d.plugInAddons = {}

function RegisterPlugIn(plugInName, plugInData)
    d.plugInAddons[plugInName] = CopyTable(plugInData)
end

Does this need to be a globally defined (and carefully named) function or can I define it as f.RegisterPlugIn() and access it from the PlugIn addon?

That’s the bit I’m struggling with.

I could go with this:

function MyAddonNameRegisterPlugIn(plugInName, plugInData)
    ...
end

That would (likely) keep it from colliding with something else.

I’m not sure how much from within in the core addon is visible to the plugin addon.

It feels like I need something in global space to reach between the two.

The register thing seems cleaner than the global data thing, for sure, though.


Okay, I went with the global Registration function - but I still have a question.

There is, in the data I’m passing back (registering), an array of functions.

I’m struggling to understand which scope those functions run under.

Am I passing in pointers to them and they run in the plug-in addon’s scope or am I passing in the actual functions and they run in the core addon’s scope?

It makes a difference where I store some values that I can get once and use multiple times.

I have to check, for instance, the CD or availability of several spells multiple times in rotation logic for one character and doing that just once and storing it is more efficient than doing it every time I need it. Since this is happening fairly often, I’d prefer to use the former, but I don’t know where to store the data for use by those functions.

I could, if needed, pass a small utility table and store the values in there. Since it’d be passed with the functions themselves, it wouldn’t really matter which scope was being used, but I’d like to know before I do anything that baroque.

Thanks!

I’m slowly getting this thing together.

My problem is mostly that I know too many computer languages and I can’t sort out what is supposed to happen in all of them.

I couldn’t find a reference anywhere to how the scope of functions work in Lua when the function is passed in from another module.

Ooops. Wrong character. Sorry. It’s “Lodge” still.

By direct experimentation, I had d.TestScope set to “Core” in the core module and “PlugIn” in the plug-in module and printed d.TestScope in a test function passed in.

It printed “PlugIn” - which is the result I’d have preferred as it makes compartmentalizing the bits that need to run easier.

I just wasn’t sure and couldn’t quite see how to test it for a bit.


My next question is this:

Core - What event should I use to build the infrastructure needed to validate the registration?

PlugIn - What event should I use to trigger the registration?

If it’s the same event for both, do I have any guarantee which runs first?

if you need your code/data to be accessible to other mods then you cant hide behind the inbuilt addon table. you need to make it global.

the simplest method is to just do MyAddonName = coreAddonCommon or skip the addon table all together and just have MyAddonName = { }

anything in your addon can then be referenced by MyAddonName - just make sure you define all your functions as either function coreAddonCommon.functionname( ) or function MyAddonName.functionname( ) so they are within the addon table you go with.

it already is global, you didnt use local function RegisterPlugIn(plugInName, plugInData) so its in the global namespace as RegisterPlugIn

better option is function MyAddonName.RegisterPlugIn(plugInName, plugInData) that way everything to do with your mod is contained within the MyAddonName table, and so long as that name is unique then youll never get a clash with another mod.

anything that isnt local can be seen/altered by any other mod.

you’ll probably need both

everything runs in the same global scope. you can hide things from that by using local but thats it. if you need interaction with other mods you need to make something global.

i cant remember if lua passes the parameters as values or pointers/references.

if you are really that worried about the scope and hiding your core addon functions/data then i would suggest keeping the internal addon table for all your private code/data and also create the MyAddonName table for just the globally accessible API functions/data you need.

where you need them to cross you just have the public function call the private function.

its part of your core code - it just exists because your mod gets loaded

i use the Ace3 libraries for my mods which do it for you and i cant remember what you do for mods from scratch any more, but probably listenting for either VARIABLES_LOADED or PLAYER_ENTERING_WORLD and calling the register function there? google will probably help with this.

its not event based so you should be ok. if it was then no, you wont be able to tell which order it would trigger each plugin (at least i dont think you can)

more than likely your plugins should be telling your mod which class/spec they work for, then when your mod needs data for a specific class/spec it looks up which plugin handles that and calls the appropriate function that was registered.

it really depends on why you want to go with plugins, and not just code it all into the core mod? are you saving the users anything my making it modular when it might not need to be?

The actual plug-in structure is pretty simple.

It’s essentially just a Lua code module with the necessary logic to pick the priority action and return an event-like string that the core module will move into _G space. That is very little more than an array of functions passed back to the core module to be run in sequence with the first non-nil result stopping the run and indicating the “hit” in your rotation.

The idea is that any other addon can then make my core module a dependency and poll that _G space variable for a) existence and b) a value (best done on selected CLEU events) and flag or activate frames as necessary to indicate “do this now”.

The rotation helpers generally available in Lua all seem to work the same way - a string of icons where you pick the left-most icon, mentally translate that to what spell/ability/item it corresponds with, then find (or recall) that action’s location on your bars and activate it.

I find that whole process exhausting.

I’ve hardcoded a LOT of rotation helpers for different classes and spec in WA where what I do is either “glow” (WA’s term) the button directly with an overlay (pretty simple to set up) or replicate the hardware button layout on screen and “glow” the appropriate button there.

That works much better for me (and for a few other folks I’ve shown it to and helped them develop). What you get is not a triad of connections between icon-action-button to be deciphered, but just a visual indicator of which button to activate.

From a HUD ergonomic perspective, it’s MUCH simpler.

The problem is two-fold.

One - I don’t want to activate EVERY potential “priority” (the existing rotation helpers do that, but use position in the list to indicate which is the highest priority priority action) because I’m wanting to highlight only the highest priority action.

Two - WA has a fairly shallow limit on how deep you can stack nested logic in their “conditions” section (which is where you have to stick the “glow” action).

In order to solve those problems, I have to move the logic outside of WA and yet make it accessible.

Once I decided to do that, making it available for ANY addon to use was a small leap.

The other side of this is that there isn’t just one rotation for a given class/spec.

There are probably half a dozen viable meta or near-meta builds for each class/spec combination, especially considering racials, covenants, soulbinds, and conduits in the mix. Also, there are contextual rotational changes as well. What is best for raiding, for instance, is not always best for M+.

There isn’t any possible way for me to write a rotation sequence for every one of those combinations, particularly since I don’t play more than 2 or 3 classes myself.

Providing a relatively simple mechanism for users (including myself - if I start playing some other class/spec combos) to plug in rotational logic seems the best way to do this.

I’m going to have to reread your posts to get the full effect of them, but I think I see what you’re getting at regarding the globals and it’s why I came here. I tend to reinvent wheels that really don’t need reinventing with regards to best practices in coding.

It’s early yet and I have to get out of the house this morning for breakfast and shopping, but I’ll dig into this when I get back. It looks promising.

Thank you very much.

You don’t really need events for a plugin architecture. If you make your core a dependency of your plugins in their TOC, your core can just have a global function that you can then call in each of your plugins. For handling ingame events you can either create a frame in your core and dispatch them to registered plugins from there or you can just let each plugin create their own event frames.

You’re correct. I just needed to be able to do what the plug-in is providing on limited events (it’s a bit of processing - not huge, but more than I want to do every frame).

At first read, I didn’t understand the specifics of making a global variable that I can hook things into from different other addons (plug-ins and addons that use my data).

I think it best if I let the core process handle the events, although I could offer a way to override that (in a later release) if it becomes an issue.

I think I have it now. I just got back from coffee, breakfast, and shopping (in that order) and my blood-to-caffeine ratio is about right for critical thinking.

I’m gonna work on it a bit and see if I can make sense of it.

Thanks!