docs: add plugin setup guide

This commit is contained in:
2026-05-30 18:33:24 +12:00
parent f3db41c402
commit b61043a3fe
1776 changed files with 122386 additions and 2 deletions
@@ -0,0 +1,242 @@
#pragma once
#include "RE/B/BSSpinLock.h"
#include "RE/B/BSTArray.h"
#include "RE/B/BSTOptional.h"
#include "RE/B/BSTSingleton.h"
namespace RE
{
enum class BSEventNotifyControl : std::int32_t
{
kContinue,
kStop
};
template <class>
class BSTEventSource;
template <class Event>
class __declspec(novtable) BSTEventSink
{
public:
virtual ~BSTEventSink() = default; // 00
// add
virtual BSEventNotifyControl ProcessEvent(const Event& a_event, BSTEventSource<Event>* a_source) = 0; // 01
};
static_assert(sizeof(BSTEventSink<void*>) == 0x8);
template <class Event>
class BSTEventSource
{
public:
using event_type = Event;
void Notify(const event_type& a_event)
{
const BSAutoLock locker(_lock);
if (!_notifying && !_pendingRegisters.empty()) {
for (const auto& toAdd : _pendingRegisters) {
if (std::find(_sinks.begin(), _sinks.end(), toAdd) == _sinks.end()) {
_sinks.push_back(toAdd);
}
}
_pendingRegisters.clear();
}
++_notifying;
for (const auto& sink : _sinks) {
if (std::find(_pendingUnregisters.begin(), _pendingUnregisters.end(), sink) == _pendingUnregisters.end()) {
if (sink->ProcessEvent(a_event, this) == BSEventNotifyControl::kStop) {
break;
}
}
}
const auto uncontended = _notifying-- == 1;
if (uncontended && !_pendingUnregisters.empty()) {
for (const auto& toRemove : _pendingUnregisters) {
auto it = std::find(_sinks.begin(), _sinks.end(), toRemove);
if (it != _sinks.end()) {
_sinks.erase(it);
}
}
_pendingUnregisters.clear();
}
}
void RegisterSink(BSTEventSink<event_type>* a_sink)
{
if (!a_sink) {
return;
}
const BSAutoLock locker(_lock);
if (_notifying) {
if (std::find(_pendingRegisters.begin(), _pendingRegisters.end(), a_sink) == _pendingRegisters.end()) {
_pendingRegisters.push_back(a_sink);
}
} else {
if (std::find(_sinks.begin(), _sinks.end(), a_sink) == _sinks.end()) {
_sinks.push_back(a_sink);
}
}
const auto it = std::find(_pendingUnregisters.begin(), _pendingUnregisters.end(), a_sink);
if (it != _pendingUnregisters.end()) {
_pendingUnregisters.erase(it);
}
}
void UnregisterSink(BSTEventSink<event_type>* a_sink)
{
if (!a_sink) {
return;
}
const BSAutoLock locker(_lock);
if (_notifying) {
if (std::find(_pendingUnregisters.begin(), _pendingUnregisters.end(), a_sink) == _pendingUnregisters.end()) {
_pendingUnregisters.push_back(a_sink);
}
} else {
const auto it = std::find(_sinks.begin(), _sinks.end(), a_sink);
if (it != _sinks.end()) {
_sinks.erase(it);
}
}
const auto it = std::find(_pendingRegisters.begin(), _pendingRegisters.end(), a_sink);
if (it != _pendingRegisters.end()) {
_pendingRegisters.erase(it);
}
}
private:
// members
BSSpinLock _lock; // 00
BSTArray<BSTEventSink<event_type>*> _sinks; // 08
BSTArray<BSTEventSink<event_type>*> _pendingRegisters; // 20
BSTArray<BSTEventSink<event_type>*> _pendingUnregisters; // 38
std::int8_t _notifying{ 0 }; // 50
};
static_assert(sizeof(BSTEventSource<void*>) == 0x58);
template <class T>
class BSTValueEvent
{
public:
using value_type = T;
// members
BSTOptional<T> optionalValue; // 00
};
template <class T>
class BSTValueEventSink :
public BSTEventSink<T> // 00
{
public:
class BSTEventValueData
{
public:
// members
BSTOptional<typename T::value_type> optionalValue; // 00
bool eventReceived; // ??
};
// override (BSTEventSink<T>)
virtual BSEventNotifyControl ProcessEvent([[maybe_unused]] const T& a_event, BSTEventSource<T>*) override { return BSEventNotifyControl::kContinue; } // 01
// members
BSTEventValueData eventDataStruct; // 08
BSSpinLock dataLock; // ??
};
template <class T>
class BSTValueRequestEvent
{
public:
// members
BSTValueEventSink<T>& valueEventSink; // 00
};
template <class T>
class BSTValueEventSource :
public BSTEventSink<BSTValueRequestEvent<T>> // 00
{
public:
using event_type = BSTValueRequestEvent<T>;
// override (BSTEventSink<BSTValueRequestEvent<T>>)
BSEventNotifyControl ProcessEvent(const event_type& a_event, BSTEventSource<event_type>*) override // 01
{
BSAutoLock l{ dataLock };
T event;
event.optionalValue = optionalValue;
return a_event.valueEventSink.ProcessEvent(event, nullptr);
}
// members
BSTOptional<typename T::value_type> optionalValue; // ??
BSSpinLock dataLock; // ??
};
class BSTGlobalEvent :
public BSTSingletonSDM<BSTGlobalEvent>
{
public:
static constexpr auto RTTI{ RTTI::BSTGlobalEvent };
static constexpr auto VTABLE{ RTTI::BSTGlobalEvent };
class KillSDMEvent
{};
static_assert(sizeof(KillSDMEvent) == 0x01);
class KillSDMEventSource :
public BSTEventSource<KillSDMEvent>
{};
static_assert(sizeof(KillSDMEventSource) == 0x58);
template <class Event>
class EventSource :
public BSTEventSink<KillSDMEvent>,
public BSTSingletonSDM<EventSource<Event>>,
public BSTEventSource<Event>
{
public:
EventSource(KillSDMEventSource* a_source)
{
a_source->RegisterSink(this);
}
virtual ~EventSource() = default; // 00
// override
virtual BSEventNotifyControl ProcessEvent([[maybe_unused]] const KillSDMEvent& a_event, BSTEventSource<KillSDMEvent>* a_source) override // 01
{
a_source->UnregisterSink(this);
delete this;
return BSEventNotifyControl::kContinue;
}
};
static_assert(sizeof(EventSource<void*>) == 0x68);
virtual ~BSTGlobalEvent(); // 00
[[nodiscard]] static BSTGlobalEvent* GetSingleton()
{
static REL::Relocation<BSTGlobalEvent**> singleton{ ID::BSTEvent::BSTGlobalEvent::Singleton };
return *singleton;
}
// members
KillSDMEventSource eventSourceSDMKiller; // 10
};
static_assert(sizeof(BSTGlobalEvent) == 0x68);
}