mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2025-07-27 19:21:47 +02:00
.github
bin
module
Base
Core
Privilege
Base.cpp
Base.hpp
Class.cpp
Class.hpp
Entry.cpp
Entry.hpp
Unit.cpp
Unit.hpp
Areas.cpp
Areas.hpp
Buffer.cpp
Buffer.hpp
Command.cpp
Command.hpp
Common.cpp
Common.hpp
Entity.cpp
Entity.hpp
Events.inc
Inventory.cpp
Inventory.hpp
Loot.cpp
Loot.hpp
Privilege.cpp
Privilege.hpp
Routine.cpp
Routine.hpp
Script.cpp
Script.hpp
Signal.cpp
Signal.hpp
Tasks.cpp
Tasks.hpp
ThreadPool.cpp
ThreadPool.hpp
Utility.cpp
Utility.hpp
VecMap.hpp
Entity
Library
Misc
PocoLib
Sqrat
VCMP
CMakeLists.txt
Core.cpp
Core.hpp
Logger.cpp
Logger.hpp
Main.cpp
Register.cpp
SqBase.hpp
vendor
.gitignore
.gitmodules
CMakeLists.txt
LICENSE
README.md
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
|