#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 BSTEventSource; template class __declspec(novtable) BSTEventSink { public: virtual ~BSTEventSink() = default; // 00 // add virtual BSEventNotifyControl ProcessEvent(const Event& a_event, BSTEventSource* a_source) = 0; // 01 }; static_assert(sizeof(BSTEventSink) == 0x8); template 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* 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* 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*> _sinks; // 08 BSTArray*> _pendingRegisters; // 20 BSTArray*> _pendingUnregisters; // 38 std::int8_t _notifying{ 0 }; // 50 }; static_assert(sizeof(BSTEventSource) == 0x58); template class BSTValueEvent { public: using value_type = T; // members BSTOptional optionalValue; // 00 }; template class BSTValueEventSink : public BSTEventSink // 00 { public: class BSTEventValueData { public: // members BSTOptional optionalValue; // 00 bool eventReceived; // ?? }; // override (BSTEventSink) virtual BSEventNotifyControl ProcessEvent([[maybe_unused]] const T& a_event, BSTEventSource*) override { return BSEventNotifyControl::kContinue; } // 01 // members BSTEventValueData eventDataStruct; // 08 BSSpinLock dataLock; // ?? }; template class BSTValueRequestEvent { public: // members BSTValueEventSink& valueEventSink; // 00 }; template class BSTValueEventSource : public BSTEventSink> // 00 { public: using event_type = BSTValueRequestEvent; // override (BSTEventSink>) BSEventNotifyControl ProcessEvent(const event_type& a_event, BSTEventSource*) override // 01 { BSAutoLock l{ dataLock }; T event; event.optionalValue = optionalValue; return a_event.valueEventSink.ProcessEvent(event, nullptr); } // members BSTOptional optionalValue; // ?? BSSpinLock dataLock; // ?? }; class BSTGlobalEvent : public BSTSingletonSDM { public: static constexpr auto RTTI{ RTTI::BSTGlobalEvent }; static constexpr auto VTABLE{ RTTI::BSTGlobalEvent }; class KillSDMEvent {}; static_assert(sizeof(KillSDMEvent) == 0x01); class KillSDMEventSource : public BSTEventSource {}; static_assert(sizeof(KillSDMEventSource) == 0x58); template class EventSource : public BSTEventSink, public BSTSingletonSDM>, public BSTEventSource { public: EventSource(KillSDMEventSource* a_source) { a_source->RegisterSink(this); } virtual ~EventSource() = default; // 00 // override virtual BSEventNotifyControl ProcessEvent([[maybe_unused]] const KillSDMEvent& a_event, BSTEventSource* a_source) override // 01 { a_source->UnregisterSink(this); delete this; return BSEventNotifyControl::kContinue; } }; static_assert(sizeof(EventSource) == 0x68); virtual ~BSTGlobalEvent(); // 00 [[nodiscard]] static BSTGlobalEvent* GetSingleton() { static REL::Relocation singleton{ ID::BSTEvent::BSTGlobalEvent::Singleton }; return *singleton; } // members KillSDMEventSource eventSourceSDMKiller; // 10 }; static_assert(sizeof(BSTGlobalEvent) == 0x68); }