summaryrefslogtreecommitdiff
path: root/src/KingSystem/ActorSystem/actBaseProc.cpp
diff options
context:
space:
mode:
authorLéo Lam <leo@leolam.fr>2020-06-05 17:09:06 +0200
committerLéo Lam <leo@leolam.fr>2020-08-18 22:50:25 +0200
commit2de366be0fa75e571c605e017b5604bccbdb75bf (patch)
treeb8044fc96bba792b1875067032da2fd4498a44a3 /src/KingSystem/ActorSystem/actBaseProc.cpp
Initial commit
Diffstat (limited to 'src/KingSystem/ActorSystem/actBaseProc.cpp')
-rw-r--r--src/KingSystem/ActorSystem/actBaseProc.cpp409
1 files changed, 409 insertions, 0 deletions
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