enderalse/source/fs.dll/skse64/skse64/PapyrusDelayFunctors.cpp

548 lines
10 KiB
C++

#include "PapyrusDelayFunctors.h"
#include <algorithm>
#include "GameAPI.h"
#include "PapyrusVM.h"
#include "Serialization.h"
///
/// LatentSKSEDelayFunctor
///
LatentSKSEDelayFunctor::LatentSKSEDelayFunctor(UInt32 stackId) :
stackId_( stackId )
{}
LatentSKSEDelayFunctor::LatentSKSEDelayFunctor(SerializationTag) :
stackId_( 0 )
{}
bool LatentSKSEDelayFunctor::ShouldReschedule(SInt32& delayMSOut)
{
return false;
}
bool LatentSKSEDelayFunctor::ShouldResumeStack(UInt32& stackIdOut)
{
stackIdOut = stackId_;
return true;
}
bool LatentSKSEDelayFunctor::Save(SKSESerializationInterface* intfc)
{
using namespace Serialization;
WriteData(intfc, &stackId_);
//_MESSAGE("Serialized STACKID %d", stackId_);
return true;
}
bool LatentSKSEDelayFunctor::Load(SKSESerializationInterface* intfc, UInt32 version)
{
using namespace Serialization;
if (! ReadData(intfc, &stackId_))
return false;
//_MESSAGE("De-serialized STACKID %d", stackId_);
return true;
}
void* LatentSKSEDelayFunctor::operator new(std::size_t size)
{
return Heap_Allocate(size);
}
void* LatentSKSEDelayFunctor::operator new(std::size_t size, const std::nothrow_t&)
{
return Heap_Allocate(size);
}
// placement new
void* LatentSKSEDelayFunctor::operator new(std::size_t size, void* ptr)
{
return ptr;
}
void LatentSKSEDelayFunctor::operator delete(void* ptr)
{
Heap_Free(ptr);
}
void LatentSKSEDelayFunctor::operator delete(void* ptr, const std::nothrow_t&)
{
Heap_Free(ptr);
}
void LatentSKSEDelayFunctor::operator delete(void*, void*)
{
// placement delete
}
///
/// SKSEDelayFunctorQueue
///
SKSEDelayFunctorQueue::~SKSEDelayFunctorQueue()
{
ClearAndRelease();
}
void SKSEDelayFunctorQueue::Push(ISKSEDelayFunctor* func)
{// lock_
IScopedCriticalSection scopedLock( &lock_ );
data_.push_back(func);
}// ~lock_
ISKSEDelayFunctor* SKSEDelayFunctorQueue::Pop()
{
ISKSEDelayFunctor* result = NULL;
{// lock_
IScopedCriticalSection scopedLock( &lock_ );
if (! data_.empty())
{
result = data_.front();
data_.pop_front();
}
}// ~lock_
return result;
}
void SKSEDelayFunctorQueue::ClearAndRelease()
{
for (DataT::iterator it = data_.begin(); it != data_.end(); ++it)
delete *it;
data_.clear();
}
bool SKSEDelayFunctorQueue::Save(SKSESerializationInterface* intfc)
{
using namespace Serialization;
// Save data
UInt32 dataSize = data_.size();
if (! WriteData(intfc,&dataSize))
return false;
for (UInt32 i=0; i<dataSize; i++)
{
ISKSEDelayFunctor* functor = data_[i];
if (! WriteSKSEObject(intfc, functor))
return false;
}
return true;
}
bool SKSEDelayFunctorQueue::Load(SKSESerializationInterface* intfc, UInt32 loadedVersion)
{
using namespace Serialization;
UInt32 dataSize;
if (! ReadData(intfc,&dataSize))
return false;
for (UInt32 i=0; i<dataSize; i++)
{
ISKSEObject* obj = NULL;
if (! ReadSKSEObject(intfc, obj))
continue;
// Wront type? Can't happen but who knows.
ISKSEDelayFunctor* functor = dynamic_cast<ISKSEDelayFunctor*>(obj);
if (functor == NULL)
{
// Throw the loaded object away.
delete obj;
continue;
}
data_.push_back(functor);
}
return true;
}
///
/// SKSEDelayFunctorWaitList
///
SKSEDelayFunctorWaitList::SKSEDelayFunctorWaitList() :
lastTickTime_( GetPerfCounter() )
{
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
freq.QuadPart /= 1000;
msToCountMult_ = freq.QuadPart;
}
SKSEDelayFunctorWaitList::~SKSEDelayFunctorWaitList()
{
ClearAndRelease();
}
void SKSEDelayFunctorWaitList::Add(SInt32 delayMS, ISKSEDelayFunctor* func)
{// inLock_
IScopedCriticalSection scopedLock( &inLock_ );
WaitEntryT t( msToCountMult_ * delayMS, func );
inData_.push_back(t);
}// ~inLock_
void SKSEDelayFunctorWaitList::Update()
{
// Move items from thread-safe input buffer to non-thread safe main queue
{// inLock_
IScopedCriticalSection scopedLock( &inLock_ );
waitData_.insert(waitData_.end(), inData_.begin(), inData_.end());
inData_.clear();
}// ~inLock
SInt64 curTime = GetPerfCounter();
SInt64 dt = curTime - lastTickTime_;
lastTickTime_ = curTime;
if (dt <= 0)
return;
// Decrement wait times by time delta
for (WaitDataT::iterator it = waitData_.begin(); it != waitData_.end(); ++it)
if (it->first > 0)
it->first -= dt;
struct IsStillWaiting_
{
bool operator()(const WaitEntryT& e) { return e.first > 0; }
};
IsStillWaiting_ pred;
// Swap all ready entries to end of the vector#1, add them to vector#2, and truncate #1
WaitDataT::iterator r = std::partition(waitData_.begin(), waitData_.end(), pred);
for (WaitDataT::iterator it = r; it != waitData_.end(); ++it)
readyData_.push_back(it->second );
waitData_.resize(std::distance(waitData_.begin(), r));
}
ISKSEDelayFunctor* SKSEDelayFunctorWaitList::PopReady()
{
ISKSEDelayFunctor* result = NULL;
if (! readyData_.empty())
{
result = readyData_.back();
readyData_.pop_back();
}
return result;
}
void SKSEDelayFunctorWaitList::ClearAndRelease()
{
for (WaitDataT::iterator it = inData_.begin(); it != inData_.end(); ++it)
delete it->second;
inData_.clear();
for (WaitDataT::iterator it = waitData_.begin(); it != waitData_.end(); ++it)
delete it->second;
waitData_.clear();
for (ReadyDataT::iterator it = readyData_.begin(); it != readyData_.end(); ++it)
delete *it;
readyData_.clear();
// Avoid interval spanning two sessions
lastTickTime_= GetPerfCounter();
}
bool SKSEDelayFunctorWaitList::Save(SKSESerializationInterface* intfc)
{
using namespace Serialization;
// inData_
UInt32 inDataSize = inData_.size();
if (! WriteData(intfc,&inDataSize))
return false;
for (UInt32 i=0; i<inDataSize; i++)
{
SInt64 delay = inData_[i].first;
ISKSEDelayFunctor* functor = inData_[i].second;
if (! WriteSKSEObject(intfc, functor))
return false;
if (! WriteData(intfc, &delay))
return false;
}
// waitData_
UInt32 waitDataSize = waitData_.size();
if (! WriteData(intfc,&waitDataSize))
return false;
for (UInt32 i=0; i<waitDataSize; i++)
{
SInt64 delay = waitData_[i].first;
ISKSEDelayFunctor* functor = waitData_[i].second;
if (! WriteSKSEObject(intfc, functor))
return false;
if (! WriteData(intfc, &delay))
return false;
}
// readyData_
UInt32 readyDataSize = readyData_.size();
if (! WriteData(intfc,&readyDataSize))
return false;
for (UInt32 i=0; i<readyDataSize; i++)
{
ISKSEDelayFunctor* functor = readyData_[i];
if (! WriteSKSEObject(intfc, functor))
return false;
}
return true;
}
bool SKSEDelayFunctorWaitList::Load(SKSESerializationInterface* intfc, UInt32 loadedVersion)
{
using namespace Serialization;
// inData_
UInt32 inDataSize;
if (! ReadData(intfc,&inDataSize))
return false;
inData_.reserve(inDataSize);
for (UInt32 i=0; i<inDataSize; i++)
{
ISKSEObject* obj = NULL;
if (! ReadSKSEObject(intfc, obj))
continue;
// Wront type? Can't happen but who knows.
ISKSEDelayFunctor* functor = dynamic_cast<ISKSEDelayFunctor*>(obj);
if (functor == NULL)
{
// Throw the loaded object away.
delete obj;
continue;
}
SInt64 delay;
if (! ReadData(intfc, &delay))
return false;
WaitEntryT t( delay, functor );
inData_.push_back(t);
}
// waitData_
UInt32 waitDataSize;
if (! ReadData(intfc,&waitDataSize))
return false;
waitData_.reserve(waitDataSize);
for (UInt32 i=0; i<waitDataSize; i++)
{
ISKSEObject* obj = NULL;
if (! ReadSKSEObject(intfc, obj))
continue;
// Wront type? Can't happen but who knows.
ISKSEDelayFunctor* functor = dynamic_cast<ISKSEDelayFunctor*>(obj);
if (functor == NULL)
{
// Throw the loaded object away.
delete obj;
continue;
}
SInt64 delay;
if (! ReadData(intfc, &delay))
return false;
WaitEntryT t( delay, functor );
waitData_.push_back(t);
}
// readyData_
UInt32 readyDataSize;
if (! ReadData(intfc,&readyDataSize))
return false;
readyData_.reserve(readyDataSize);
for (UInt32 i=0; i<readyDataSize; i++)
{
ISKSEObject* obj = NULL;
if (! ReadSKSEObject(intfc, obj))
continue;
// Wront type? Can't happen but who knows.
ISKSEDelayFunctor* functor = dynamic_cast<ISKSEDelayFunctor*>(obj);
if (functor == NULL)
{
// Throw the loaded object away.
delete obj;
continue;
}
readyData_.push_back(functor);
}
return true;
}
///
/// SKSEDelayFunctorManager
///
SKSEDelayFunctorManager::SKSEDelayFunctorManager()
{
LARGE_INTEGER t;
QueryPerformanceFrequency(&t);
budgetFreqScale_ = double(t.QuadPart) * 0.001;
}
void SKSEDelayFunctorManager::Enqueue(ISKSEDelayFunctor* func, SInt32 delayMS)
{
if (delayMS < 0)
queue_.Push(func);
else
waitList_.Add(delayMS, func);
}
void SKSEDelayFunctorManager::OnPreTick()
{
waitList_.Update();
ISKSEDelayFunctor* func;
while ((func = waitList_.PopReady()) != NULL)
queue_.Push(func);
}
void SKSEDelayFunctorManager::OnTick(SInt64 startTime, float budget)
{
SInt64 budgetTime = budget * budgetFreqScale_;
do
{
ISKSEDelayFunctor* functor = queue_.Pop();
if (functor == NULL)
break;
VMValue result;
functor->Run(result);
SInt32 delayMS;
if (functor->ShouldReschedule(delayMS))
{
if (delayMS > 0)
waitList_.Add(delayMS, functor);
else
queue_.Push(functor);
continue;
}
UInt32 stackId;
if (functor->ShouldResumeStack(stackId))
{
(*g_skyrimVM)->GetClassRegistry()->ResumeStack(stackId, &result);
}
delete functor;
}
while (GetPerfCounter() - startTime <= budgetTime);
}
void SKSEDelayFunctorManager::OnRevert()
{
queue_.ClearAndRelease();
waitList_.ClearAndRelease();
}
bool SKSEDelayFunctorManager::Save(SKSESerializationInterface* intfc)
{
using namespace Serialization;
if (! SaveClassHelper(intfc, 'FUNQ', queue_))
return false;
if (! SaveClassHelper(intfc, 'WLST', waitList_))
return false;
intfc->OpenRecord('____', 1);
return true;
}
bool SKSEDelayFunctorManager::Load(SKSESerializationInterface* intfc, UInt32 loadedVersion)
{
using namespace Serialization;
UInt32 type, length, version;
while (intfc->GetNextRecordInfo(&type, &version, &length))
{
switch (type)
{
case 'FUNQ':
if (! queue_.Load(intfc, version))
return false;
break;
case 'WLST':
if (! waitList_.Load(intfc, version))
return false;
break;
// Done
case '____':
return true;
default:
_MESSAGE("Error loading unexpected chunk type %08X (%.4s)", type, &type);
break;
}
}
_MESSAGE("Missing record data for SKSEDelayFunctorManager");
return false;
}
///
/// Global instances
///
SKSEDelayFunctorManager& SKSEDelayFunctorManagerInstance()
{
static SKSEDelayFunctorManager instance;
return instance;
}