UEVR: Plugin Development
This section covers the basics of developing plugins for UEVR. If you're looking for information on how to use UEVR, check out the Usage section.
If you are unfamiliar with C/C++, you can try the Blueprint API.
Overview
Plugins in UEVR are developed primarily in C++. However, the API header is written in C. This means it's possible to bind it to other languages, or even just write them in C.
The API header is located in include/uevr/API.h
.
The C++ API header is in include/uevr/API.hpp
.
The base C++ plugin header is in include/uevr/Plugin.hpp
.
Plugin Installation
In the frontend, click the "Open Global Dir" button. Locate the corresponding game directory, and place the DLL in the plugins
folder.
During plugin development, you many want to create a symbolic link from the plugins
dir to your project's DLL output directory. This way, you can build the plugin and have it automatically load into UEVR.
Plugin Lifecycle
Plugins are loaded and unloaded at runtime. The plugin lifecycle is as follows:
- UEVR starts its initialization process
- UEVR initially loads all plugins, and calls their
DLLMain
functions- If the plugin is a C++ plugin, the DLLMain function will call the
on_dllmain
function of the plugin
- If the plugin is a C++ plugin, the DLLMain function will call the
- UEVR begins initializing the rest of its own components
- After initialization, UEVR calls the
on_initialize
function of each plugin - During its execution loop, UEVR will call the various
on_*
functions of each plugin - The user can choose to unload all plugins, and reload them at will at runtime
The easy way
Plugin.hpp
provides a class that can be inherited from to create a plugin. This class provides a number of virtual functions that can be overridden to implement the plugin's functionality.
The way it is structured is also how you would use the API in C++ using the API.h and API.hpp headers.
Plugin.hpp
also implements a DLLMain for you, so you don't have to worry about that. All you need to worry about is overriding the virtual functions.
The project must be compiled as a DLL.
A simple example
#include <memory>
#include "uevr/Plugin.hpp"
using namespace uevr;
#define PLUGIN_LOG_ONCE(...) {\
static bool _logged_ = false; \
if (!_logged_) { \
_logged_ = true; \
API::get()->log_info(__VA_ARGS__); \
}}
class ExamplePlugin : public uevr::Plugin {
public:
ExamplePlugin() = default;
void on_dllmain() override {}
void on_initialize() override {
// Logs to the appdata UnrealVRMod log.txt file
API::get()->log_error("%s %s", "Hello", "error");
API::get()->log_warn("%s %s", "Hello", "warning");
API::get()->log_info("%s %s", "Hello", "info");
}
void on_pre_engine_tick(UEVR_UGameEngineHandle engine, float delta) override {
PLUGIN_LOG_ONCE("Pre Engine Tick: %f", delta);
}
void on_post_engine_tick(UEVR_UGameEngineHandle engine, float delta) override {
PLUGIN_LOG_ONCE("Post Engine Tick: %f", delta);
}
void on_pre_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) override {
PLUGIN_LOG_ONCE("Pre Slate Draw Window");
}
void on_post_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) override {
PLUGIN_LOG_ONCE("Post Slate Draw Window");
}
};
// Actually creates the plugin. Very important that this global is created.
// The fact that it's using std::unique_ptr is not important, as long as the constructor is called in some way.
std::unique_ptr<ExamplePlugin> g_plugin{new ExamplePlugin()};