mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-08 08:47:17 +01:00
449 lines
15 KiB
C++
449 lines
15 KiB
C++
// ------------------------------------------------------------------------------------------------
|
|
#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
|