// ------------------------------------------------------------------------------------------------ #include "Core/Privilege/Unit.hpp" #include "Core/Privilege/Entry.hpp" #include "Core/Privilege/Class.hpp" #include "Core/Privilege.hpp" // ------------------------------------------------------------------------------------------------ namespace SqMod { // ------------------------------------------------------------------------------------------------ SQMOD_DECL_TYPENAME(UnitTn, _SC("SqPrivilegeUnit")) // ------------------------------------------------------------------------------------------------ void PvUnit::SetTag(StackStrF & tag) { mTag = std::move(tag); // Hash the name and cache it (the name hash is cached in .mRes member variable) mTag.CacheHash(); // Do we still belong to a class? (should always be. but always check) if (!mClass.expired()) { auto p = mClass.lock(); // Propagate this change to the class as well p->UpdateUnitHash(mID, static_cast< size_t >(mTag.mRes)); // Propagate this change to the manager as well if (p->mManager) { p->mManager->UpdateUnitHash(mID, static_cast< size_t >(mTag.mRes)); } } } // ------------------------------------------------------------------------------------------------ void PvUnit::Release() { mOnQuery.Release(); mOnGained.Release(); mOnLost.Release(); mTag.Release(); mData.Release(); } // ------------------------------------------------------------------------------------------------ void PvUnit::ValidateManager() const { ValidateClass(); // Validate the manager through the class if (!(mClass.lock()->mManager)) { STHROWF("Unit ({} : {}) has invalid manager reference", mID, mTag.mPtr); } } // ------------------------------------------------------------------------------------------------ PvManager & PvUnit::ValidManager() const { ValidateClass(); // Retrieve the manager pointer (lock only once) PvManager * ptr = mClass.lock()->mManager; // Validate the manager through the class if (!ptr) { STHROWF("Unit ({} : {}) has invalid manager reference", mID, mTag.mPtr); } // Acquire a reference and return it return *ptr; } // ------------------------------------------------------------------------------------------------ const Function & PvUnit::GetOnQuery(SQInteger id) const { // Should we go for the one in the class? if (mOnQuery.IsNull()) { return ValidClass().GetOnQuery(id); } // We're using our own return mOnQuery; } // ------------------------------------------------------------------------------------------------ const Function & PvUnit::GetOnGained(SQInteger id) const { // Should we go for the one in the class? if (mOnGained.IsNull()) { return ValidClass().GetOnGained(id); } // We're using our own return mOnGained; } // ------------------------------------------------------------------------------------------------ const Function & PvUnit::GetOnLost(SQInteger id) const { // Should we go for the one in the class? if (mOnLost.IsNull()) { return ValidClass().GetOnLost(id); } // We're using our own return mOnLost; } // ------------------------------------------------------------------------------------------------ SQInteger PvUnit::GetEntryValue(SQInteger id) const { // Look for the specified status value auto itr = mPrivileges.find(id); // Should we go for the one in the parent? if (itr == mPrivileges.end()) { return ValidClass().GetEntryValue(id); } // Return the associated value return itr->second; } // ------------------------------------------------------------------------------------------------ SQInteger PvUnit::GetInheritedEntryValue(SQInteger id) const { return ValidClass().GetEntryValue(id); } // ------------------------------------------------------------------------------------------------ void PvUnit::DoGained(SQInteger id, SQInteger value) const { // Function to be called when a privilege is gained const Function & gained = GetOnGained(id); // Is there someone interested in this result? if (!gained.IsNull()) { gained.Execute(id, value); } } // ------------------------------------------------------------------------------------------------ void PvUnit::DoLost(SQInteger id, SQInteger value) const { // Function to be called when a privilege is lost const Function & lost = GetOnLost(id); // Is there someone interested in this result? if (!lost.IsNull()) { lost.Execute(id, value); } } // ------------------------------------------------------------------------------------------------ void PvUnit::DoChanged(SQInteger id, bool status, SQInteger value) const { // Was this considered an upgrade? if (status) { DoGained(id, value); } else { DoLost(id, value); } } // ------------------------------------------------------------------------------------------------ void PvUnit::AssignPrivilege(SQInteger id, SQInteger value) { // Find the current status of this entry SQInteger current = GetEntryValue(id); // Retrieve the associated entry PvEntry & entry = ValidManager().ValidEntry(id); // Is there someone that can identify this change? if (!entry.mOnModify.IsNull()) { LightObj r = entry.mOnModify.Eval(current, value); // Was this considered a change? if (!r.IsNull()) { DoChanged(id, r.Cast< bool >(), value); } } // Either way, we are setting this value mPrivileges[id] = value; } // ------------------------------------------------------------------------------------------------ void PvUnit::RemovePrivilege(SQInteger id) { // Look for the status of this value auto itr = mPrivileges.find(id); // Do we even have this status? if (itr == mPrivileges.end()) { return; // Nothing to remove! } // Find the inherited status of this entry SQInteger inherited = GetInheritedEntryValue(id); // Get the current status of this entry SQInteger current = itr->second; // Erase this status value mPrivileges.erase(itr); // Retrieve the associated entry PvEntry & entry = ValidManager().ValidEntry(id); // Is there someone that can identify this change? if (!entry.mOnModify.IsNull()) { LightObj r = entry.mOnModify.Eval(current, inherited); // Was this considered a change? if (!r.IsNull()) { DoChanged(id, r.Cast< bool >(), current); } } } // ------------------------------------------------------------------------------------------------ void PvUnit::ModifyPrivilege(SQInteger id, SQInteger value) { // Find the current status of this entry SQInteger current = GetEntryValue(id); // If they are exactly the same if (value == current) { return; // Just do nothing } // Retrieve the associated entry PvEntry & entry = ValidManager().ValidEntry(id); // Is there someone that can identify this change? if (!entry.mOnModify.IsNull()) { LightObj r = entry.mOnModify.Eval(current, value); // Was this considered a change? if (!r.IsNull()) { DoChanged(id, r.Cast< bool >(), value); // Use this value now as well mPrivileges[id] = value; } } else { // By default we use > comparison to decide upgrades DoChanged(id, value > current, value); // Use this value now mPrivileges[id] = value; } } // ------------------------------------------------------------------------------------------------ void PvUnit::AssignPrivilege(StackStrF & tag, SQInteger value) { AssignPrivilege(ValidManager().GetValidEntryWithTag(tag.CacheHash())->mID, value); } // ------------------------------------------------------------------------------------------------ void PvUnit::RemovePrivilege(StackStrF & tag) { RemovePrivilege(ValidManager().GetValidEntryWithTag(tag.CacheHash())->mID); } // ------------------------------------------------------------------------------------------------ void PvUnit::ModifyPrivilege(StackStrF & tag, SQInteger value) { ModifyPrivilege(ValidManager().GetValidEntryWithTag(tag.CacheHash())->mID, value); } // ------------------------------------------------------------------------------------------------ void PvUnit::RemoveAllPrivileges() { // Discard all privileges but not before gaining ownership of them PvStatusList list = std::move(mPrivileges); // Go over all entries and see if this unit will gain or loose any privileges from this change for (const auto & e : list) { // Get the value that we have now after the change SQInteger current = GetEntryValue(e.first); // Were they literally the same? if (current == e.second) { continue; // Don't even bother } // Retrieve the associated entry PvEntry & entry = ValidManager().ValidEntry(e.first); // Is there someone that can identify this change? if (!entry.mOnModify.IsNull()) { LightObj r = entry.mOnModify.Eval(current, e.second); // Was this considered a change? if (!r.IsNull()) { DoChanged(e.first, r.Cast< bool >(), e.second); } } else { // By default we use > comparison to decide upgrades DoChanged(e.first, e.second > current, e.second); } } } // ------------------------------------------------------------------------------------------------ void PvUnit::AssignClass(const std::shared_ptr< PvClass > & cls) { // Make sure we have a valid class ValidateClass(); // Get a strong reference to the current class PvClass::Ref current = mClass.lock(); // Are they the same? if (current == cls) { return; // Nothing will change } // Assign this class mClass = cls; // Propagate changes ValidManager().PropagateClassChange(*this, cls); } // ------------------------------------------------------------------------------------------------ bool PvUnit::Can(SQInteger id, SQInteger req) const { // Get the current status of the specified entry SQInteger current = GetEntryValue(id); // Retrieve the function responsible for the query event const Function & query = GetOnQuery(id); // Is there someone that can arbitrate this request? if (!query.IsNull()) { // Attempt arbitration LightObj r = query.Eval(current, req); // If NULL or false the request was denied if (!r.IsNull() && r.Cast< bool >()) { return true; // Request allowed } } // We use the >= comparison to settle arbitration else if (current >= ValidManager().ValidEntry(id).mDefault) { return true; } // Request failed, no arbitration return false; } // ------------------------------------------------------------------------------------------------ void PvUnit::EachEntryID(Object & ctx, Function & func) const { // In order to be safe from modifications while iterating, create a copy PvStatusList list(mPrivileges); // Iterate entries and forward the ID to the callback for (const auto & e : list) { func(ctx, e.first); } } // ================================================================================================ LightObj SqPvUnit::GetClass() const { return LightObj(SqTypeIdentity< SqPvClass >{}, SqVM(), Valid().mClass); } // ------------------------------------------------------------------------------------------------ inline void SqPvUnit::SetClass(const SqPvClass & cls) const { Validate(); cls.Validate(); mI.lock()->AssignClass(cls.mI.lock()); } // ------------------------------------------------------------------------------------------------ LightObj SqPvUnit::GetManager() const { return LightObj(ValidCls().mManager); } // ------------------------------------------------------------------------------------------------ bool SqPvUnit::Can(LightObj & obj, SQInteger req) const { // Entry ID? if (obj.GetType() == OT_INTEGER) { return Valid().Can(obj.Cast< SQInteger >(), req); } // Entry tag? else if (obj.GetType() == OT_STRING) { Var< LightObj >::push(SqVM(), obj); // Tag string StackStrF tag(SqVM(), -1); // Attempt extraction if (SQ_FAILED(tag.Proc(false))) { // Restore the stack first sq_poptop(SqVM()); // Now the exception STHROWF("Unable to extract tag string"); } // Restore the stack sq_poptop(SqVM()); // Reference the instance PvUnit & u = Valid(); // Generate and cache the hash tag.CacheHash(); // Forward request return u.Can(u.ValidManager().GetValidEntryWithTag(tag)->mID, req); } // Entry instance? else if (obj.GetType() == OT_INSTANCE && obj.GetTypeTag() == StaticClassTypeTag< SqPvEntry >::Get()) { return Valid().Can(obj.CastI< SqPvEntry >()->Valid().mID, req); } STHROWF("Unknown or unsupported entry identification type (%s)", SqTypeName(obj.GetType())); SQ_UNREACHABLE } // ================================================================================================ void Register_Privilege_Unit(HSQUIRRELVM vm, Table & ns) { ns.Bind(_SC("Unit"), Class< SqPvUnit, NoConstructor< SqPvUnit > >(vm, UnitTn::Str) // Meta-methods .SquirrelFunc(_SC("_typename"), &UnitTn::Fn) .Func(_SC("_tostring"), &SqPvUnit::ToString) // Core Properties .Prop(_SC("ID"), &SqPvUnit::GetID) .Prop(_SC("Tag"), &SqPvUnit::GetTag, &SqPvUnit::SetTag) .Prop(_SC("Data"), &SqPvUnit::GetData, &SqPvUnit::SetData) .Prop(_SC("Authority"), &SqPvUnit::GetAuthority, &SqPvUnit::SetAuthority) .Prop(_SC("Manager"), &SqPvUnit::GetManager) .Prop(_SC("Class"), &SqPvUnit::GetClass, &SqPvUnit::SetClass) .Prop(_SC("Authority"), &SqPvUnit::GetAuthority, &SqPvUnit::SetAuthority) // Core Methods .FmtFunc(_SC("SetTag"), &SqPvUnit::ApplyTag) .CbFunc(_SC("OnQuery"), &SqPvUnit::SetOnQuery) .CbFunc(_SC("OnLost"), &SqPvUnit::SetOnLost) .CbFunc(_SC("OnGained"), &SqPvUnit::SetOnGained) // Member Methods .Func(_SC("Can"), &SqPvUnit::Can) .Func(_SC("Assign"), &SqPvUnit::AssignPrivilegeWithID) .FmtFunc(_SC("AssignWithTag"), &SqPvUnit::AssignPrivilegeWithTag) .Func(_SC("Remove"), &SqPvUnit::RemovePrivilegeWithID) .FmtFunc(_SC("RemoveWithTag"), &SqPvUnit::RemovePrivilegeWithTag) .Func(_SC("Modify"), &SqPvUnit::ModifyPrivilegeWithID) .FmtFunc(_SC("ModifyWithTag"), &SqPvUnit::ModifyPrivilegeWithTag) .Func(_SC("RemoveAll"), &SqPvUnit::RemoveAllPrivileges) .Func(_SC("EachEntryID"), &SqPvUnit::EachEntryID) ); } } // Namespace:: SqMod