diff options
Diffstat (limited to 'src/KingSystem')
33 files changed, 1921 insertions, 0 deletions
diff --git a/src/KingSystem/ActorSystem/actActor.h b/src/KingSystem/ActorSystem/actActor.h new file mode 100644 index 00000000..cc51e126 --- /dev/null +++ b/src/KingSystem/ActorSystem/actActor.h @@ -0,0 +1,20 @@ +#pragma once + +#include "KingSystem/ActorSystem/actBaseProc.h" + +namespace ksys { + +namespace act { + +class Actor : public BaseProc { +public: + Actor(); // FIXME + ~Actor() override; + + void emitBasicSigOn(); + void emitBasicSigOff(); +}; + +} // namespace act + +} // namespace ksys diff --git a/src/KingSystem/ActorSystem/actActorCreator.cpp b/src/KingSystem/ActorSystem/actActorCreator.cpp new file mode 100644 index 00000000..be6c43b4 --- /dev/null +++ b/src/KingSystem/ActorSystem/actActorCreator.cpp @@ -0,0 +1,7 @@ +#include "KingSystem/ActorSystem/actActorCreator.h" + +namespace ksys::act { + +SEAD_SINGLETON_DISPOSER_IMPL(ActorCreator) + +} diff --git a/src/KingSystem/ActorSystem/actActorCreator.h b/src/KingSystem/ActorSystem/actActorCreator.h new file mode 100644 index 00000000..65a3494b --- /dev/null +++ b/src/KingSystem/ActorSystem/actActorCreator.h @@ -0,0 +1,38 @@ +#pragma once + +#include <container/seadOffsetList.h> +#include <heap/seadDisposer.h> +#include <prim/seadDelegate.h> +#include <thread/seadCriticalSection.h> + +namespace ksys::act { + +class Actor; +struct ActorCreateArg; +class ActorFactory; + +class ActorCreator { + SEAD_SINGLETON_DISPOSER(ActorCreator) + ActorCreator(); + virtual ~ActorCreator(); + +public: + void setActorFactory(ActorFactory* factory) { mActorFactory = factory; } + +private: + sead::Heap* forBaseProcDualHeap; + sead::Heap* placementMgrHeap; + sead::OffsetList<Actor> mActorList; + void* _50; + bool mEnableDistanceChecks; + bool _59; + bool _5a; + void* _60; + sead::Delegate1<ActorCreator, ActorCreateArg&> mCreateActorDelegate; + // FIXME: argument type + sead::Delegate1<ActorCreator, void*> mCleanUpDelegate; + sead::CriticalSection mActorListCS; + ActorFactory* mActorFactory; +}; + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actActorFactory.cpp b/src/KingSystem/ActorSystem/actActorFactory.cpp new file mode 100644 index 00000000..6a835f3f --- /dev/null +++ b/src/KingSystem/ActorSystem/actActorFactory.cpp @@ -0,0 +1,21 @@ +#include "KingSystem/ActorSystem/actActorFactory.h" +#include "KingSystem/ActorSystem/actActorCreator.h" + +namespace ksys::act { + +SEAD_SINGLETON_DISPOSER_IMPL(ActorFactoryHolder) + +void ActorFactory::dummy() {} + +void ActorFactory::dummy2() {} + +ActorFactoryHolder::~ActorFactoryHolder() { + ActorCreator::deleteInstance(); +} + +void ActorFactoryHolder::init(sead::Heap* heap) { + ActorCreator::createInstance(heap); + ActorCreator::instance()->setActorFactory(&mFactory); +} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actActorFactory.h b/src/KingSystem/ActorSystem/actActorFactory.h new file mode 100644 index 00000000..77729030 --- /dev/null +++ b/src/KingSystem/ActorSystem/actActorFactory.h @@ -0,0 +1,28 @@ +#pragma once + +#include <heap/seadDisposer.h> + +namespace ksys::act { + +struct ActorCreateArg; +class BaseProc; + +class ActorFactory { +public: + virtual void dummy(); + virtual BaseProc* createActor(const ActorCreateArg& arg); + virtual void dummy2(); +}; + +class ActorFactoryHolder { + SEAD_SINGLETON_DISPOSER(ActorFactoryHolder) + virtual ~ActorFactoryHolder(); + +public: + void init(sead::Heap* heap); + +private: + ActorFactory mFactory; +}; + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actActorLinkConstDataAccess.cpp b/src/KingSystem/ActorSystem/actActorLinkConstDataAccess.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/KingSystem/ActorSystem/actActorLinkConstDataAccess.cpp diff --git a/src/KingSystem/ActorSystem/actActorLinkConstDataAccess.h b/src/KingSystem/ActorSystem/actActorLinkConstDataAccess.h new file mode 100644 index 00000000..628138b1 --- /dev/null +++ b/src/KingSystem/ActorSystem/actActorLinkConstDataAccess.h @@ -0,0 +1,21 @@ +#pragma once + +namespace ksys { + +namespace act { + +class BaseProc; + +/// Provides read-only access to actor data for safe, multi-threaded access. +class ActorLinkConstDataAccess { +public: + ~ActorLinkConstDataAccess(); + +private: + bool mAcquired = false; + BaseProc* mProc = nullptr; +}; + +} // namespace act + +} // namespace ksys diff --git a/src/KingSystem/ActorSystem/actAiAction.cpp b/src/KingSystem/ActorSystem/actAiAction.cpp new file mode 100644 index 00000000..ad5e4c1c --- /dev/null +++ b/src/KingSystem/ActorSystem/actAiAction.cpp @@ -0,0 +1,7 @@ +#include "KingSystem/ActorSystem/actAiAction.h" + +namespace ksys::act::ai { + +Action::Action(const ClassArg& arg) : ActionBase(arg) {} + +} // namespace ksys::act::ai diff --git a/src/KingSystem/ActorSystem/actAiAction.h b/src/KingSystem/ActorSystem/actAiAction.h new file mode 100644 index 00000000..be96de6d --- /dev/null +++ b/src/KingSystem/ActorSystem/actAiAction.h @@ -0,0 +1,17 @@ +#pragma once + +#include "KingSystem/ActorSystem/actAiClass.h" +#include "KingSystem/Utils/Types.h" + +namespace ksys::act::ai { + +class Action : public ActionBase { +public: + Action(const ClassArg& arg); + + virtual void enter() {} + virtual void loadParams() {} +}; +KSYS_CHECK_SIZE_NX150(Action, 0x20); + +} // namespace ksys::act::ai diff --git a/src/KingSystem/ActorSystem/actAiClass.cpp b/src/KingSystem/ActorSystem/actAiClass.cpp new file mode 100644 index 00000000..a75511d3 --- /dev/null +++ b/src/KingSystem/ActorSystem/actAiClass.cpp @@ -0,0 +1,8 @@ +#include "KingSystem/ActorSystem/actAiClass.h" + +namespace ksys::act::ai { + +ActionBase::ActionBase(const ClassArg& arg) + : mActor{arg.actor}, mDefinitionIdx{u16(arg.definitionIdx)}, mRootIdx{u8(arg.rootIdx)} {} + +} // namespace ksys::act::ai diff --git a/src/KingSystem/ActorSystem/actAiClass.h b/src/KingSystem/ActorSystem/actAiClass.h new file mode 100644 index 00000000..d065af14 --- /dev/null +++ b/src/KingSystem/ActorSystem/actAiClass.h @@ -0,0 +1,44 @@ +#pragma once + +#include <basis/seadTypes.h> + +#include "KingSystem/ActorSystem/actAiParam.h" +#include "KingSystem/Utils/Types.h" + +namespace ksys::act { + +class Actor; + +namespace ai { + +struct ClassArg { + Actor* actor; + u32 definitionIdx; + u32 rootIdx; +}; +KSYS_CHECK_SIZE_NX150(ClassArg, 0x10); + +class ActionBase { +public: + ActionBase(const ClassArg& arg); + virtual ~ActionBase() = default; + +protected: + void setFinished(); + + template <typename T> + void getParamStatic(ParamRef<T>* value, const sead::SafeString& key); + + Actor* mActor; + ParamPack mParams; + u16 mDefinitionIdx; + u8 mRootIdx; + u8 mStatus = 0; + u16 mClassIdx; + u16 mPrevClassIdx; +}; +KSYS_CHECK_SIZE_NX150(ActionBase, 0x20); + +} // namespace ai + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actAiParam.cpp b/src/KingSystem/ActorSystem/actAiParam.cpp new file mode 100644 index 00000000..5343ff07 --- /dev/null +++ b/src/KingSystem/ActorSystem/actAiParam.cpp @@ -0,0 +1,110 @@ +#include "KingSystem/ActorSystem/actAiParam.h" + +namespace ksys::act::ai { + +ParamPack::ParamPack() = default; + +ParamPack::~ParamPack() { + auto* param = mParams; + while (param) { + if (param->data) { + switch (Param::Type(param->type)) { + case Param::Type::String: + delete static_cast<sead::SafeString*>(param->data); + break; + case Param::Type::Int: + delete static_cast<int*>(param->data); + break; + case Param::Type::Float: + delete static_cast<float*>(param->data); + break; + case Param::Type::Vec3: + delete static_cast<sead::Vector3f*>(param->data); + break; + case Param::Type::Bool: + delete static_cast<bool*>(param->data); + break; + case Param::Type::AITreeVariablePointer: + delete static_cast<void**>(param->data); + break; + case Param::Type::UInt: + delete static_cast<u32*>(param->data); + break; + case Param::Type::MesTransceiverId: + delete static_cast<mes::TransceiverId*>(param->data); + break; + case Param::Type::BaseProcHandle: + delete static_cast<BaseProcHandle**>(param->data); + break; + case Param::Type::Rail: + delete static_cast<Rail**>(param->data); + break; + case Param::Type::BaseProcLink: + delete static_cast<BaseProcLink*>(param->data); + break; + default: + break; + } + } + param = mParams->next; + if (mParams) + delete mParams; + mParams = param; + } +} + +void InlineParamPack::copyToParamPack(ParamPack& pack) const { + const u32 n = count >= 0x1F ? 0x1F : count; + const InlineParam* param = params; + for (u32 i = 0; i < n; ++i, ++param) { + switch (param->type) { + case Param::Type::String: { + const sead::SafeString src = param->cstr; + const sead::SafeString key = param->key; + auto* dst = pack.getVariable<sead::BufferedSafeString>(key, Param::Type::String); + if (dst) + dst->copy(src); + break; + } + case Param::Type::Int: + pack.setVariable(param->key, Param::Type::Int, param->i); + break; + case Param::Type::Float: + pack.setVariable(param->key, Param::Type::Float, param->f); + break; + case Param::Type::Vec3: + if (auto* dst = pack.getVariable<sead::Vector3f>(param->key, Param::Type::Vec3)) { + dst->x = param->vec3.x; + dst->y = param->vec3.y; + dst->z = param->vec3.z; + } + break; + case Param::Type::Bool: + pack.setVariable(param->key, Param::Type::Bool, param->b); + break; + case Param::Type::UInt: + pack.setVariable(param->key, Param::Type::UInt, param->u); + break; + case Param::Type::BaseProcLink: + pack.setVariable(param->key, Param::Type::BaseProcLink, param->baseProcLink); + break; + case Param::Type::MesTransceiverId: + pack.setVariable(param->key, Param::Type::MesTransceiverId, param->mesTransceiverId); + break; + case Param::Type::BaseProcHandle: { + auto* baseProcHandle = static_cast<BaseProcHandle*>(param->ptr); + pack.setVariable(param->key, Param::Type::BaseProcHandle, baseProcHandle); + break; + } + case Param::Type::Rail: { + auto* rail = static_cast<Rail*>(param->ptr); + pack.setVariable(param->key, Param::Type::Rail, rail); + break; + } + default: + break; + } + } +} + +} // namespace ksys::act::ai diff --git a/src/KingSystem/ActorSystem/actAiParam.h b/src/KingSystem/ActorSystem/actAiParam.h new file mode 100644 index 00000000..b5df35c2 --- /dev/null +++ b/src/KingSystem/ActorSystem/actAiParam.h @@ -0,0 +1,113 @@ +#pragma once + +#include <agl/Utils/aglParameter.h> +#include <basis/seadTypes.h> +#include <math/seadVector.h> +#include <prim/seadSafeString.h> + +#include "KingSystem/ActorSystem/actBaseProc.h" +#include "KingSystem/MessageSystem/mesTransceiver.h" +#include "KingSystem/Utils/Types.h" + +namespace ksys { + +class Rail; + +namespace act::ai { + +struct Param { + enum class Type { + String = 0, + Int = 1, + Float = 2, + Vec3 = 3, + Bool = 4, + Tree = 5, + AITreeVariablePointer = 6, + UInt = 7, + BaseProcLink = 8, + MesTransceiverId = 9, + BaseProcHandle = 10, + Rail = 11, + Other = 12, + }; + + Param* next; + u32 hash; + const char* name; + void* data; + u16 type; + bool used; + u8 _23; +}; +KSYS_CHECK_SIZE_NX150(Param, 0x28); + +class ParamPack { +public: + ParamPack(); + ~ParamPack(); + + template <typename T> + T* getVariable(const sead::SafeString& key, Param::Type type, bool a4 = true) const { + const u32 hash = agl::utl::ParameterBase::calcHash(key); + auto* param = mParams; + if (!param) + return nullptr; + while (param->hash != hash || Param::Type(param->type) != type) { + param = param->next; + if (!param) + return nullptr; + } + if (a4) + param->used = true; + return static_cast<T*>(param->data); + } + + template <typename T> + void setVariable(const sead::SafeString& key, Param::Type type, const T& val) const { + T* variable = getVariable<T>(key, type, true); + if (variable) + *variable = val; + } + +private: + Param* mParams = nullptr; +}; + +struct InlineParam { + union { + bool b; + int i; + u32 u; + float f; + const char* cstr; + void* ptr; + }; + BaseProcLink baseProcLink; + sead::Vector3f vec3; + mes::TransceiverId mesTransceiverId; + Param::Type type; + const char* key; +}; +KSYS_CHECK_SIZE_NX150(InlineParam, 0x50); + +struct InlineParamPack { + void copyToParamPack(ParamPack& pack) const; + + InlineParam params[32]; + int count; +}; +KSYS_CHECK_SIZE_NX150(InlineParamPack, 0xA08); + +template <typename T> +class ParamRef { +public: + const T& value() const { return *mValue; } + void setValuePtr(const T* ptr) { mValue = ptr; } + +private: + const T* mValue = nullptr; +}; +} // namespace act::ai + +} // namespace ksys diff --git a/src/KingSystem/ActorSystem/actBaseProc.cpp b/src/KingSystem/ActorSystem/actBaseProc.cpp new file mode 100644 index 00000000..01ccc5d4 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProc.cpp @@ -0,0 +1,409 @@ +#include "KingSystem/ActorSystem/actBaseProc.h" +#include "KingSystem/ActorSystem/actBaseProcJobHandler.h" +#include "KingSystem/ActorSystem/actBaseProcLinkDataMgr.h" +#include "KingSystem/ActorSystem/actBaseProcMgr.h" +#include "KingSystem/ActorSystem/actBaseProcUnit.h" +#include "KingSystem/Terrain/teraSystem.h" + +namespace ksys::act { + +BaseProcLink::BaseProcLink() = default; + +BaseProc::BaseProc(const CreateArg& arg) + : mName(arg.actor_name), mPriority(arg.class_info->priority) { + BaseProcMgr* mgr = BaseProcMgr::instance(); + mgr->generateProcId(&mId); + mgr->registerProc(*this); + + mJobHandlers.fill(nullptr); + + BaseProcLinkDataMgr::instance()->acquireLink(this); +} + +BaseProcMapNode::~BaseProcMapNode() = default; + +BaseProc::~BaseProc() { + unlinkCalcChild_(); + unlinkCalcParent_(); + + if (mDeleteListNode.isLinked()) + BaseProcMgr::instance()->eraseFromUpdateStateList(*this); +} + +bool BaseProc::init(sead::Heap* heap, bool sleep_after_init) { + InitContext context; + context.sleep_after_init = sleep_after_init; + context.result = InitResult::Skipped; + + if (shouldInit_()) { + PrepareArg arg; + if (prepareInit_(heap, arg)) { + if (context.result != InitResult::Failed) + context.result = init_(); + } else { + context.result = InitResult::Failed; + } + } + + finalizeInit_(&context); + return context.result == InitResult::Ok && mStateFlags.isOff(StateFlags::RequestDelete); +} + +bool BaseProc::deleteLater(DeleteReason reason) { + if (isDeletedOrDeleting()) + return false; + + // Debug leftovers? + mName.cstr(); + mName.cstr(); + + BaseProcMgr* mgr = BaseProcMgr::instance(); + const bool is_high_prio = mgr->isHighPriorityThread(); + + if (!is_high_prio) { + mgr->getProcUpdateStateListCS().lock(); + if (isDeletedOrDeleting()) { + mgr->getProcUpdateStateListCS().unlock(); + return false; + } + } + + if (!BaseProcMgr::instance()->addToUpdateStateList(*this)) + onDeleteRequested_(reason); + + if (!is_high_prio) + mgr->getProcUpdateStateListCS().unlock(); + + return true; +} + +BaseProc::InitResult BaseProc::init_() { + return InitResult::Ok; +} + +bool BaseProc::shouldInit_() { + return true; +} + +void BaseProc::finalizeInit_(InitContext* context) { + if (mState == State::Delete) + return; + + if (context->result != InitResult::Ok) { + deleteLater(DeleteReason::_1); + return; + } + + if (!mProcUnit) { + if (context->sleep_after_init) + sleep(SleepWakeReason::_0); + else + wakeUp(SleepWakeReason::_0); + } else { + sleep(SleepWakeReason::_0); + if (!mProcUnit->setProc(this)) + deleteLater(DeleteReason::_2); + } +} + +BaseProc::PreDeletePrepareResult BaseProc::prepareForPreDelete_() { + return PreDeletePrepareResult::Done; +} + +// NON_MATCHING: branching +bool BaseProc::startPreparingForPreDelete_() { + if (mDeleteListNode.isLinked()) + return false; + + return !mBaseProcLinkData || mBaseProcLinkData->refCount() <= 0 || + BaseProcMgr::instance()->getUnk3() || tera::checkTeraSystemStatus(); +} + +void BaseProc::destruct_(int should_destruct) { + if (should_destruct == 1) { + BaseProcMgr::instance()->eraseFromPreDeleteList(*this); + BaseProcMgr::instance()->unregisterProc(*this); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundefined-bool-conversion" + // Yes, this check looks strange. + if (this) + delete this; +#pragma clang diagnostic pop + } +} + +void BaseProc::onEnterCalc_() {} + +void BaseProc::onDeleteRequested_(BaseProc::DeleteReason) {} + +void BaseProc::onSleepRequested_(BaseProc::SleepWakeReason) {} + +void BaseProc::onWakeUpRequested_(BaseProc::SleepWakeReason) {} + +void BaseProc::onEnterDelete_() {} + +void BaseProc::onEnterSleep_() {} + +void BaseProc::preDelete3_(bool*) {} + +bool BaseProc::prepareInit_(sead::Heap*, BaseProc::PrepareArg&) { + return true; +} + +void BaseProc::onPreDeleteStart_(PrepareArg&) {} + +void BaseProc::preDelete2_(bool*) {} + +void BaseProc::preDelete1_() {} + +// NON_MATCHING: branching +bool BaseProc::isSpecialJobType_(JobType type) { + return BaseProcMgr::instance()->isSpecialJobType(type) || isSpecialJobTypeForThisActor_(type); +} + +bool BaseProc::canWakeUp_() { + return true; +} + +void BaseProc::queueExtraJobPush_(JobType type) { + if (!isDeletedOrDeleting()) + BaseProcMgr::instance()->queueExtraJobPush(&mJobHandlers[int(type)]->getLink()); +} + +bool BaseProc::hasJobType_(JobType type) { + return mJobHandlers[int(type)] != nullptr; +} + +void BaseProc::afterUpdateState_() { + mFlags.reset(Flags::_80); + mFlags.reset(Flags::_100); +} + +bool BaseProc::shouldSkipJobPush_(JobType) { + return false; +} + +void BaseProc::onJobPush1_(JobType) {} + +void BaseProc::onJobPush2_(JobType) {} + +// NON_MATCHING: branching +bool BaseProc::processStateUpdate(u8 counter) { + const bool delete_requested = mStateFlags.isOn(StateFlags::RequestDelete); + const bool initialized = mFlags.isOn(Flags::Initialized); + + if (delete_requested) { + // Deletion cannot start until the reference count is 0. + if (initialized && mRefCount.compareExchange(0, -1)) { + if (mState != State::Delete) { + BaseProcMgr::instance()->eraseJobs(*this); + BaseProcMgr::instance()->addToPreDeleteList(*this); + startDelete_(); + } + mStateFlags.makeAllZero(); + return true; + } + // Try the deletion again later. + mStateFlags.setDirect(StateFlags::RequestDelete); + return false; + } + + sead::TypedBitFlag<StateFlags> new_flags{mStateFlags}; + + if (initialized) { + if (new_flags.isOn(StateFlags::RequestDeleteProcUnit)) + unlinkProcUnit_(); + + if (mStateFlags.isOn(StateFlags::_4000)) { + if (shouldClearStateFlag4000_()) + new_flags.makeAllZero(); + else + new_flags.setDirect(StateFlags::_4000); + } else { + new_flags.makeAllZero(); + } + + const auto check_calc_sleep = [&] { + const bool sleep_requested = mStateFlags.isOn(StateFlags::RequestSleep); + const State state = mState; + if (sleep_requested) { + handleSleepRequest_(); + if (mStateFlags.isAnyOn({StateFlags::RequestWakeUp, StateFlags::_2})) { + new_flags.setDirect(mStateFlags.getDirect() & + (u32(StateFlags::RequestWakeUp) | u32(StateFlags::_2))); + } + return true; + } + + const sead::TypedBitFlag<StateFlags> flags{mStateFlags}; + + if (state == State::Calc) { + if (flags.isOn(StateFlags::RequestChangeCalcJobPriority)) + handleJobPriorityChangeRequest_(); + return true; + } + + if (!flags.isAnyOn({StateFlags::RequestWakeUp, StateFlags::_2})) + return true; + + if (canWakeUpOrFlagsSet_()) { + handleWakeUpRequest_(); + return true; + } + + return mStateFlags.isOn(StateFlags::RequestSleep); + }; + + if (!check_calc_sleep()) { + new_flags.setDirect(mStateFlags.getDirect() & + (u32(StateFlags::RequestWakeUp) | u32(StateFlags::_2))); + } + + if (mStateFlags.isOn(StateFlags::RequestDelete)) { + new_flags.set(StateFlags::RequestDelete); + mStateFlags.setDirect(new_flags.getDirect()); + return false; + } + + if (mStateFlags.isOn(StateFlags::RequestSleep)) + handleSleepRequest_(); + + if (mStateFlags.isOn(StateFlags::RequestResetChild)) + unlinkCalcChild_(); + + if (mStateFlags.isOn(StateFlags::RequestResetParent)) + unlinkCalcParent_(); + + if (mStateFlags.isOn(StateFlags::RequestSetChild)) + loadNewCalcChild_(counter); + + if (mStateFlags.isOn(StateFlags::RequestSetParent)) + loadNewCalcParent_(counter); + } + + const bool ret = new_flags.isZero(); + mStateFlags = new_flags; + return ret; +} + +void BaseProc::processPreDelete() { + if (!mFlags.isOn(Flags::Initialized)) + return; + + if (mFlags.isOn(Flags::PreDeleteFailed)) { + // Try again. + if (BaseProcMgr::instance()->requestPreDelete(*this)) { + mFlags.set(Flags::PreDeleteStarted); + mFlags.reset(Flags::PreDeleteFailed); + } + + } else if (!mFlags.isOn(Flags::PreDeleteStarted) && startPreparingForPreDelete_()) { + mFlags.set(Flags::PreDeleting); + if (prepareForPreDelete_() == PreDeletePrepareResult::Done) { + BaseProcLinkDataMgr::instance()->releaseLink(this); + PrepareArg arg; + onPreDeleteStart_(arg); + + if (BaseProcMgr::instance()->requestPreDelete(*this)) + mFlags.set(Flags::PreDeleteStarted); + else + mFlags.set(Flags::PreDeleteFailed); + } + } +} + +void BaseProc::doPreDelete(bool* do_not_destruct_immediately) { + preDelete1_(); + preDelete2_(do_not_destruct_immediately); + preDelete3_(do_not_destruct_immediately); + + if (*do_not_destruct_immediately) + return; + + mFlags.set(Flags::Destructed); + destruct_(1); + BaseProcMgr::instance()->decrementPendingDeletions(); +} + +void BaseProc::unlinkProcUnit_() { + if (mProcUnit) { + mProcUnit->unlinkProc(this); + mProcUnit = nullptr; + } +} + +void BaseProc::unlinkCalcChild_() { + if (mConnectedCalcChild) { + mConnectedCalcChild->mConnectedCalcParent = nullptr; + mConnectedCalcChild = nullptr; + } +} + +void BaseProc::unlinkCalcParent_() { + auto parent = mConnectedCalcParent; + if (parent && parent->mConnectedCalcChild) { + parent->mConnectedCalcChild->mConnectedCalcParent = nullptr; + parent->mConnectedCalcChild = nullptr; + } +} + +void BaseProc::loadNewCalcChild_(u8 counter) { + auto child = mConnectedCalcChildNew; + if (child && !child->isDeletedOrDeleting() && !mConnectedCalcChild && + !child->mConnectedCalcParent && !isDeletedOrDeleting() && + child->mStateFlags.isOff(StateFlags::RequestDelete)) { + mConnectedCalcChild = child; + child->mConnectedCalcParent = this; + mCounter = counter; + } + mConnectedCalcChildNew = nullptr; +} + +void BaseProc::loadNewCalcParent_(u8 counter) { + auto parent = mConnectedCalcParentNew; + if (parent && !parent->isDeletedOrDeleting() && !parent->mConnectedCalcChild && + !mConnectedCalcParent && parent->mStateFlags.isOff(StateFlags::RequestDelete) && + !isDeletedOrDeleting()) { + parent->mConnectedCalcChild = this; + mConnectedCalcParent = parent; + parent->mCounter = counter; + } + mConnectedCalcParentNew = nullptr; +} + +void BaseProc::handleSleepRequest_() { + if (isDeletedOrDeleting() || mState == State::Sleep) + return; + + mState = State::Sleep; + onEnterSleep_(); + BaseProcMgr::instance()->eraseJobs(*this); +} + +void BaseProc::handleWakeUpRequest_() { + if (isDeletedOrDeleting()) + return; + + onEnterCalc_(); + + if (!mStateFlags.isOff(StateFlags::RequestDelete)) + return; + + mState = State::Calc; + BaseProcMgr::instance()->pushJobs(*this); + unlinkProcUnit_(); +} + +void BaseProc::handleJobPriorityChangeRequest_() { + for (u32 i = 0; i < BaseProcMgr::instance()->getNumJobTypes(); ++i) { + if (!mJobHandlers[i] || !mJobHandlers[i]->getLink().hasPriorityChange()) + continue; + + BaseProcMgr::instance()->eraseJob(*this, JobType(i)); + mJobHandlers[i]->getLink().loadNewPriority(); + mJobHandlers[i]->getLink().loadNewPriority2(); + BaseProcMgr::instance()->pushJob(*this, JobType(i)); + } +} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProc.h b/src/KingSystem/ActorSystem/actBaseProc.h new file mode 100644 index 00000000..12408819 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProc.h @@ -0,0 +1,286 @@ +#pragma once + +#include <basis/seadTypes.h> +#include <container/seadListImpl.h> +#include <container/seadSafeArray.h> +#include <container/seadTreeMap.h> +#include <prim/seadBitFlag.h> +#include <prim/seadRuntimeTypeInfo.h> +#include <prim/seadSafeString.h> +#include <prim/seadTypedBitFlag.h> +#include <thread/seadAtomic.h> +#include "KingSystem/ActorSystem/actBaseProcJob.h" +#include "KingSystem/ActorSystem/actBaseProcMap.h" +#include "KingSystem/Utils/StrTreeMap.h" +#include "KingSystem/Utils/Types.h" + +namespace ksys { + +namespace act { + +class ActorLinkConstDataAccess; +class BaseProc; +class BaseProcLinkData; +class BaseProcJobHandler; + +class BaseProcLink { +public: + BaseProcLink(); + ~BaseProcLink() { reset(); } + BaseProcLink& operator=(const BaseProcLink&); + bool operator==(const BaseProcLink&) const; + bool operator!=(const BaseProcLink& rhs) const { return !operator==(rhs); } + BaseProc* fromAccessorAndActor(ActorLinkConstDataAccess&, BaseProc*); + BaseProc* fromAccessor(ActorLinkConstDataAccess&); + void reset(); + +private: + BaseProcLinkData* mData = nullptr; + u32 mId = -1; + bool mAcquired = false; +}; +KSYS_CHECK_SIZE_NX150(BaseProcLink, 0x10); + +class BaseProcUnit; + +class BaseProcHandle { +public: + BaseProcHandle(); + ~BaseProcHandle(); + +private: + BaseProcUnit* mUnit; + u8 mFlag; +}; +KSYS_CHECK_SIZE_NX150(BaseProcHandle, 0x10); + +/// Actor base class that encapsulates all the low-level actor lifetime logic. +class BaseProc { +public: + enum class State : u8 { + /// The actor is constructed, but not fully initialized yet. + Init = 0, + /// The actor is active. + Calc = 1, + /// The actor is sleeping: its update jobs won't be run. + Sleep = 2, + /// The actor is scheduled for deletion. + Delete = 3, + }; + + enum class DeleteReason : u32 { + _0 = 0, + _1 = 1, + _2 = 2, + }; + + enum class SleepWakeReason : u32 { + _0 = 0, + }; + + struct ClassInfo { + sead::SafeString name; + u8 priority; + }; + KSYS_CHECK_SIZE_NX150(ClassInfo, 0x18); + + struct CreateArg { + const ClassInfo* class_info; + void* _10; + u32 _14; + u32 _18; + sead::SafeString actor_name; + }; + KSYS_CHECK_SIZE_NX150(CreateArg, 0x28); + + explicit BaseProc(const CreateArg& arg); + virtual ~BaseProc(); + + SEAD_RTTI_BASE(BaseProc) + + /// @return true iff state is Calc and the actor is not being deleted. + bool init(sead::Heap* heap, bool sleep_after_init); + /// Put this actor to sleep. The request is queued. + void sleep(SleepWakeReason reason); + /// Wake up this actor. The request is queued. + void wakeUp(SleepWakeReason reason); + /// Delete this actor. The request is queued. + bool deleteLater(DeleteReason reason); + + u8 getPriority() const { return mPriority; } + void setPriority(u8 priority) { mPriority = priority; } + + bool isDeletedOrDeleting() const { + return mState == State::Delete || mStateFlags.isOn(StateFlags::RequestDelete); + } + +protected: + friend class BaseProcLinkDataMgr; + friend class BaseProcMgr; + + enum class Flags : u32 { + Initialized = 1, + PreDeleteStarted = 2, + PreDeleteFailed = 4, + Destructed = 8, + DoNotDelete = 0x10, + DeleteChildOnDelete = 0x20, + DeleteParentOnDelete = 0x40, + _80 = 0x80, + _100 = 0x100, + PreDeleting = 0x200, + SleepWakeReason0 = 0x400, + }; + + enum class StateFlags : u32 { + RequestDelete = 1, + _2 = 2, + RequestSleep = 4, + RequestWakeUp = 8, + RequestChangeCalcJobPriority = 0x10, + _20 = 0x20, + _40 = 0x40, + _80 = 0x80, + RequestSetParent = 0x100, + RequestSetChild = 0x200, + RequestResetParent = 0x400, + RequestResetChild = 0x800, + _1000 = 0x1000, + _2000 = 0x2000, + _4000 = 0x4000, + RequestDeleteProcUnit = 0x8000, + }; + + enum class InitResult : u32 { + Ok = 1, + Failed = 2, + Skipped = 3, + }; + + enum class PreDeletePrepareResult : u32 { + NotDone = 0, + Done = 1, + }; + + struct InitContext { + InitResult result; + bool sleep_after_init; + }; + + struct PrepareArg { + bool _0 = false; + int _4; + }; + + /// Initialize the actor. + /// @return Ok to keep the actor alive, anything else to kill it? + virtual InitResult init_(); + /// @return whether prepareInit_ and init_ should be called. + virtual bool shouldInit_(); + /// Finalize the initialization; the actor is deleted if the result is not Ok. + virtual void finalizeInit_(InitContext* context); + + /// Called every tick to prepare for pre-delete (after startPreparingForPreDelete_). + virtual PreDeletePrepareResult prepareForPreDelete_(); + /// Called to start preparing for pre-delete. Return true to allow pre-delete to go ahead. + virtual bool startPreparingForPreDelete_(); + + /// Destructs this actor if should_destruct is 1. + /// @warning The actor must NOT be used after calling this function. + virtual void destruct_(int should_destruct); + + /// Called when entering the Calc state. + virtual void onEnterCalc_(); + + /// Called when a new delete operation is queued. + virtual void onDeleteRequested_(DeleteReason reason); + /// Called when a new sleep operation is queued. + virtual void onSleepRequested_(SleepWakeReason reason); + /// Called when a new wakeup operation is queued. + virtual void onWakeUpRequested_(SleepWakeReason reason); + + virtual bool shouldClearStateFlag4000_() { return true; } + + /// Called when entering the Delete state. + virtual void onEnterDelete_(); + /// Called when entering the Sleep state. + virtual void onEnterSleep_(); + + /// Called to actually pre-delete (third and final callback). + virtual void preDelete3_(bool* do_not_destruct_immediately); + + virtual bool prepareInit_(sead::Heap* heap, PrepareArg& arg); + + /// Called when pre-delete actually starts (after preparation, before requesting it). + virtual void onPreDeleteStart_(PrepareArg&); + /// Called to actually pre-delete (second callback). + virtual void preDelete2_(bool* do_not_destruct_immediately); + /// Called to actually pre-delete (first callback). + virtual void preDelete1_(); + + virtual bool isSpecialJobType_(JobType type); + virtual bool canWakeUp_(); + virtual void queueExtraJobPush_(JobType type); + virtual bool hasJobType_(JobType type); + /// Called after processStateUpdate() is called for all actors in the update state list. + virtual void afterUpdateState_(); + + virtual bool shouldSkipJobPush_(JobType type); + /// Called before pushing a job with the specified job type (first callback). + virtual void onJobPush1_(JobType type); + /// Called before pushing a job with the specified job type (second and final callback). + virtual void onJobPush2_(JobType type); + + bool processStateUpdate(u8 counter); + void processPreDelete(); + /// Actually pre-delete the actor. Called from BaseProcDeleter. + void doPreDelete(bool* do_not_destruct_immediately); + void startDelete_(); + + bool isSpecialJobTypeForThisActor_(JobType type) const { + return mSpecialJobTypesMask.isOnBit(int(type)); + } + + sead::FixedSafeString<64> mName; + u32 mId = -1; + State mState = State::Init; + u8 mPriority = 0; + u8 mCreatePriorityState = 0; + u8 mCounter = 0; + BaseProcLinkData* mBaseProcLinkData = nullptr; + sead::BitFlag16 mSkippedJobTypesMask; + sead::BitFlag16 mSpecialJobTypesMask; + sead::TypedBitFlag<Flags, sead::Atomic<u32>> mFlags; + sead::TypedBitFlag<StateFlags, sead::Atomic<u32>> mStateFlags; + BaseProc* mConnectedCalcParent = nullptr; + BaseProc* mConnectedCalcChild = nullptr; + BaseProc* mConnectedCalcParentNew = nullptr; + BaseProc* mConnectedCalcChildNew = nullptr; + sead::SafeArray<BaseProcJobHandler*, 7> mJobHandlers{}; + sead::Delegate<BaseProc> mInvoker; // TODO: is this correct? + sead::ListNode mPostDeleteListNode; + sead::ListNode mDeleteListNode; + BaseProcMapNode mMapNode{this}; + BaseProcUnit* mProcUnit = nullptr; + sead::Atomic<u32> mRefCount = 0; + +private: + void unlinkProcUnit_(); + void unlinkCalcChild_(); + void unlinkCalcParent_(); + void loadNewCalcChild_(u8 counter); + void loadNewCalcParent_(u8 counter); + + void handleSleepRequest_(); + void handleWakeUpRequest_(); + void handleJobPriorityChangeRequest_(); + + bool canWakeUpOrFlagsSet_() { + return mFlags.isOn(Flags::_80) ? mFlags.isOn(Flags::_100) : canWakeUp_(); + } +}; +KSYS_CHECK_SIZE_NX150(BaseProc, 0x180); + +} // namespace act + +} // namespace ksys diff --git a/src/KingSystem/ActorSystem/actBaseProcJob.cpp b/src/KingSystem/ActorSystem/actBaseProcJob.cpp new file mode 100644 index 00000000..2b3801fa --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcJob.cpp @@ -0,0 +1,9 @@ +#include "KingSystem/ActorSystem/actBaseProcJob.h" + +namespace ksys::act { + +BaseProcJobLink::BaseProcJobLink(BaseProc* proc, u8 priority) + : TListNode(proc), mPriority(priority), mNewPriority(priority), mPriority2(3), + mNewPriority2(3) {} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcJob.h b/src/KingSystem/ActorSystem/actBaseProcJob.h new file mode 100644 index 00000000..bd30b23e --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcJob.h @@ -0,0 +1,53 @@ +#pragma once + +#include <container/seadTList.h> +#include "KingSystem/Utils/Types.h" + +namespace ksys::act { + +class BaseProc; + +enum class Priority : u8 { + PlayerBefore = 0, + Player = 2, + PlayerAfter = 2, + AllAfter = 3, +}; + +enum class JobType { + PreCalc = 0, + Calc1 = 1, + Calc2 = 2, + Calc3 = 3, + Calc4 = 4, + AfterMessageDispatch = 5, + Calc3Alt = 6, + Invalid = 7, +}; + +class BaseProcJobLink : public sead::TListNode<BaseProc*> { +public: + BaseProcJobLink(BaseProc* proc, u8 priority); + + u8 getPriority() const { return mPriority; } + u8 getPriority2() const { return mPriority2; } + + void setNewPriority(u8 priority) { mNewPriority = priority; } + void setNewPriority2(u8 priority) { mNewPriority2 = priority; } + + void loadNewPriority() { mPriority = mNewPriority; } + void loadNewPriority2() { mPriority2 = mNewPriority2; } + + bool hasPriorityChange() const { + return mPriority != mNewPriority || mPriority2 != mNewPriority2; + } + +private: + u8 mPriority; + u8 mNewPriority; + u8 mPriority2; + u8 mNewPriority2; +}; +KSYS_CHECK_SIZE_NX150(BaseProcJobLink, 0x28); + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcJobHandler.cpp b/src/KingSystem/ActorSystem/actBaseProcJobHandler.cpp new file mode 100644 index 00000000..b74fcb1f --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcJobHandler.cpp @@ -0,0 +1,8 @@ +#include "KingSystem/ActorSystem/actBaseProcJobHandler.h" +#include "KingSystem/ActorSystem/actBaseProc.h" + +namespace ksys::act { + +BaseProcJobHandler::BaseProcJobHandler(BaseProc* proc) : mLink(proc, proc->getPriority()) {} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcJobHandler.h b/src/KingSystem/ActorSystem/actBaseProcJobHandler.h new file mode 100644 index 00000000..1df63276 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcJobHandler.h @@ -0,0 +1,23 @@ +#pragma once + +#include "KingSystem/ActorSystem/actBaseProcJob.h" +#include "KingSystem/Utils/Types.h" + +namespace ksys::act { + +class BaseProcJobHandler { +public: + BaseProcJobHandler(BaseProc* proc); + virtual ~BaseProcJobHandler() = default; + virtual void invoke() = 0; + virtual void invokeSpecial() {} + + BaseProcJobLink& getLink() { return mLink; } + const BaseProcJobLink& getLink() const { return mLink; } + +protected: + BaseProcJobLink mLink; +}; +KSYS_CHECK_SIZE_NX150(BaseProcJobHandler, 0x30); + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.cpp b/src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.cpp new file mode 100644 index 00000000..cc986835 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.cpp @@ -0,0 +1,52 @@ +#include "KingSystem/ActorSystem/actBaseProcLinkDataMgr.h" +#include <prim/seadScopedLock.h> +#include <thread/seadThread.h> +#include "KingSystem/ActorSystem/actBaseProc.h" + +namespace ksys::act { + +SEAD_SINGLETON_DISPOSER_IMPL(BaseProcLinkDataMgr) + +bool BaseProcLinkDataMgr::acquireLink(BaseProc* proc) { + auto lock = sead::makeScopedLock(mCS); + s32 index = mIndex; + for (s32 i = 0; i < mData.size(); ++i) { + const s32 j = index == 0x800 ? 0 : index; + auto& data = mData[j]; + if (data.mId == u32(-1)) { + { + auto data_lock = sead::makeScopedLock(data.mCS); + data.mProc = proc; + data.mId = proc->mId; + proc->mBaseProcLinkData = &data; + } + mIndex = j + 1; + return true; + } + index = j + 1; + } + + for (s32 i = 0; i < mData.size(); ++i) { + auto data_lock = sead::makeScopedLock(mData[i].mCS); + // Debug code was probably here? + } + + return false; +} + +bool BaseProcLinkDataMgr::releaseLink(BaseProc* proc) { + auto* data = proc->mBaseProcLinkData; + if (!data) + return false; + + const auto thread = sead::ThreadMgr::instance()->getCurrentThread(); + thread->getPriority(); + + auto lock = sead::makeScopedLock(data->mCS); + data->mProc = nullptr; + data->mId = -1; + proc->mBaseProcLinkData = nullptr; + return true; +} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.h b/src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.h new file mode 100644 index 00000000..b7f19f81 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.h @@ -0,0 +1,43 @@ +#pragma once + +#include <container/seadSafeArray.h> +#include <heap/seadDisposer.h> +#include <prim/seadScopedLock.h> +#include <thread/seadCriticalSection.h> +#include "KingSystem/Utils/Types.h" + +namespace ksys::act { + +class BaseProc; + +class BaseProcLinkData { +public: + u32 id() const { return mId; } + BaseProc* proc() const { return mProc; } + s32 refCount() const { return mRefCount; } + +private: + friend class BaseProcLinkDataMgr; + + sead::CriticalSection mCS; + u32 mId = -1; + BaseProc* mProc = nullptr; + s32 mRefCount = 0; +}; +KSYS_CHECK_SIZE_NX150(BaseProcLinkData, 0x58); + +class BaseProcLinkDataMgr { + SEAD_SINGLETON_DISPOSER(BaseProcLinkDataMgr) + +public: + bool acquireLink(BaseProc* proc); + bool releaseLink(BaseProc* proc); + +private: + sead::CriticalSection mCS; + s32 mIndex = 0; + sead::SafeArray<BaseProcLinkData, 2048> mData{}; +}; +KSYS_CHECK_SIZE_NX150(BaseProcLinkDataMgr, 0x2C068); + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcMap.cpp b/src/KingSystem/ActorSystem/actBaseProcMap.cpp new file mode 100644 index 00000000..c38072e9 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcMap.cpp @@ -0,0 +1,22 @@ +#include "KingSystem/ActorSystem/actBaseProcMap.h" + +namespace ksys::act { + +void BaseProcMapNode::add(BaseProcMapNode* node) { + auto next = mNext; + node->mPrev = this; + node->mNext = next; + mNext = node; + if (next) + next->mPrev = node; +} + +void BaseProcMap::insert(BaseProcMapNode* node) { + if (auto existing_node = find(node->key())) + existing_node->add(node); + else + StrTreeMap::insert(node); + node->setInserted(); +} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcMap.h b/src/KingSystem/ActorSystem/actBaseProcMap.h new file mode 100644 index 00000000..9cbe42c9 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcMap.h @@ -0,0 +1,61 @@ +#pragma once + +#include "KingSystem/Utils/StrTreeMap.h" + +namespace ksys::act { + +class BaseProc; + +class BaseProcMapNode final : public util::StrTreeMapNode { +public: + explicit BaseProcMapNode(BaseProc* proc) : mProc(proc) {} + ~BaseProcMapNode() override; + + // NON_MATCHING: addressing mode for mPrev + void erase_() override { + if (mPrev) + mPrev->mNext = mNext; + + if (mNext) + mNext->mPrev = mPrev; + + StrTreeMapNode::erase_(); + mInserted = false; + mPrev = mNext = nullptr; + } + + util::StrTreeMapKey& key() { return mKey; } + const util::StrTreeMapKey& key() const { return mKey; } + BaseProc* proc() const { return mProc; } + BaseProcMapNode* prev() const { return mPrev; } + BaseProcMapNode* next() const { return mNext; } + + void add(BaseProcMapNode* node); + + bool isInserted() const { return mInserted; } + void setInserted() { mInserted = true; } + +private: + BaseProc* mProc = nullptr; + BaseProcMapNode* mPrev = nullptr; + BaseProcMapNode* mNext = nullptr; + bool mInserted = false; +}; + +class BaseProcMap : public util::StrTreeMap<BaseProcMapNode> { +public: + void insert(BaseProcMapNode* node); + + void erase(BaseProcMapNode* node) { + auto prev = node->prev(); + auto next = node->next(); + if (prev) + node->erase_(); + else if (next) + StrTreeMap::insert(next); + else + StrTreeMap::erase(node->key()); + } +}; + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcMgr.cpp b/src/KingSystem/ActorSystem/actBaseProcMgr.cpp new file mode 100644 index 00000000..81ebe6a2 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcMgr.cpp @@ -0,0 +1,133 @@ +#include "KingSystem/ActorSystem/actBaseProcMgr.h" +#include <prim/seadScopedLock.h> +#include <thread/seadThread.h> + +namespace ksys::act { + +SEAD_SINGLETON_DISPOSER_IMPL(BaseProcMgr) + +void BaseProcMgr::generateProcId(u32* id) { + *id = mCreatedProcCounter.increment(); +} + +void BaseProcMgr::registerProc(BaseProc& proc) { + auto lock = sead::makeScopedLock(mProcMapCS); + proc.mMapNode.key().setKey(proc.mName); + mProcMap.insert(&proc.mMapNode); +} + +void BaseProcMgr::unregisterProc(BaseProc& proc) { + if (!proc.mMapNode.isInserted()) + return; + + auto lock = sead::makeScopedLock(mProcMapCS); + mProcMap.erase(&proc.mMapNode); +} + +void BaseProcMgr::addToPreDeleteList(BaseProc& proc) { + auto lock = sead::makeScopedLock(mProcPreDeleteListCS); + mProcPreDeleteList.pushBack(&proc); +} + +void BaseProcMgr::doAddToUpdateStateList_(BaseProc& proc) { + if (mProcUpdateStateList.isNodeLinked(&proc)) + return; + + if (proc.mCreatePriorityState == 1) { + proc.mCreatePriorityState = 0; + mProcUpdateStateList.pushFront(&proc); + } else { + mProcUpdateStateList.pushBack(&proc); + } +} + +void BaseProcMgr::eraseFromPreDeleteList(BaseProc& proc) { + auto lock = sead::makeScopedLock(mProcPreDeleteListCS); + if (mProcPreDeleteList.isNodeLinked(&proc)) + mProcPreDeleteList.erase(&proc); +} + +void BaseProcMgr::eraseFromUpdateStateList(BaseProc& proc) { + auto lock = sead::makeScopedLock(mProcUpdateStateListCS); + if (mProcUpdateStateList.isNodeLinked(&proc)) + mProcUpdateStateList.erase(&proc); +} + +void BaseProcMgr::processPreDeleteList() { + mStatus = Status::ProcessingPreDeleteList; + + auto lock = sead::makeScopedLock(mProcPreDeleteListCS); + for (auto& proc : mProcPreDeleteList.robustRange()) + proc.processPreDelete(); + + mStatus = Status::Idle; +} + +bool BaseProcMgr::hasExtraJobLink(BaseProcJobLink* job_link, s32 idx) { + for (auto& ptr : mExtraJobLinkArrays[idx]) { + if (&ptr == job_link) + return true; + } + return false; +} + +void BaseProcMgr::clearExtraJobArrays() { + mExtraJobLinkArrays[0].clear(); + mExtraJobLinkArrays[1].clear(); +} + +void BaseProcMgr::setActorJobTypeAndPrio(JobType type, s32 prio, bool x) { + mStatus = Status::ProcessingActorJobs; + mJobType = type; + mCurrentlyProcessingPrio = prio; + mUnk2 = x; +} + +void BaseProcMgr::goIdle() { + mStatus = Status::Idle; + mJobType = JobType::Invalid; + mIsPushingJobs = false; + mUnk2 = false; + mEnableExtraJobPush = false; + mCurrentlyProcessingPrio = 8; +} + +void BaseProcMgr::clearMode() { + mMode = Mode::_0; +} + +bool BaseProcMgr::isHighPriorityThread() const { + const auto current_thread = sead::ThreadMgr::instance()->getCurrentThread(); + + if (!current_thread) + return false; + + const auto id = current_thread->getId(); + + if (!current_thread->isDefaultPriority()) + return false; + + return id == mMainThreadId || id == mHavokThreadId1 || id == mHavokThreadId2; +} + +void BaseProcMgr::incrementUnk3() { + if (mUnk3 != 0xFF) + ++mUnk3; +} + +void BaseProcMgr::decrementUnk3() { + if (mUnk3 != 0) + --mUnk3; +} + +sead::CriticalSection* BaseProcMgr::lockProcMap() { + mProcMapCS.lock(); + return &mProcMapCS; +} + +void BaseProcMgr::unlockProcMap() { + mLastProcMapNode = nullptr; + mProcMapCS.unlock(); +} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcMgr.h b/src/KingSystem/ActorSystem/actBaseProcMgr.h new file mode 100644 index 00000000..95d79f9b --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcMgr.h @@ -0,0 +1,219 @@ +#pragma once + +#include <agl/Utils/aglAtomicPtrArray.h> +#include <container/seadOffsetList.h> +#include <container/seadPtrArray.h> +#include <container/seadSafeArray.h> +#include <heap/seadDisposer.h> +#include <prim/seadBitFlag.h> +#include <prim/seadScopedLock.h> +#include <thread/seadAtomic.h> +#include <thread/seadCriticalSection.h> +#include "KingSystem/ActorSystem/actBaseProc.h" +#include "KingSystem/ActorSystem/actBaseProcJob.h" +#include "KingSystem/ActorSystem/actBaseProcMap.h" +#include "KingSystem/Utils/StrTreeMap.h" +#include "KingSystem/Utils/Types.h" + +namespace sead { +class FixedSizeJQ; +template <typename F> +class IFunction; +class WorkerMgr; +} // namespace sead + +namespace ksys::act { + +class BaseProcDeleter; +class BaseProcInitializer; +class BaseProcInitializerArgs; +class BaseProcJobLists; +class BaseProcJobQue; + +class BaseProcMgr { + SEAD_SINGLETON_DISPOSER(BaseProcMgr) + BaseProcMgr(); + +public: + class ProcCreateRequest; + + enum class Status : u8 { + Idle = 0, + _1 = 1, + ProcessingActorJobs = 2, + ProcessingPreDeleteList = 3, + ProcessingUpdateStateList = 4, + }; + + enum class Mode : u8 { + _0 = 0, + }; + + using ExtraJobLinkArray = agl::utl::FixedPtrArray<BaseProcJobLink, 512>; + + static void createInstanceAndInit(sead::Heap* heap); + static u32 getConstant0() { return sConstant0; } + static u32 getConstant1() { return sConstant1; } + static u32 getConstant2() { return sConstant2; } + static u32 getConstant4() { return sConstant4; } + + virtual ~BaseProcMgr(); + + void init(sead::Heap* heap, s32 num_job_types, u32 main_thread_id, u32 havok_thread_id1, + u32 havok_thread_id2, BaseProcInitializerArgs* initializer_args); + + // region BaseProc management + + void generateProcId(u32* id); + void registerProc(BaseProc& proc); + void unregisterProc(BaseProc& proc); + + void addToPreDeleteList(BaseProc& proc); + bool addToUpdateStateList(BaseProc& proc); + void eraseFromPreDeleteList(BaseProc& proc); + void eraseFromUpdateStateList(BaseProc& proc); + void processPreDeleteList(); + + void forEachProc(const sead::IDelegate1<BaseProc*>& callback, u32 flags); + void deleteAllProcs(); + bool hasFinishedDeletingAllProcs(); + + // endregion + + bool requestPreDelete(BaseProc& proc); + void requestUnloadActorParam(void*); + + // region Job processing + + void pushJob(BaseProc& proc, JobType type); + void pushJobs(BaseProc& proc); + void eraseJob(BaseProc& proc, JobType type); + void eraseJobs(BaseProc& proc); + + void processExtraJobsDirectly(JobType type, s32 prio, bool); + ExtraJobLinkArray& getExtraJobs(); + void swapExtraJobArray(); + + void queueExtraJobPush(BaseProcJobLink* job_link); + void moveExtraJobsToOtherBuffer(); + bool hasExtraJobLink(BaseProcJobLink* job_link, s32 idx); + void clearExtraJobArrays(); + + void pushJobQueues(sead::WorkerMgr* mgr, JobType type, bool); + bool pushExtraJobsEx(sead::FixedSizeJQ* jq, JobType type, bool, bool); + bool pushExtraJobsForCurrentTypeAndPrio(sead::FixedSizeJQ* jq, ExtraJobLinkArray& array); + bool pushPreCalcJobs(sead::FixedSizeJQ* jq, JobType type, s32 prio, bool, bool); + + void setActorJobType(JobType type); + void setActorJobTypeAndPrio(JobType type, s32 prio, bool); + void goIdle(); + void clearMode(); + void calc(); + void jobInvoked(BaseProcJobLink* link, s32 required_calc_rounds); + + // endregion + + // region Special job types + + bool isSpecialJobType(JobType type) const; + void addSpecialJobTypes(u16 mask); + void removeSpecialJobTypes(u16 mask); + + // endregion + + /// Returns true if and only if the calling thread is the game thread or a Havok thread. + bool isHighPriorityThread() const; + /// Returns true if and only if it is safe to access the specified BaseProc. + bool isAccessingProcSafe(BaseProc* proc, BaseProc* other); + + // region BaseProc creation + + bool requestCreateProc(ProcCreateRequest& req); + BaseProc* createProc(ProcCreateRequest& req); + + // endregion + + // region Actor initializer control + + void resumeThreadMaybe(); + void stopThreads(); + void clearMessageQueueMaybe(); + void startActorCreateThread(); + + void clearInitializerMessageQueuesMaybe(); + s32 getActorCreateInitializerQueueSize(); + s32 getActorCreateInitializerQueueSizeEx(s32 x); + void invokeOnActorCreateInitializerThreadMaybe(void* delegate); + void setActorGenerationEnabled(bool enabled); + + // endregion + + auto getUnk3() const { return mUnk3; } + void incrementUnk3(); + void decrementUnk3(); + + void writeResidentActorsCsv(const sead::SafeString& file_path); + + auto& getProcUpdateStateListCS() { return mProcUpdateStateListCS; } + + void incrementPendingDeletions() { mNumPendingDeletions.increment(); } + void decrementPendingDeletions() { mNumPendingDeletions.decrement(); } + + u32 getNumJobTypes() const { return mNumJobTypes; } + +private: + void doAddToUpdateStateList_(BaseProc& proc); + + sead::CriticalSection* lockProcMap(); + void unlockProcMap(); + void getNextActor(sead::CriticalSection* cs, BaseProc* proc, u32 flags); + + static sead::BufferedSafeString* sResidentActorListStr; + static u32 sConstant0; + static u32 sConstant1; + static u32 sConstant2; + static u32 sConstant4; + + Status mStatus = Status::Idle; + util::SizedEnum<JobType, u8> mJobType = JobType::Invalid; + u8 mCurrentlyProcessingPrio = 8; + u8 mCounter = 0; + sead::CriticalSection mProcMapCS; + sead::OffsetList<BaseProc> mProcPreDeleteList; + u32 mNumJobTypes = 0; + BaseProcJobLists* mJobLists = nullptr; + BaseProcMap mProcMap; + sead::OffsetList<BaseProc> mProcUpdateStateList; + sead::CriticalSection mProcUpdateStateListCS; + sead::CriticalSection mProcPreDeleteListCS; + BaseProcMapNode* mLastProcMapNode = nullptr; + BaseProcJobQue* mProcJobQue = nullptr; + sead::Atomic<u32> mCreatedProcCounter = 0; + sead::Atomic<u32> mNumPendingDeletions = 0; + BaseProcInitializer* mProcInitializer = nullptr; + BaseProcDeleter* mProcDeleter = nullptr; + bool mIsPushingJobs = false; + bool mPushActorJobType3InsteadOf6 = false; + bool mEnableExtraJobPush = false; + Mode mMode = Mode::_0; + bool mUnk2 = false; + bool mIsInitialisingQuestMgrMaybe = false; + u8 mCurrentExtraJobArrayIdx = 0; + u8 mUnk3 = 0; + sead::BitFlag16 mSpecialJobTypesMask = 0; + u32 mMainThreadId = 0; + u32 mHavokThreadId1 = 0; + u32 mHavokThreadId2 = 0; + u32 mUnk4 = 0; + sead::SafeArray<ExtraJobLinkArray, 2> mExtraJobLinkArrays{}; +}; +KSYS_CHECK_SIZE_NX150(BaseProcMgr, 0x21a0); + +inline bool BaseProcMgr::addToUpdateStateList(BaseProc& proc) { + auto lock = sead::makeScopedLock(mProcUpdateStateListCS); + doAddToUpdateStateList_(proc); + return (proc.mStateFlags.set(BaseProc::StateFlags::RequestDelete) & + u32(BaseProc::StateFlags::RequestDelete)) != 0; +} + +} // namespace ksys::act diff --git a/src/KingSystem/ActorSystem/actBaseProcUnit.cpp b/src/KingSystem/ActorSystem/actBaseProcUnit.cpp new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcUnit.cpp diff --git a/src/KingSystem/ActorSystem/actBaseProcUnit.h b/src/KingSystem/ActorSystem/actBaseProcUnit.h new file mode 100644 index 00000000..7bfe7e28 --- /dev/null +++ b/src/KingSystem/ActorSystem/actBaseProcUnit.h @@ -0,0 +1,28 @@ +#pragma once + +#include <basis/seadTypes.h> +#include <thread/seadCriticalSection.h> + +namespace ksys::act { + +class BaseProc; +class BaseProcHandle; + +class BaseProcUnit { +public: + bool deleteProc(void*, BaseProcHandle* handle); + bool setProc(BaseProc* proc); + void unlinkProc(BaseProc* proc); + void cleanUp(BaseProc* proc, bool set_flag_5); + bool isParentHandleDefault() const; + +private: + u32 mFlags; + BaseProcHandle* mHandle; + BaseProc* mProc; + // FIXME: + // BaseProcRequest mRequest; + sead::CriticalSection mCS; +}; + +} // namespace ksys::act diff --git a/src/KingSystem/MessageSystem/mesTransceiver.h b/src/KingSystem/MessageSystem/mesTransceiver.h new file mode 100644 index 00000000..ea9d3f79 --- /dev/null +++ b/src/KingSystem/MessageSystem/mesTransceiver.h @@ -0,0 +1,31 @@ +#pragma once + +#include <basis/seadTypes.h> + +#include "KingSystem/Utils/Types.h" + +namespace ksys { + +namespace mes { + +struct TransceiverId { + TransceiverId(); + + TransceiverId& operator=(const TransceiverId& other) { + _0 = other._0; + _4 = other._4; + _8 = other._8; + _10 = other._10; + return *this; + } + + u32 _0; + u32 _4; + void* _8; + void* _10; +}; +KSYS_CHECK_SIZE_NX150(TransceiverId, 0x18); + +} // namespace mes + +} // namespace ksys diff --git a/src/KingSystem/Movie/.gitkeep b/src/KingSystem/Movie/.gitkeep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/KingSystem/Movie/.gitkeep diff --git a/src/KingSystem/Physics/NavMesh/.gitkeep b/src/KingSystem/Physics/NavMesh/.gitkeep new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/KingSystem/Physics/NavMesh/.gitkeep diff --git a/src/KingSystem/Terrain/teraSystem.h b/src/KingSystem/Terrain/teraSystem.h new file mode 100644 index 00000000..a3aab5d2 --- /dev/null +++ b/src/KingSystem/Terrain/teraSystem.h @@ -0,0 +1,34 @@ +#pragma once + +namespace ksys::tera { + +// TODO: +class ApertureMaps { + void updateMap(); +}; +class ApertureMapsCollector { + void allocateImage(); + void releaseImage(); +}; +class Core { + class Grass; + class Model; + class Tree; +}; +class ImageResourceMgr { + void procUnloadResidualRequest(); +}; +class ResourceHolder; +class Scene { + void exportFileBinary(); +}; +class System { + void loadScene(); +}; +class Water { + void setUpAttributeTable(); +}; + +bool checkTeraSystemStatus(); + +} // namespace ksys::tera diff --git a/src/KingSystem/Utils/StrTreeMap.h b/src/KingSystem/Utils/StrTreeMap.h new file mode 100644 index 00000000..d12bbcc6 --- /dev/null +++ b/src/KingSystem/Utils/StrTreeMap.h @@ -0,0 +1,45 @@ +#pragma once + +#include <basis/seadTypes.h> +#include <codec/seadHashCRC32.h> +#include <container/seadTreeMap.h> + +namespace ksys::util { + +class StrTreeMapKey { +public: + const sead::SafeString& key() const { return mKey; } + + // NON_MATCHING: stack + void setKey(u32 hash, sead::SafeString key) { + mKeyHash = hash; + mKey = key; + } + + void setKey(const sead::SafeString& key) { + setKey(sead::HashCRC32::calcStringHash(key.cstr()), key); + } + + s32 compare(const StrTreeMapKey& rhs) const { + if (mKeyHash < rhs.mKeyHash) + return -1; + if (mKeyHash > rhs.mKeyHash) + return 1; + return mKey.compare(rhs.mKey); + } + +private: + u32 mKeyHash = 0; + sead::SafeString mKey; +}; + +class StrTreeMapNode : public sead::TreeMapNode<StrTreeMapKey> { +public: + ~StrTreeMapNode() override { ; } + void erase_() override { mLeft = mRight = nullptr; } +}; + +template <typename Node> +class StrTreeMap : public sead::IntrusiveTreeMap<StrTreeMapKey, Node> {}; + +} // namespace ksys::util diff --git a/src/KingSystem/Utils/Types.h b/src/KingSystem/Utils/Types.h new file mode 100644 index 00000000..e29701ae --- /dev/null +++ b/src/KingSystem/Utils/Types.h @@ -0,0 +1,31 @@ +#pragma once + +#include <type_traits> + +#ifdef SEAD_DEBUG +#define KSYS_CHECK_SIZE_NX150(CLASS, SIZE) +#else +#define KSYS_CHECK_SIZE_NX150(CLASS, SIZE) static_assert(sizeof(CLASS) == SIZE) +#endif + +namespace ksys::util { + +/// For storing an enum with a particular storage size when specifying the underlying type of the +/// enum is not an option. +template <typename Enum, typename Storage> +struct SizedEnum { + static_assert(std::is_enum<Enum>()); + static_assert(!std::is_enum<Storage>()); + + constexpr SizedEnum() = default; + constexpr SizedEnum(Enum value) { *this = value; } + constexpr operator Enum() const { return static_cast<Enum>(mValue); } + constexpr SizedEnum& operator=(Enum value) { + mValue = static_cast<Storage>(value); + return *this; + } + + Storage mValue; +}; + +} // namespace ksys::util |
