/Plugin

SKSE Plugin helpers

Primary LanguageC++BSD Zero Clause License0BSD

SKSE Plugin helpers

SKSE Plugin

#include <SkyrimScripting/Plugin.h>

To get started, use the Starter Kit.

logger

By default, a log file is created for your plugin, based on its plugin name.

My Games\Skyrim Special Edition\SKSE\<your plugin name>.log

To write to the log file, use:

  • logger::trace()
  • logger::debug()
  • logger::info()
  • logger::warn()
  • logger::error()
  • logger::critical()

You can pass fmt formatted strings:

logger::info(
    "{}'s form ID in hexadecimal is {:x}",
    baseObject->GetName(),
    baseObject->GetFormID()
);

Customizing Log File Path

If you want to customize the filename of the log without changing the name of your SKSE mod:

#include <SkyrimScripting/Plugin.h>

PluginLogFileName("Butts.log");

// Or customize the full system path:
PluginLogFullPath("C:\\logs\\butts.log");

// By default, we do NOT support logging to the Debug Console
// whenever a debugger is attached. To enable that feature:
PluginLogToDebugConsole;

// Or totally disable lots, entirely.
PluginLogDisable;

OnInit { ... }

ConsoleLog

Want to log to the ~ game console?

ConsoleLog("Hello, world!");

It supports fmt formatting, just like logger:

ConsoleLog(
    "{}'s form ID in hexadecimal is {:x}",
    baseObject->GetName(),
    baseObject->GetFormID()
);

Note: the console is not available for logging until OnDataLoaded or after.

SKSE Events (OnInit { ... })

Specify code that should run on certain SKSE event:

#include <SkyrimScripting/Plugin.h>

OnInit {
    logger::info("I run immediately, as soon as the plugin is loaded");
}

OnDataLoaded {
    logger::info("Data loaded! Now I have access to all of the forms in the game");
}

OnNewGame {
    logger::info("Oh, hey! A new game was created!");
}

Event Types

The following callback macros are available:

Timing SKSE Event
OnPluginLoad
OnInit
Runs immediately, as soon as the SKSE plugin is loaded. n/a
OnPluginsLoaded Runs as soon as all SKSE plugins have been loaded.
It can be preferable to run code here, as any code run in OnInit may run before other SKSE plugins have loaded.
kPostLoad
AfterPluginsLoaded Runs immediately after OnPluginsLoaded. Infrequently used, but can be useful for coordinating messaging with other plugins. kPostPostLoad
OnLoadingGame Runs before the savegame is loaded by Skyrim.
TODO: provide a way to get the size/path of the .ess save game
kPreLoadGame
OnLoadedGame Runs after the savegame has been loaded by Skyrim.
TODO: provide a way to get whether load was successful
kPostLoadGame
OnSaveGame Runs when saving a game kSaveGame
OnDeleteGame Runs when deleting a game kDeleteGame
OnNewGame Runs after a new game has been created, but before the game has loaded kNewGame
OnInputLoaded Runs after the game's input initializes, right before the Main Menu initializes kInputLoaded
OnDataLoaded
OnFormsLoaded
Runs after all of the game mods have been loaded (all Forms are loaded) kDataLoaded

Providing your own SKSEPluginLoad

If you really want to provide your own SKSEPluginLoad, just make sure to call SkyrimScripting::Plugin::Initialize().

SKSEPluginLoad(const SKSE::LoadInterface *skse) {
    SKSE::Init(skse);
    
    // Your code
    
    // Note: this is where OnInit { ... } code runs
    SkyrimScripting::Plugin::Initialize();
    
    // Your code
    
    return true;
}

This will:

  • Initialize the log
  • Run the OnInit handlers

⚠️ Note: Calling Plugin::Initialize() will register a SKSE message listener.
You will no be able to *also* SKSE::GetMessagingInterface()->RegisterListener().

Game Events (On<EventType>)

Game events are events like RE::TESActivateEvent and RE::TESHitEvent and more.

If you want to find out whenever one of these events happens in the game:

#include <SkyrimScripting/Plugin.h>

EventHandlers {

    On<RE::TESActivateEvent>([](const RE::TESActivateEvent* event) {
        auto activator = event->actionRef->GetBaseObject()->GetName();
        auto activated = event->objectActivated->GetBaseObject()->GetName();
        logger::info("{} activated {}", activator, activated);
    });

    // Basically:
    On<TheEventType>([](const TheEventType* event) {
        // Then, in here, you can do things with the event.
        // The `event` will store fields about the event.
    });

}

To find events, I recommend searching CommonLibSSE for files with TES*Event in their names.

  1. Visit this link: https://github.com/CharmedBaryon/CommonLibSSE-NG
  2. Press the T key to open the fuzzy find file search
  3. Type TESEvent
  4. Choose any of the files that show up! They're probably events that you can get via an event sink 😸

Note: not all events have defined structures. You want to find ones which have their own files defined.

That's all... for now!