Load Priorities - noglass/FarageBot GitHub Wiki

Introduction

Each module is loaded into the engine with a priority level. The higher the level, the sooner it is to process information.

Each module processes the same bits of information linearly. This means only one module can process the same piece of information at any given time.

The default priority of a module is 327687. Internally, the priority is treated as an unsigned long long.

Internal Engine Commands and Overriding Them

The internal commands defined by the engine are considered to have a pseudo-load priority of 1. This means they will, in most cases, be the first to process command information.

Events and Hooks will always be processed before commands. No matter the load priority, if a Chat Hook from a module has returned PLUGIN_HANDLED, and that message should have triggered a Chat Command (regardless of it being an internal or module command), it will never reach that point.

Load priority will still determine the order of each modules hooks, events, commands, etc...

Overriding Internals

If you have a module that has a conflicting chat command with an internal chat command, you may give that module a load priority of 0. This will put it ahead of the internal's processing. If that command returns PLUGIN_HANDLED, then the internal command will never be executed. By returning PLUGIN_CONTINUE, both commands will execute.

Negative Priorities

Negative priorities are a special case that will allow you to do the opposite and push a module onto the end of the priority list by underflowing the unsigned integral type to its largest value.

A priority of -1 will ensure that module is the last to process information unless there are multiple modules set to -1. In that case, the operating system will determine the exact order for those modules.

A priority of -2 will put it just before any with -1.

Order of Message-type Executions

For the several types of triggers on message-triggered events, there is an order to them.

This order is not on a module to module basis, but rather on an event to event basis.

  1. Check if the channel is currently ignored.
  2. Check if the user who sent the message is currently ignored.
  3. Process all Chat Hooks.
  4. Process all onMessage* events for each module before proceeding to the next module:
    1. onMessage
    2. onMessagePrefixed
    3. onDirectMessage
    4. onDirectMessagePrefixed
    5. onChannelMessage
    6. onChannelMessagePrefixed
    7. onGroupMessage
    8. onGroupMessagePrefixed
  5. Process all commands from modules with load priority of 0.
  6. Process all internal engine commands.
  7. Process remaining module's commands.

If at any point during processing PLUGIN_HANDLED is returned, processing will stop.

This means all onMessage events for every module will be processed before any chat commands get triggered.

Changing or Setting Priorities

Internally, Farage will look for .prio files in the ./config/priority/ directory on startup.

These files should correspond directly to any .fso files located in ./modules/.

Their format is <module_basename>.prio.

These files can be avoided entirely by simplifying the process with the modules priority console command.

For help, type modules priority into your Farage console.

A module can also set its own priority on startup. This is generally useful if the module knows it will be overriding certain features.

The onModulesLoaded event is a special event that is triggered on startup after the last module has been loaded:

extern "C" int onModulesLoaded(Handle &handle, int event, void *iterator, void *position, void *foo, void *bar)
{
    std::vector<Farage::Handle*>::iterator &it = *(std::vector<Farage::Handle*>::iterator*)(iterator);
    if (handle.getLoadPriority() != 0)
    {
        handle.setLoadPriority(0,true);
        it = recallGlobal()->plugins.begin() + (*(size_t*)(position));
    }
    return PLUGIN_CONTINUE;
}

This can potentially be a dangerous event, because changing load priority can cause an iterator invalidation from within the loop. This is why void *iterator is passed to the function. No matter the type of modification you do here, you should always revalidate the iterator before returning.

This particular example will set its own load priority to 0 upon loading, unless it is already set to 0, so it will generally only do anything the very first time it is loaded at startup.