diff --git a/module/CMakeLists.txt b/module/CMakeLists.txt index e4016361..75b5f637 100644 --- a/module/CMakeLists.txt +++ b/module/CMakeLists.txt @@ -38,11 +38,17 @@ add_library(SqModule MODULE SqBase.hpp Main.cpp Core/Command.cpp Core/Command.hpp Core/Common.cpp Core/Common.hpp Core/Entity.cpp Core/Entity.hpp + Core/Privilege.cpp Core/Privilege.hpp + Core/Privilege/Base.cpp Core/Privilege/Base.hpp + Core/Privilege/Class.cpp Core/Privilege/Class.hpp + Core/Privilege/Entry.cpp Core/Privilege/Entry.hpp + Core/Privilege/Unit.cpp Core/Privilege/Unit.hpp Core/Routine.cpp Core/Routine.hpp Core/Script.cpp Core/Script.hpp Core/Signal.cpp Core/Signal.hpp Core/Tasks.cpp Core/Tasks.hpp Core/Utility.cpp Core/Utility.hpp + Core/VecMap.hpp # Entity Entity/Blip.cpp Entity/Blip.hpp Entity/Checkpoint.cpp Entity/Checkpoint.hpp diff --git a/module/Core.cpp b/module/Core.cpp index f8c9a9e2..d366f635 100644 --- a/module/Core.cpp +++ b/module/Core.cpp @@ -44,7 +44,7 @@ extern void InitializeTasks(); extern void InitializeRoutines(); extern void TerminateAreas(); extern void TerminateTasks(); -//extern void TerminatePrivileges(); +extern void TerminatePrivileges(); extern void TerminateRoutines(); extern void TerminateCommands(); extern void TerminateSignals(); @@ -501,7 +501,7 @@ void Core::Terminate(bool shutdown) // Release all managed areas TerminateAreas(); // Release privilege managers - //TerminatePrivileges(); + TerminatePrivileges(); // Release announcers AnnounceTerminate(); // Release ZMQ sockets diff --git a/module/Core/Privilege.cpp b/module/Core/Privilege.cpp new file mode 100644 index 00000000..836935a8 --- /dev/null +++ b/module/Core/Privilege.cpp @@ -0,0 +1,391 @@ +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(ManagerTn, _SC("SqPrivilegeManager")) + +// ------------------------------------------------------------------------------------------------ +void TerminatePrivileges() +{ + // Go over all managers and try to close them + for (PvManager * inst = PvManager::sHead; inst && inst->mNext != PvManager::sHead; inst = inst->mNext) + { + inst->Terminate(); + } +} + +// ------------------------------------------------------------------------------------------------ +void PvManager::Release() +{ + // Release objects from this instance + m_OnQuery.Release(); + m_OnGained.Release(); + m_OnLost.Release(); + m_Tag.Release(); + m_Data.Release(); +} + +// ------------------------------------------------------------------------------------------------ +void PvManager::Terminate() +{ + // Release script objects held by classes + for (auto & c : m_Classes) + { + //c->Release(); + } + // Release script objects held by units + for (auto & u : m_Units) + { + //c->Release(); + } + // Release script objects held by entries + for (auto & e : m_Entries) + { + //c->Release(); + } + // Release script objects held by the manager + Release(); +} + +// ------------------------------------------------------------------------------------------------ +LightObj PvManager::CreateEntry(SQInteger id, StackStrF & name) +{ + ModifyEntries(); + // Look for a similar entry + auto itr = m_Entries.find(PvIdentity(id)); + // Is this unique? + if (itr != m_Entries.end()) + { + STHROWF("Entry ({} : {}) already exists", id, name.mPtr); + } + // We backup the name hash now because I don't know if the move will happen before or after i create the identity + // Compiler optimizations and sh!t. (the name hash is cached in .mRes member variable) + const auto h = name.CacheHash().GetHash(); + // Create it now + auto & e = m_Entries.emplace_back(PvIdentity(id, h), std::make_shared< PvEntry >(id, std::move(name), this)); + // Create a wrapper instance and return it + return LightObj(SqTypeIdentity< SqPvEntry >{}, SqVM(), e); +} + +// ------------------------------------------------------------------------------------------------ +LightObj PvManager::CreateClass(SQInteger id, StackStrF & name) +{ + ModifyClasses(); + // Look for a similar class + auto itr = m_Classes.find(PvIdentity(id)); + // Is this unique? + if (itr != m_Classes.end()) + { + STHROWF("Class ({} : {}) already exists", id, name.mPtr); + } + // We backup the name hash now because I don't know if the move will happen before or after i create the identity + // Compiler optimizations and sh!t. (the name hash is cached in .mRes member variable) + const auto h = name.CacheHash().GetHash(); + // Create it now + auto & e = m_Classes.emplace_back(PvIdentity(id, h), std::make_shared< PvClass >(id, std::move(name), this)); + // Create a wrapper instance and return it + return LightObj(SqTypeIdentity< SqPvClass >{}, SqVM(), e); +} + +// ------------------------------------------------------------------------------------------------ +LightObj PvManager::CreateUnit(SQInteger id, const SqPvClass & cls, StackStrF & name) +{ + ModifyUnits(); + // Validate the given class + cls.Validate(); + // Look for a similar unit + auto itr = m_Units.find(PvIdentity(id)); + // Is this unique? + if (itr != m_Units.end()) + { + STHROWF("Unit ({} : {}) already exists", id, name.mPtr); + } + // We backup the name hash now because I don't know if the move will happen before or after i create the identity + // Compiler optimizations and sh!t. (the name hash is cached in .mRes member variable) + const auto h = name.CacheHash().GetHash(); + // Create it now + auto & e = m_Units.emplace_back(PvIdentity(id, h), std::make_shared< PvUnit >(id, std::move(name), cls.mI)); + // Create a wrapper instance and return it + return LightObj(SqTypeIdentity< SqPvUnit >{}, SqVM(), e); +} + +// ------------------------------------------------------------------------------------------------ +void PvManager::PropagateParentAssign(const PvClass & cls, const PvClass::Ref & parent) +{ + +} + +// ------------------------------------------------------------------------------------------------ +void PvManager::PropagateParentChange(const PvClass & cls, const PvClass::Ref & parent) +{ + // Prevent any changes to entries during this operation + AutoAssign< bool > aag(m_LockEntries, false, true); + // Go over all entries and see if the specified class will gain or loose any privileges from this change + for (const auto & e : m_Entries) + { + // Owned privileges are not affected regardless of the inherited class + { + // See if this class inherits the current entry + auto itr = cls.mPrivileges.find(e.second->mID); + // Is this entry inherited or owned? + if (itr != cls.mPrivileges.end()) + { + break; // Nothing new will happen here + } + } + // We know we inherit this entry value so let's get that value instead + SQInteger inherited = cls.GetInheritedEntryValue(e.second->mID); + // Get the value that will be inherited for this entry + SQInteger current = parent->GetEntryValue(e.second->mID); + // Are they literally the same? + if (inherited == current) + { + break; // Don't even bother + } + // Find out who can identify this change + const Function & modify = e.second->mOnModify.IsNull() ? m_OnModify : e.second->mOnModify; + // Is there someone that can identify this change? + if (!modify.IsNull()) + { + LightObj r = modify.Eval(inherited, current); + // Was this considered a change? + if (!r.IsNull()) + { + cls.DoChanged(e.second->mID, r.Cast< bool >(), current); + } + } + else + { + // By default we use > comparison to decide upgrades + cls.DoChanged(e.second->mID, current > inherited, current); + } + } +} + +// ------------------------------------------------------------------------------------------------ +void PvManager::PropagateClassChange(const PvUnit & unit, const PvClass::Ref & cls) +{ + // Prevent any changes to entries during this operation + AutoAssign< bool > aag(m_LockEntries, false, true); + // Go over all entries and see if the specified unit will gain or loose any privileges from this change + for (const auto & e : m_Entries) + { + // Owned privileges are not affected regardless of the inherited class + { + // See if this unit inherits the current entry + auto itr = unit.mPrivileges.find(e.second->mID); + // Is this entry inherited or owned? + if (itr != unit.mPrivileges.end()) + { + break; // Nothing new will happen here + } + } + // We know we inherit this entry value so let's get that value instead + SQInteger inherited = unit.GetInheritedEntryValue(e.second->mID); + // Get the value that will be inherited for this entry + SQInteger current = cls->GetEntryValue(e.second->mID); + // Are they literally the same? + if (inherited == current) + { + break; // Don't even bother + } + // Find out who can identify this change + const Function & modify = e.second->mOnModify.IsNull() ? m_OnModify : e.second->mOnModify; + // Is there someone that can identify this change? + if (!modify.IsNull()) + { + LightObj r = modify.Eval(inherited, current); + // Was this considered a change? + if (!r.IsNull()) + { + unit.DoChanged(e.second->mID, r.Cast< bool >(), current); + } + } + else + { + // By default we use > comparison to decide upgrades + unit.DoChanged(e.second->mID, current > inherited, current); + } + } +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD LightObj PvManager::GetEntryWithID(SQInteger id) +{ + return LightObj(SqTypeIdentity< SqPvEntry >{}, SqVM(), GetValidEntry(id)); +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD LightObj PvManager::GetEntryWithTag(StackStrF & tag) +{ + return LightObj(SqTypeIdentity< SqPvEntry >{}, SqVM(), GetValidEntryWithTag(tag.CacheHash())); +} + +// ------------------------------------------------------------------------------------------------ +bool PvManager::HaveEntryWithID(SQInteger id) +{ + for (const auto & e : m_Entries) // NOLINT(readability-use-anyofallof) + { + if (e.first.mID == id) + { + return true; + } + } + // Not found + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool PvManager::HaveEntryWithTag(StackStrF & tag) +{ + const size_t h = tag.ToHash(); + for (const auto & e : m_Entries) // NOLINT(readability-use-anyofallof) + { + if (e.first.mHash == h) + { + return true; + } + } + // Not found + return false; +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD LightObj PvManager::GetClassWithID(SQInteger id) +{ + return LightObj(SqTypeIdentity< SqPvClass >{}, SqVM(), GetValidClass(id)); +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD LightObj PvManager::GetClassWithTag(StackStrF & tag) +{ + return LightObj(SqTypeIdentity< SqPvClass >{}, SqVM(), GetValidClassWithTag(tag.CacheHash())); +} + +// ------------------------------------------------------------------------------------------------ +bool PvManager::HaveClassWithID(SQInteger id) +{ + for (const auto & c : m_Classes) // NOLINT(readability-use-anyofallof) + { + if (c.first.mID == id) + { + return true; + } + } + // Not found + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool PvManager::HaveClassWithTag(StackStrF & tag) +{ + const size_t h = tag.ToHash(); + for (const auto & c : m_Classes) // NOLINT(readability-use-anyofallof) + { + if (c.first.mHash == h) + { + return true; + } + } + // Not found + return false; +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD LightObj PvManager::GetUnitWithID(SQInteger id) +{ + return LightObj(SqTypeIdentity< SqPvUnit >{}, SqVM(), GetValidUnit(id)); +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD LightObj PvManager::GetUnitWithTag(StackStrF & tag) +{ + return LightObj(SqTypeIdentity< SqPvUnit >{}, SqVM(), GetValidUnitWithTag(tag.CacheHash())); +} + +// ------------------------------------------------------------------------------------------------ +bool PvManager::HaveUnitWithID(SQInteger id) +{ + for (const auto & u : m_Units) // NOLINT(readability-use-anyofallof) + { + if (u.first.mID == id) + { + return true; + } + } + // Not found + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool PvManager::HaveUnitWithTag(StackStrF & tag) +{ + const size_t h = tag.ToHash(); + for (const auto & u : m_Units) // NOLINT(readability-use-anyofallof) + { + if (u.first.mHash == h) + { + return true; + } + } + // Not found + return false; +} + +// ------------------------------------------------------------------------------------------------ +extern void Register_Privilege_Class(HSQUIRRELVM vm, Table & ns); +extern void Register_Privilege_Entry(HSQUIRRELVM vm, Table & ns); +extern void Register_Privilege_Unit(HSQUIRRELVM vm, Table & ns); + +// ================================================================================================ +void Register_Privilege(HSQUIRRELVM vm) +{ + Table ns(vm); + + // -------------------------------------------------------------------------------------------- + Register_Privilege_Class(vm, ns); + Register_Privilege_Entry(vm, ns); + Register_Privilege_Unit(vm, ns); + + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("Manager"), + Class< PvManager, NoCopy< PvManager > >(vm, ManagerTn::Str) + // Constructors + .Ctor() + .Ctor< StackStrF & >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &ManagerTn::Fn) + .Func(_SC("_tostring"), &PvManager::ToString) + // Core Properties + .Prop(_SC("Tag"), &PvManager::GetTag, &PvManager::SetTag) + .Prop(_SC("Data"), &PvManager::GetData, &PvManager::SetData) + // Core Methods + .FmtFunc(_SC("SetTag"), &PvManager::ApplyTag) + .CbFunc(_SC("OnQuery"), &PvManager::SetOnQuery) + .CbFunc(_SC("OnLost"), &PvManager::SetOnLost) + .CbFunc(_SC("OnGained"), &PvManager::SetOnGained) + // Member Methods + .CbFunc(_SC("CreateEntry"), &PvManager::CreateEntry) + .CbFunc(_SC("CreateClass"), &PvManager::CreateClass) + .CbFunc(_SC("CreateUnit"), &PvManager::CreateUnit) + .Func(_SC("GetEntry"), &PvManager::GetEntryWithID) + .FmtFunc(_SC("GetEntryWithTag"), &PvManager::GetEntryWithTag) + .Func(_SC("HaveEntry"), &PvManager::HaveEntryWithID) + .FmtFunc(_SC("HaveEntryWithTag"), &PvManager::HaveEntryWithTag) + .Func(_SC("GetClass"), &PvManager::GetClassWithID) + .FmtFunc(_SC("GetClassWithTag"), &PvManager::GetClassWithTag) + .Func(_SC("HaveClass"), &PvManager::HaveClassWithID) + .FmtFunc(_SC("HaveClassWithTag"), &PvManager::HaveClassWithTag) + .Func(_SC("GetUnit"), &PvManager::GetUnitWithID) + .FmtFunc(_SC("GetUnitWithTag"), &PvManager::GetUnitWithTag) + .Func(_SC("HaveUnit"), &PvManager::HaveUnitWithID) + .FmtFunc(_SC("HaveUnitWithTag"), &PvManager::HaveUnitWithTag) + ); + + RootTable(vm).Bind(_SC("SqPrivilege"), ns); +} + +} // Namespace:: SqMod diff --git a/module/Core/Privilege.hpp b/module/Core/Privilege.hpp new file mode 100644 index 00000000..d2b3fca7 --- /dev/null +++ b/module/Core/Privilege.hpp @@ -0,0 +1,621 @@ +#pragma once + +/* ------------------------------------------------------------------------------------------------ + * This could have been much more flexible and advanced than this (as initially planned). + * However, several factors played an important role in its current simplicity: + * 1) Time constraints: + * This had to be done over the course of a weekend (at worst). It might be extended + * over time but right now this will do. + * 2) Performance cost: + * Privilege micro-management will become costly if scripting is involved. Therefore, several + * sacrifices had to be made. Such as type-agnostic status value, script controlled query etc. +*/ + +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege/Class.hpp" +#include "Core/Privilege/Entry.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Privilege manager responsible for keeping track of each resource and handling requests. +*/ +struct PvManager : SqChainedInstances< PvManager > +{ + /* -------------------------------------------------------------------------------------------- + * Container that stores privilege entries. + */ + PvEntry::List m_Entries; + + /* -------------------------------------------------------------------------------------------- + * Container that stores the managed classes. + */ + PvClass::List m_Classes; + + /* -------------------------------------------------------------------------------------------- + * Container that stores all the managed units. + */ + PvUnit::List m_Units; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege query event. + */ + Function m_OnQuery; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege modify event. + */ + Function m_OnModify; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege gained event. + */ + Function m_OnGained; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege lost event. + */ + Function m_OnLost; + + /* -------------------------------------------------------------------------------------------- + * User tag associated with this instance. + */ + StackStrF m_Tag; + + /* -------------------------------------------------------------------------------------------- + * User data associated with this instance. + */ + LightObj m_Data; + + /* -------------------------------------------------------------------------------------------- + * Used to prevent modification of entries, classes or units. + */ + bool m_LockEntries; + bool m_LockClasses; + bool m_LockUnits; + +public: + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + PvManager() + : m_Entries(), m_Classes(), m_Units() + , m_OnQuery(), m_OnModify(), m_OnGained(), m_OnLost() + , m_Tag(), m_Data() + , m_LockEntries(false), m_LockClasses(false), m_LockUnits(false) + { + // Remember this instance + ChainInstance(); + } + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + explicit PvManager(StackStrF & tag) + : m_Entries(), m_Classes(), m_Units() + , m_OnQuery(), m_OnModify(), m_OnGained(), m_OnLost() + , m_Tag(std::move(tag)), m_Data() + , m_LockEntries(false), m_LockClasses(false), m_LockUnits(false) + { + // Remember this instance + ChainInstance(); + } + + /* ------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + PvManager(const PvManager & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move constructor (disabled). + */ + PvManager(PvManager && o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Destructor. + */ + ~PvManager() + { + // Forget about this instance + UnchainInstance(); + } + + /* ------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + PvManager & operator = (const PvManager & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move assignment operator (disabled). + */ + PvManager & operator = (PvManager && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Used by the script engine to convert an instance of this type to a string. + */ + SQMOD_NODISCARD auto ToString() const + { + return m_Tag.mObj; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated user tag. + */ + SQMOD_NODISCARD auto GetTag() const + { + return m_Tag.mObj; + } + + /* -------------------------------------------------------------------------------------------- + * Modify the associated user tag. + */ + void SetTag(StackStrF & tag) + { + m_Tag = std::move(tag); + } + + /* -------------------------------------------------------------------------------------------- + * Modify the associated user tag. + */ + SQMOD_NODISCARD PvManager & ApplyTag(StackStrF & tag) + { + SetTag(tag); + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the associated user data. + */ + SQMOD_NODISCARD LightObj & GetData() + { + return m_Data; + } + + /* -------------------------------------------------------------------------------------------- + * Modify the associated user data. + */ + void SetData(LightObj & data) + { + m_Data = data; + } + + /* -------------------------------------------------------------------------------------------- + * Release all script resources. Recursively forward request to all classes, units and entries. + */ + void Release(); + + /* -------------------------------------------------------------------------------------------- + * Terminate the all managers by releasing their classes and units and callbacks. + */ + void Terminate(); + + /* -------------------------------------------------------------------------------------------- + * Makes sure you can modify current entries. + */ + void ModifyEntries() + { + if (m_LockEntries) + { + STHROWF("Entries cannot be modified"); + } + } + + /* -------------------------------------------------------------------------------------------- + * Makes sure you can modify current entries. + */ + void ModifyClasses() + { + if (m_LockClasses) + { + STHROWF("Classes cannot be modified"); + } + } + + /* -------------------------------------------------------------------------------------------- + * Makes sure you can modify current entries. + */ + void ModifyUnits() + { + if (m_LockUnits) + { + STHROWF("Units cannot be modified"); + } + } + + /* -------------------------------------------------------------------------------------------- + * Bind a script function to the status query callback. + */ + void SetOnQuery(Function & func) + { + m_OnQuery = std::move(func); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the function bound to the status query callback. + */ + SQMOD_NODISCARD const Function & GetOnQuery() const + { + return m_OnQuery; + } + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle query events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnQuery(SQInteger id) const + { + // Look for a the specified entry + auto itr = m_Entries.find(PvIdentity(id)); + // Does the specified entry exist? + if (itr != m_Entries.end()) + { + return itr->second->mOnQuery; + } + // Default to the global one + return m_OnQuery; + } + + /* -------------------------------------------------------------------------------------------- + * Bind a script function to the status modify callback. + */ + void SetOnModify(Function & func) + { + m_OnModify = std::move(func); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the function bound to the status modify callback. + */ + SQMOD_NODISCARD const Function & GetOnModify() const + { + return m_OnModify; + } + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle modify events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnModify(SQInteger id) const + { + // Look for a the specified entry + auto itr = m_Entries.find(PvIdentity(id)); + // Does the specified entry exist? + if (itr != m_Entries.end()) + { + return itr->second->mOnModify; + } + // Default to the global one + return m_OnModify; + } + + /* -------------------------------------------------------------------------------------------- + * Bind a script function to the status gained callback. + */ + void SetOnGained(Function & func) + { + m_OnGained = std::move(func); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the function bound to the status gained callback. + */ + SQMOD_NODISCARD const Function & GetOnGained() const + { + return m_OnGained; + } + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle gained events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnGained(SQInteger id) const + { + // Look for a the specified entry + auto itr = m_Entries.find(PvIdentity(id)); + // Does the specified entry exist? + if (itr != m_Entries.end()) + { + return itr->second->mOnGained; + } + // Default to the global one + return m_OnGained; + } + + /* -------------------------------------------------------------------------------------------- + * Bind a script function to the status lost callback. + */ + void SetOnLost(Function & func) + { + m_OnLost = std::move(func); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the function bound to the status lost callback. + */ + SQMOD_NODISCARD Function & GetOnLost() + { + return m_OnLost; + } + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle lost events for a certain entry. + */ + SQMOD_NODISCARD Function & GetOnLost(SQInteger id) + { + // Look for a the specified entry + auto itr = m_Entries.find(PvIdentity(id)); + // Does the specified entry exist? + if (itr != m_Entries.end()) + { + return itr->second->mOnLost; + } + // Default to the global one + return m_OnLost; + } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific entry exists and return a smart reference to it. + */ + SQMOD_NODISCARD const PvEntry::Ref & GetValidEntry(SQInteger id) + { + // Look for a the specified entry + auto itr = m_Entries.find(PvIdentity(id)); + // Does this entry exist? + if (itr == m_Entries.end()) + { + STHROWF("Entry ({}) doesn't exists", id); + } + // Return the reference + return itr->second; + } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific class exists and return a smart reference to it. + */ + SQMOD_NODISCARD const PvClass::Ref & GetValidClass(SQInteger id) + { + // Look for a the specified class + auto itr = m_Classes.find(PvIdentity(id)); + // Does this class exist? + if (itr == m_Classes.end()) + { + STHROWF("Class ({}) doesn't exists", id); + } + // Return the reference + return itr->second; + } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific unit exists and return a smart reference to it. + */ + SQMOD_NODISCARD const PvUnit::Ref & GetValidUnit(SQInteger id) + { + // Look for a the specified unit + auto itr = m_Units.find(PvIdentity(id)); + // Does this unit exist? + if (itr == m_Units.end()) + { + STHROWF("Unit ({}) doesn't exists", id); + } + // Return the reference + return itr->second; + } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific entry exists and return a reference to it. + */ + SQMOD_NODISCARD PvEntry & ValidEntry(SQInteger id) { return *GetValidEntry(id); } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific class exists and return a reference to it. + */ + SQMOD_NODISCARD PvClass & ValidClass(SQInteger id) { return *GetValidClass(id); } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific unit exists and return a reference to it. + */ + SQMOD_NODISCARD PvUnit & ValidUnit(SQInteger id) { return *GetValidUnit(id); } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific entry exists and return a smart reference to it. + */ + SQMOD_NODISCARD const PvEntry::Ref & GetValidEntryWithTag(StackStrF & tag) + { + const size_t h = tag.GetHash(); + for (const auto & e : m_Entries) + { + if (e.first.mHash == h) + { + return e.second; + } + } + // Not found + STHROWF("Entry ({}) doesn't exists", tag.mPtr); + SQ_UNREACHABLE + } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific class exists and return a smart reference to it. + */ + SQMOD_NODISCARD const PvClass::Ref & GetValidClassWithTag(StackStrF & tag) + { + const size_t h = tag.GetHash(); + for (const auto & c : m_Classes) + { + if (c.first.mHash == h) + { + return c.second; + } + } + // Not found + STHROWF("Class ({}) doesn't exists", tag.mPtr); + SQ_UNREACHABLE + } + + /* -------------------------------------------------------------------------------------------- + * Make sure a specific unit exists and return a smart reference to it. + */ + SQMOD_NODISCARD const PvUnit::Ref & GetValidUnitWithTag(StackStrF & tag) + { + const size_t h = tag.GetHash(); + for (const auto & u : m_Units) + { + if (u.first.mHash == h) + { + return u.second; + } + } + // Not found + STHROWF("Unit ({}) doesn't exists", tag.mPtr); + SQ_UNREACHABLE + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the default value of an entry. + */ + SQMOD_NODISCARD SQInteger GetEntryValue(SQInteger id) const + { + // Look for a the specified entry + auto itr = m_Entries.find(PvIdentity(id)); + // Does this entry exist? + if (itr == m_Entries.end()) + { + STHROWF("Entry ({}) doesn't exists", id); + } + // Return the associated callback + return itr->second->mDefault; + } + + /* -------------------------------------------------------------------------------------------- + * Update the hash of the specified entry. + */ + void UpdateEntryHash(SQInteger id, size_t hash) + { + auto itr = m_Entries.find(PvIdentity(id)); + // Only update if it exists + if (itr != m_Entries.end()) + { + itr->first.mHash = hash; + } + } + + /* -------------------------------------------------------------------------------------------- + * Update the hash of the specified class. + */ + void UpdateClassHash(SQInteger id, size_t hash) + { + auto itr = m_Classes.find(PvIdentity(id)); + // Only update if it exists + if (itr != m_Classes.end()) + { + itr->first.mHash = hash; + } + } + + /* -------------------------------------------------------------------------------------------- + * Update the hash of the specified unit. + */ + void UpdateUnitHash(SQInteger id, size_t hash) + { + auto itr = m_Units.find(PvIdentity(id)); + // Only update if it exists + if (itr != m_Units.end()) + { + itr->first.mHash = hash; + } + } + + /* -------------------------------------------------------------------------------------------- + * Create a entry unit. It throws an error if it already exists. + */ + LightObj CreateEntry(SQInteger id, StackStrF & name); + + /* -------------------------------------------------------------------------------------------- + * Create a class unit. It throws an error if it already exists. + */ + LightObj CreateClass(SQInteger id, StackStrF & name); + + /* -------------------------------------------------------------------------------------------- + * Create a unit unit. It throws an error if it already exists. + */ + LightObj CreateUnit(SQInteger id, const SqPvClass & cls, StackStrF & name); + + /* -------------------------------------------------------------------------------------------- + * Performs the necessary checks to see if privileges were gained or lost when changing classes. + */ + void PropagateParentAssign(const PvClass & cls, const PvClass::Ref & parent); + void PropagateParentAssign(const PvClass::Ref & cls, const PvClass::Ref & parent) { PropagateParentAssign(*cls, parent); } + + /* -------------------------------------------------------------------------------------------- + * Performs the necessary checks to see if privileges were gained or lost when changing classes. + */ + void PropagateParentChange(const PvClass & cls, const PvClass::Ref & parent); + void PropagateParentChange(const PvClass::Ref & cls, const PvClass::Ref & parent) { PropagateParentChange(*cls, parent); } + + /* -------------------------------------------------------------------------------------------- + * Performs the necessary checks to see if privileges were gained or lost when changing classes. + */ + void PropagateClassChange(const PvUnit & unit, const PvClass::Ref & cls); + void PropagateClassChange(const PvUnit::Ref & unit, const PvClass::Ref & cls) { PropagateClassChange(*unit, cls); } + + /* -------------------------------------------------------------------------------------------- + * See if a entry with a certain identifier inherits this class. + */ + SQMOD_NODISCARD LightObj GetEntryWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a entry with a certain tag inherits this class. + */ + SQMOD_NODISCARD LightObj GetEntryWithTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * See if a entry with a certain identifier inherits this class. + */ + bool HaveEntryWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a entry with a certain tag inherits this class. + */ + bool HaveEntryWithTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * See if a class with a certain identifier inherits this class. + */ + SQMOD_NODISCARD LightObj GetClassWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a class with a certain tag inherits this class. + */ + SQMOD_NODISCARD LightObj GetClassWithTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * See if a class with a certain identifier inherits this class. + */ + bool HaveClassWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a class with a certain tag inherits this class. + */ + bool HaveClassWithTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain identifier inherits this class. + */ + SQMOD_NODISCARD LightObj GetUnitWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain tag inherits this class. + */ + SQMOD_NODISCARD LightObj GetUnitWithTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain identifier inherits this class. + */ + bool HaveUnitWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain tag inherits this class. + */ + bool HaveUnitWithTag(StackStrF & tag); +}; + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Base.cpp b/module/Core/Privilege/Base.cpp new file mode 100644 index 00000000..9962fb5c --- /dev/null +++ b/module/Core/Privilege/Base.cpp @@ -0,0 +1,9 @@ +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege/Base.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Base.hpp b/module/Core/Privilege/Base.hpp new file mode 100644 index 00000000..4dd11a89 --- /dev/null +++ b/module/Core/Privilege/Base.hpp @@ -0,0 +1,105 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" +#include "Core/VecMap.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +struct SqPvUnit; +struct SqPvClass; +struct SqPvEntry; + +// ------------------------------------------------------------------------------------------------ +struct PvUnit; +struct PvClass; +struct PvEntry; +struct PvManager; + +// ------------------------------------------------------------------------------------------------ +typedef VecMap< SQInteger, SQInteger > PvStatusList; + +/* ------------------------------------------------------------------------------------------------ + * Used to represent unique identity for entries, units and classes. +*/ +struct PvIdentity +{ + /* -------------------------------------------------------------------------------------------- + * Unique identifier. This must be unique for amongst similar identities. + */ + SQInteger mID{0}; + + /* -------------------------------------------------------------------------------------------- + * Name hash. Optional cached value used when searching by name. + */ + size_t mHash{0}; + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + constexpr PvIdentity() noexcept = default; + + /* ------------------------------------------------------------------------------------------- + * Identifier constructor. + */ + constexpr explicit PvIdentity(SQInteger id) noexcept + : mID(id), mHash(0) + { + } + + /* ------------------------------------------------------------------------------------------- + * Identifier and hash constructor. + */ + constexpr explicit PvIdentity(SQInteger id, size_t h) noexcept + : mID(id), mHash(h) + { + } + + /* ------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + PvIdentity(const PvIdentity & o) noexcept = default; + + /* ------------------------------------------------------------------------------------------- + * Move constructor (disabled). + */ + PvIdentity(PvIdentity && o) noexcept = default; + + /* ------------------------------------------------------------------------------------------- + * Destructor. + */ + ~PvIdentity() = default; + + /* ------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + PvIdentity & operator = (const PvIdentity & o) noexcept = default; + + /* ------------------------------------------------------------------------------------------- + * Move assignment operator (disabled). + */ + PvIdentity & operator = (PvIdentity && o) noexcept = default; +}; + +/* ------------------------------------------------------------------------------------------------ + * Used as for VecMap to know when two elements are considered equal (the same). +*/ +struct PvIdPred +{ + /* -------------------------------------------------------------------------------------------- + * Function call operator. Takes two identities and compares the `mID` member. + */ + bool operator() (const PvIdentity & a, const PvIdentity & b) const noexcept + { + return (a.mID == b.mID); + } +}; + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Class.cpp b/module/Core/Privilege/Class.cpp new file mode 100644 index 00000000..58f8b381 --- /dev/null +++ b/module/Core/Privilege/Class.cpp @@ -0,0 +1,404 @@ +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege/Class.hpp" +#include "Core/Privilege.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(ClassTn, _SC("SqPrivilegeClass")) + +// ------------------------------------------------------------------------------------------------ +void PvClass::SetTag(StackStrF & tag) +{ + mTag = std::move(tag); + // Hash the name and cache it (the name hash is cached in .mRes member variable) + mTag.CacheHash(); + // Propagate this change to the manager as well + if (mManager) + { + mManager->UpdateClassHash(mID, static_cast< size_t >(mTag.mRes)); + } +} + +// ------------------------------------------------------------------------------------------------ +void PvClass::Release() +{ + mOnQuery.Release(); + mOnGained.Release(); + mOnLost.Release(); + mTag.Release(); + mData.Release(); +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD PvManager & PvClass::ValidManager() const +{ + ValidateManager(); + // Return a reference it + return *mManager; +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD const Function & PvClass::GetOnQuery(SQInteger id) const +{ + // Should we go for the one in the manager? + if (mOnQuery.IsNull()) + { + return ValidManager().GetOnQuery(id); + } + // We're using our own + return mOnQuery; +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD const Function & PvClass::GetOnGained(SQInteger id) const +{ + // Should we go for the one in the manager? + if (mOnGained.IsNull()) + { + return ValidManager().GetOnGained(id); + } + // We're using our own + return mOnGained; +} + +// ------------------------------------------------------------------------------------------------ +SQMOD_NODISCARD const Function & PvClass::GetOnLost(SQInteger id) const +{ + // Should we go for the one in the manager? + if (mOnLost.IsNull()) + { + return ValidManager().GetOnLost(id); + } + // We're using our own + return mOnLost; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger PvClass::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()) + { + // Should we go for the default one? + if (mParent.expired()) + { + return ValidManager().GetEntryValue(id); + } + // We have a valid parent + return ValidParent().GetEntryValue(id); + } + // Return the associated value + return itr->second; +} + +// ------------------------------------------------------------------------------------------------ +SQInteger PvClass::GetInheritedEntryValue(SQInteger id) const +{ + // Should we go for the default one? + if (mParent.expired()) + { + return ValidManager().GetEntryValue(id); + } + // We have a valid parent + return ValidParent().GetEntryValue(id); +} + +// ------------------------------------------------------------------------------------------------ +void PvClass::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 PvClass::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 PvClass::DoChanged(SQInteger id, bool status, SQInteger value) const +{ + // Was this considered an upgrade? + if (status) + { + DoGained(id, value); + } + else + { + DoLost(id, value); + } +} + +// ------------------------------------------------------------------------------------------------ +void PvClass::AssignStatus(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 waiy, we are setting this value + mPrivileges[id] = value; +} + +// ------------------------------------------------------------------------------------------------ +void PvClass::RemoveStatus(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 PvClass::ModifyStatus(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 PvClass::AssignParent(const Ref & parent) +{ + // Do we have a parent? + if (mParent.expired()) + { + // Assign the specified class + mParent = parent; + // Propagate changes + ValidManager().PropagateParentAssign(*this, parent); + } + // Are they the same? + else if (mParent.lock() != parent) + { + // Assign the specified class + mParent = parent; + // Propagate changes + ValidManager().PropagateParentChange(*this, parent); + } +} + +// ------------------------------------------------------------------------------------------------ +bool PvClass::Can(SQInteger id) const +{ + // Retrieve the function responsible for the query event + const Function & query = GetOnQuery(id); + // Is there someone that can arbitrate this request? + if (!query.IsNull()) + { + // Get the current status of the specified entry + SQInteger current = GetEntryValue(id); + // Attempt arbitration + LightObj r = query.Eval(current); + // If NULL or false the request was denied + if (!r.IsNull() && r.Cast< bool >()) + { + return true; // Request allowed + } + } + // Request failed, no arbitration + return false; +} + +// ------------------------------------------------------------------------------------------------ +LightObj PvClass::GetUnitWithID(SQInteger id) +{ + for (const auto & u : mUnits) + { + if (u.first.mID == id) + { + return LightObj(SqTypeIdentity< SqPvUnit >{}, SqVM(), u.second); + } + } + // Not found + STHROWF("Unit ({}) does not inherit from this class", id); + SQ_UNREACHABLE +} + +// ------------------------------------------------------------------------------------------------ +LightObj PvClass::GetUnitWithTag(StackStrF & tag) +{ + const size_t h = tag.ToHash(); + for (const auto & u : mUnits) + { + if (u.first.mHash == h) + { + return LightObj(SqTypeIdentity< SqPvUnit >{}, SqVM(), u.second); + } + } + // Not found + STHROWF("Unit ({}) does not inherit from this class", tag.mPtr); + SQ_UNREACHABLE +} + +// ------------------------------------------------------------------------------------------------ +bool PvClass::HaveUnitWithID(SQInteger id) +{ + for (const auto & u : mUnits) // NOLINT(readability-use-anyofallof) + { + if (u.first.mID == id) + { + return true; + } + } + // Not found + return false; +} + +// ------------------------------------------------------------------------------------------------ +bool PvClass::HaveUnitWithTag(StackStrF & tag) +{ + const size_t h = tag.ToHash(); + for (const auto & u : mUnits) // NOLINT(readability-use-anyofallof) + { + if (u.first.mHash == h) + { + return true; + } + } + // Not found + return false; +} + +// ================================================================================================ +bool SqPvClass::Can(LightObj & obj) const +{ + // Entry ID? + if (obj.GetType() == OT_INTEGER) + { + return Valid().Can(obj.Cast< SQInteger >()); + } + // 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 + PvClass & c = Valid(); + // Generate and cache the hash + tag.CacheHash(); + // Forward request + return c.Can(c.ValidManager().GetValidEntryWithTag(tag)->mID); + } + // Entry instance? + else if (obj.GetType() == OT_INSTANCE && obj.GetTypeTag() == StaticClassTypeTag< SqPvEntry >::Get()) + { + return Valid().Can(obj.CastI< SqPvEntry >()->Valid().mID); + } + STHROWF("Unknown or unsupported entry identification type (%s)", SqTypeName(obj.GetType())); + SQ_UNREACHABLE +} + +// ================================================================================================ +void Register_Privilege_Class(HSQUIRRELVM vm, Table & ns) +{ + ns.Bind(_SC("Class"), + Class< SqPvClass, NoConstructor< SqPvClass > >(vm, ClassTn::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &ClassTn::Fn) + .Func(_SC("_tostring"), &SqPvClass::ToString) + // Core Properties + .Prop(_SC("ID"), &SqPvClass::GetID) + .Prop(_SC("Tag"), &SqPvClass::GetTag, &SqPvClass::SetTag) + .Prop(_SC("Data"), &SqPvClass::GetData, &SqPvClass::SetData) + .Prop(_SC("Parent"), &SqPvClass::GetParent, &SqPvClass::SetParent) + .Prop(_SC("Manager"), &SqPvClass::GetManager) + // Core Methods + .FmtFunc(_SC("SetTag"), &SqPvClass::ApplyTag) + .CbFunc(_SC("OnQuery"), &SqPvClass::SetOnQuery) + .CbFunc(_SC("OnLost"), &SqPvClass::SetOnLost) + .CbFunc(_SC("OnGained"), &SqPvClass::SetOnGained) + // Member Methods + .Func(_SC("Can"), &SqPvUnit::Can) + .Func(_SC("GetUnit"), &SqPvClass::GetUnitWithID) + .FmtFunc(_SC("GetUnitWithTag"), &SqPvClass::GetUnitWithTag) + .Func(_SC("HaveUnit"), &SqPvClass::HaveUnitWithID) + .FmtFunc(_SC("HaveUnitWithTag"), &SqPvClass::HaveUnitWithTag) + // Member Overloads + ); +} + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Class.hpp b/module/Core/Privilege/Class.hpp new file mode 100644 index 00000000..650cd443 --- /dev/null +++ b/module/Core/Privilege/Class.hpp @@ -0,0 +1,335 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege/Unit.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * An individual class/group that can optionally inherit the privileges of another and can + * change their status to affect their children but not their parent class. + * A class can extend it's privileges to units and/or other classes. + * These cost less to query but use more memory because they're fewer and they preallocate it. + * Each class must have a unique numerical identifier within their associated manager. +*/ +struct PvClass +{ + /* -------------------------------------------------------------------------------------------- + * Strong and weak reference types. + */ + typedef std::shared_ptr< PvClass > Ref; + typedef std::weak_ptr< PvClass > Ptr; + + /* -------------------------------------------------------------------------------------------- + * Type of container used to store privilege classes. + */ + typedef VecMap< PvIdentity, Ref, PvIdPred > List; + + /* -------------------------------------------------------------------------------------------- + * User identifier associated with this class instance. Always unique in the same manager! + */ + const SQInteger mID; + + /* -------------------------------------------------------------------------------------------- + * The parent class from which we are inheriting privileges, if any. + */ + Ptr mParent; + + /* -------------------------------------------------------------------------------------------- + * A container with unique privilege status values associated with this class. + */ + PvStatusList mPrivileges; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege query event. + */ + Function mOnQuery; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege gained event. + */ + Function mOnGained; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege lost event. + */ + Function mOnLost; + + /* -------------------------------------------------------------------------------------------- + * Container that stores all the associated units. + */ + PvUnit::List mUnits; + + /* -------------------------------------------------------------------------------------------- + * User tag associated with this instance. + */ + StackStrF mTag; + + /* -------------------------------------------------------------------------------------------- + * User data associated with this instance. + */ + LightObj mData; + + /* -------------------------------------------------------------------------------------------- + * Pointer to the associated manager. Should always be present. + * + */ + PvManager * mManager; + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + PvClass(SQInteger id, PvManager * mgr) + : mID(id) + , mParent() + , mPrivileges() + , mOnQuery(), mOnGained(), mOnLost() + , mUnits() + , mTag(), mData() + , mManager(mgr) + { + } + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + PvClass(SQInteger id, StackStrF && tag, PvManager * mgr) + : mID(id) + , mParent() + , mPrivileges() + , mOnQuery(), mOnGained(), mOnLost() + , mUnits() + , mTag(std::move(tag)), mData() + , mManager(mgr) + { + } + + /* ------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + PvClass(const PvClass & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move constructor (disabled). + */ + PvClass(PvClass && o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Destructor. + */ + ~PvClass() = default; + + /* ------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + PvClass & operator = (const PvClass & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move assignment operator (disabled). + */ + PvClass & operator = (PvClass && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Modify the associated user tag. + */ + void SetTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * Release all script resources. Recursively forward request. + */ + void Release(); + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced parent class is valid. + */ + void ValidateParent() const + { + if (mParent.expired()) + { + STHROWF("Class ({} : {}) has invalid parent class reference", mID, mTag.mPtr); + } + } + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced parent class is valid and return a reference to it. + */ + SQMOD_NODISCARD PvClass & ValidParent() const + { + ValidateParent(); + // Acquire a reference and return it + return *(mParent.lock().get()); + } + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced manager is valid. + */ + void ValidateManager() const + { + if (!mManager) + { + STHROWF("Class ({} : {}) has invalid manager reference", mID, mTag.mPtr); + } + } + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced manger is valid and return a reference to it. + */ + SQMOD_NODISCARD PvManager & ValidManager() const; + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle query events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnQuery(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle gained events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnGained(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle lost events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnLost(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the current value of an entry for this particular class. + */ + SQMOD_NODISCARD SQInteger GetEntryValue(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the inherited value of an entry for this particular class. + */ + SQMOD_NODISCARD SQInteger GetInheritedEntryValue(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Perform the actions necessary to handle a privilege gain event. + */ + void DoGained(SQInteger id, SQInteger value) const; + + /* -------------------------------------------------------------------------------------------- + * Perform the actions necessary to handle a privilege lost event. + */ + void DoLost(SQInteger id, SQInteger value) const; + + /* -------------------------------------------------------------------------------------------- + * Perform the actions necessary to handle a privilege change event. + */ + void DoChanged(SQInteger id, bool status, SQInteger value) const; + + /* -------------------------------------------------------------------------------------------- + * Assign a status value. Does not care if a parent (class or global) has the same status. + * Later if the parent changes this status, we will keep having this status value. + */ + void AssignStatus(SQInteger id, SQInteger value); + + /* -------------------------------------------------------------------------------------------- + * Remove a status value. If the specified status value is not assigned, nothing happens. + */ + void RemoveStatus(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * Assign a status value. If a parent (class or global) has the same value, nothing changes. + * Same as AssignStatus but the status will not be enforced if we have it (inherited or not). + */ + void ModifyStatus(SQInteger id, SQInteger value); + + /* -------------------------------------------------------------------------------------------- + * Change the parent class. + */ + void AssignParent(const Ref & parent); + + /* -------------------------------------------------------------------------------------------- + * Check if this class has a certain privilege. + */ + SQMOD_NODISCARD bool Can(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain identifier inherits this class. + */ + SQMOD_NODISCARD LightObj GetUnitWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain tag inherits this class. + */ + SQMOD_NODISCARD LightObj GetUnitWithTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain identifier inherits this class. + */ + bool HaveUnitWithID(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * See if a unit with a certain tag inherits this class. + */ + bool HaveUnitWithTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * Update the hash of the specified unit. + */ + void UpdateUnitHash(SQInteger id, size_t hash) + { + auto itr = mUnits.find(PvIdentity(id)); + // Only update if it exists + if (itr != mUnits.end()) + { + itr->first.mHash = hash; + } + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Privilege class wrapper. Mostly calls functions of the actual implementation. + * Used to avoid having to involve hard script references that can persist. + * Breaks away from usual formatting. +*/ +struct SqPvClass +{ + PvClass::Ptr mI; + // -------------------------------------------------------------------------------------------- + explicit SqPvClass(PvClass::Ptr i) : mI(std::move(i)) { } // NOLINT(modernize-pass-by-value) + explicit SqPvClass(const PvClass::Ref & i) : mI(i) { } + SqPvClass(const SqPvClass & o) = default; + SqPvClass(SqPvClass && o) noexcept = default; + ~SqPvClass() = default; + SqPvClass & operator = (const SqPvClass & o) = default; + SqPvClass & operator = (SqPvClass && o) noexcept = default; + // -------------------------------------------------------------------------------------------- + void Validate() const { if (mI.expired()) STHROWF("Invalid unit reference"); } + SQMOD_NODISCARD PvClass & Valid() const { Validate(); return *(mI.lock().get()); } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD SQInteger GetID() const { return Valid().mID; } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD auto ToString() const { return Valid().mTag.mObj; } + SQMOD_NODISCARD auto GetTag() const { return Valid().mTag.mObj; } + void SetTag(StackStrF & tag) const { Valid().SetTag(tag); } + SQMOD_NODISCARD SqPvClass & ApplyTag(StackStrF & tag) { SetTag(tag); return *this; } + SQMOD_NODISCARD LightObj & GetData() const { return Valid().mData; } + void SetData(LightObj & data) const { Valid().mData = data; } + // -------------------------------------------------------------------------------------------- + void SetOnQuery(Function & func) const { Valid().mOnQuery = std::move(func); } + void SetOnLost(Function & func) const { Valid().mOnLost = std::move(func); } + void SetOnGained(Function & func) const { Valid().mOnGained = std::move(func); } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD LightObj GetManager() const { return LightObj(Valid().mManager); } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD LightObj GetParent() const + { + return LightObj(SqTypeIdentity< SqPvClass >{}, SqVM(), Valid().mParent); + } + void SetParent(const SqPvClass & cls) const + { + Validate(); + cls.Validate(); + mI.lock()->AssignParent(cls.mI.lock()); + } + // -------------------------------------------------------------------------------------------- + bool Can(LightObj & obj) const; + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD LightObj GetUnitWithID(SQInteger id) const { return Valid().GetUnitWithID(id); } + SQMOD_NODISCARD LightObj GetUnitWithTag(StackStrF & tag) const { return Valid().GetUnitWithTag(tag); } + SQMOD_NODISCARD bool HaveUnitWithID(SQInteger id) const { return Valid().HaveUnitWithID(id); } + SQMOD_NODISCARD bool HaveUnitWithTag(StackStrF & tag) const { return Valid().HaveUnitWithTag(tag); } +}; + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Entry.cpp b/module/Core/Privilege/Entry.cpp new file mode 100644 index 00000000..17eb9615 --- /dev/null +++ b/module/Core/Privilege/Entry.cpp @@ -0,0 +1,66 @@ +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege/Entry.hpp" +#include "Core/Privilege.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(EntryTn, _SC("SqPrivilegeEntry")) + +// ------------------------------------------------------------------------------------------------ +void PvEntry::SetTag(StackStrF & tag) +{ + mTag = std::move(tag); + // Hash the name and cache it (the name hash is cached in .mRes member variable) + mTag.CacheHash(); + // Propagate this change to the manager as well + if (mManager) + { + mManager->UpdateEntryHash(mID, static_cast< size_t >(mTag.mRes)); + } +} + +// ------------------------------------------------------------------------------------------------ +void PvEntry::Release() +{ + mTag.Release(); + mOnQuery.Release(); + mOnModify.Release(); + mOnGained.Release(); + mOnLost.Release(); + mData.Release(); + mBrief.Release(); + mInfo.Release(); +} + +// ================================================================================================ +void Register_Privilege_Entry(HSQUIRRELVM vm, Table & ns) +{ + ns.Bind(_SC("Entry"), + Class< SqPvEntry, NoConstructor< SqPvEntry > >(vm, EntryTn::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &EntryTn::Fn) + .Func(_SC("_tostring"), &SqPvEntry::ToString) + // Core Properties + .Prop(_SC("ID"), &SqPvEntry::GetID) + .Prop(_SC("Tag"), &SqPvEntry::GetTag, &SqPvEntry::SetTag) + .Prop(_SC("Data"), &SqPvEntry::GetData, &SqPvEntry::SetData) + .Prop(_SC("Manager"), &SqPvEntry::GetManager) + .Prop(_SC("Brief"), &SqPvEntry::GetBrief, &SqPvEntry::SetBrief) + .Prop(_SC("Info"), &SqPvEntry::GetInfo, &SqPvEntry::SetInfo) + .Prop(_SC("Default"), &SqPvEntry::GetDefault, &SqPvEntry::SetDefault) + // Core Methods + .FmtFunc(_SC("SetTag"), &SqPvEntry::ApplyTag) + .CbFunc(_SC("OnQuery"), &SqPvEntry::SetOnQuery) + .CbFunc(_SC("OnModify"), &SqPvEntry::SetOnModify) + .CbFunc(_SC("OnLost"), &SqPvEntry::SetOnLost) + .CbFunc(_SC("OnGained"), &SqPvEntry::SetOnGained) + // Member Methods + .FmtFunc(_SC("SetBrief"), &SqPvEntry::ApplyBrief) + .FmtFunc(_SC("SetInfo"), &SqPvEntry::ApplyInfo) + // Raw functions + ); +} + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Entry.hpp b/module/Core/Privilege/Entry.hpp new file mode 100644 index 00000000..64892d53 --- /dev/null +++ b/module/Core/Privilege/Entry.hpp @@ -0,0 +1,188 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege/Base.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * An entry represents a privilege that a class or unit can can have. + * Each entry must have a unique numerical identifier within their associated manager. +*/ +struct PvEntry +{ + /* -------------------------------------------------------------------------------------------- + * Strong and weak reference types. + */ + typedef std::shared_ptr< PvEntry > Ref; + typedef std::weak_ptr< PvEntry > Ptr; + + /* -------------------------------------------------------------------------------------------- + * Type of container used to store privilege entries. + */ + typedef VecMap< PvIdentity, Ref, PvIdPred > List; + + /* -------------------------------------------------------------------------------------------- + * User identifier associated with this entry instance. Always unique in the same manager! + */ + const SQInteger mID; + + /* -------------------------------------------------------------------------------------------- + * User tag associated with this instance. + */ + StackStrF mTag; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege query event. + */ + Function mOnQuery; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege gained event. + */ + Function mOnGained; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege modify event. + */ + Function mOnModify; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege lost event. + */ + Function mOnLost; + + /* -------------------------------------------------------------------------------------------- + * User data associated with this instance. + */ + LightObj mData; + + /* -------------------------------------------------------------------------------------------- + * Brief information about the privilege. + */ + StackStrF mBrief; + + /* -------------------------------------------------------------------------------------------- + * Detailed information about the privilege. + */ + StackStrF mInfo; + + /* -------------------------------------------------------------------------------------------- + * Implicit privilege status value. + */ + SQInteger mDefault; + + /* -------------------------------------------------------------------------------------------- + * Pointer to the associated manager. Should always be present. + */ + PvManager * mManager; + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + PvEntry(SQInteger id, PvManager * mgr) + : mID(id), mTag() + , mOnQuery(), mOnModify(), mOnGained(), mOnLost() + , mData(), mBrief(), mInfo(), mDefault() + , mManager(mgr) + { + } + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + PvEntry(SQInteger id, StackStrF && tag, PvManager * mgr) + : mID(id), mTag(std::move(tag)) + , mOnQuery(), mOnModify(), mOnGained(), mOnLost() + , mData(), mBrief(), mInfo(), mDefault() + , mManager(mgr) + + { + } + + /* ------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + PvEntry(const PvEntry & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move constructor (disabled). + */ + PvEntry(PvEntry && o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Destructor. + */ + ~PvEntry() = default; + + /* ------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + PvEntry & operator = (const PvEntry & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move assignment operator (disabled). + */ + PvEntry & operator = (PvEntry && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Modify the associated user tag. + */ + void SetTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * Release all script resources. + */ + void Release(); +}; + +/* ------------------------------------------------------------------------------------------------ + * Privilege entry wrapper. Mostly calls functions of the actual implementation. + * Used to avoid having to involve hard script references that can persist. + * Breaks away from usual formatting. +*/ +struct SqPvEntry +{ + PvEntry::Ptr mI; + // -------------------------------------------------------------------------------------------- + explicit SqPvEntry(PvEntry::Ptr i) : mI(std::move(i)) { } // NOLINT(modernize-pass-by-value) + explicit SqPvEntry(const PvEntry::Ref & i) : mI(i) { } + SqPvEntry(const SqPvEntry & o) = default; + SqPvEntry(SqPvEntry && o) noexcept = default; + ~SqPvEntry() = default; + SqPvEntry & operator = (const SqPvEntry & o) = default; + SqPvEntry & operator = (SqPvEntry && o) noexcept = default; + // -------------------------------------------------------------------------------------------- + void Validate() const { if (mI.expired()) STHROWF("Invalid unit reference"); } + SQMOD_NODISCARD PvEntry & Valid() const { Validate(); return *(mI.lock().get()); } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD SQInteger GetID() const { return Valid().mID; } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD auto ToString() const { return Valid().mTag.mObj; } + SQMOD_NODISCARD auto GetTag() const { return Valid().mTag.mObj; } + void SetTag(StackStrF & tag) const { Valid().SetTag(tag); } + SQMOD_NODISCARD SqPvEntry & ApplyTag(StackStrF & tag) { SetTag(tag); return *this; } + SQMOD_NODISCARD LightObj & GetData() const { return Valid().mData; } + void SetData(LightObj & data) const { Valid().mData = data; } + // -------------------------------------------------------------------------------------------- + void SetOnQuery(Function & func) const { Valid().mOnQuery = std::move(func); } + void SetOnModify(Function & func) const { Valid().mOnModify = std::move(func); } + void SetOnLost(Function & func) const { Valid().mOnLost = std::move(func); } + void SetOnGained(Function & func) const { Valid().mOnGained = std::move(func); } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD auto GetBrief() const { return Valid().mBrief.mObj; } + void SetBrief(StackStrF & str) const { Valid().mBrief = std::move(str); } + SQMOD_NODISCARD SqPvEntry & ApplyBrief(StackStrF & str) { SetBrief(str); return *this; } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD auto GetInfo() const { return Valid().mInfo.mObj; } + void SetInfo(StackStrF & str) const { Valid().mInfo = std::move(str); } + SQMOD_NODISCARD SqPvEntry & ApplyInfo(StackStrF & str) { SetInfo(str); return *this; } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD SQInteger GetDefault() const { return Valid().mDefault; } + void SetDefault(SQInteger value) const { Valid().mDefault = value; } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD LightObj GetManager() const { return LightObj(Valid().mManager); } +}; + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Unit.cpp b/module/Core/Privilege/Unit.cpp new file mode 100644 index 00000000..4f8fa586 --- /dev/null +++ b/module/Core/Privilege/Unit.cpp @@ -0,0 +1,370 @@ +// ------------------------------------------------------------------------------------------------ +#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::AssignStatus(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::RemoveStatus(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::ModifyStatus(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::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) const +{ + // Retrieve the function responsible for the query event + const Function & query = GetOnQuery(id); + // Is there someone that can arbitrate this request? + if (!query.IsNull()) + { + // Get the current status of the specified entry + SQInteger current = GetEntryValue(id); + // Attempt arbitration + LightObj r = query.Eval(current); + // If NULL or false the request was denied + if (!r.IsNull() && r.Cast< bool >()) + { + return true; // Request allowed + } + } + // Request failed, no arbitration + return false; +} + +// ================================================================================================ +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) const +{ + // Entry ID? + if (obj.GetType() == OT_INTEGER) + { + return Valid().Can(obj.Cast< SQInteger >()); + } + // 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); + } + // Entry instance? + else if (obj.GetType() == OT_INSTANCE && obj.GetTypeTag() == StaticClassTypeTag< SqPvEntry >::Get()) + { + return Valid().Can(obj.CastI< SqPvEntry >()->Valid().mID); + } + 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) + ); +} + +} // Namespace:: SqMod diff --git a/module/Core/Privilege/Unit.hpp b/module/Core/Privilege/Unit.hpp new file mode 100644 index 00000000..970f7b87 --- /dev/null +++ b/module/Core/Privilege/Unit.hpp @@ -0,0 +1,281 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Privilege/Base.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * An individual unit/entity that inherits the privileges of a class/group and can + * change their status without affecting other units and/or their associated class. + * A unit cannot extend it's privileges to other entities. + * Units cost more to query if they differ from their class but save memory in large numbers. + * Each unit must have a unique numerical identifier within their associated manager. +*/ +struct PvUnit +{ + /* -------------------------------------------------------------------------------------------- + * Strong and weak reference types. + */ + typedef std::shared_ptr< PvUnit > Ref; + typedef std::weak_ptr< PvUnit > Ptr; + + /* -------------------------------------------------------------------------------------------- + * Type of container used to store privilege units. + */ + typedef VecMap< PvIdentity, Ref, PvIdPred > List; + + /* -------------------------------------------------------------------------------------------- + * User identifier associated with this unit instance. Always unique in the same manager! + */ + const SQInteger mID; + + /* -------------------------------------------------------------------------------------------- + * Authority level associated with a particular unit. + */ + SQInteger mAuthority; + + /* -------------------------------------------------------------------------------------------- + * A container with unique privilege status values associated with this unit. + */ + PvStatusList mPrivileges; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege query event. + */ + Function mOnQuery; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege gained event. + */ + Function mOnGained; + + /* -------------------------------------------------------------------------------------------- + * Dedicated callback for privilege lost event. + */ + Function mOnLost; + + /* -------------------------------------------------------------------------------------------- + * User tag associated with this instance. + */ + StackStrF mTag; + + /* -------------------------------------------------------------------------------------------- + * User data associated with this instance. + */ + LightObj mData; + + /* -------------------------------------------------------------------------------------------- + * Weak pointer to the associated class. Units always have an associated class! + */ + std::weak_ptr< PvClass > mClass; + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + PvUnit(SQInteger id, std::weak_ptr< PvClass > cls) + : mID(id) + , mAuthority(0) + , mPrivileges() + , mOnQuery(), mOnGained(), mOnLost() + , mTag(), mData() + , mClass(std::move(cls)) + { + } + + /* ------------------------------------------------------------------------------------------- + * Default constructor. + */ + PvUnit(SQInteger id, StackStrF && tag, std::weak_ptr< PvClass > cls) + : mID(id) + , mAuthority(0) + , mPrivileges() + , mOnQuery(), mOnGained(), mOnLost() + , mTag(std::move(tag)), mData() + , mClass(std::move(cls)) + { + } + + /* ------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + PvUnit(const PvUnit & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move constructor (disabled). + */ + PvUnit(PvUnit && o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Destructor. + */ + ~PvUnit() = default; + + /* ------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + PvUnit & operator = (const PvUnit & o) = delete; + + /* ------------------------------------------------------------------------------------------- + * Move assignment operator (disabled). + */ + PvUnit & operator = (PvUnit && o) = delete; + + /* -------------------------------------------------------------------------------------------- + * Modify the associated user tag. + */ + void SetTag(StackStrF & tag); + + /* -------------------------------------------------------------------------------------------- + * Release all script resources. + */ + void Release(); + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced parent class is valid. + */ + void ValidateClass() const + { + if (mClass.expired()) + { + STHROWF("Unit ({} : {}) has invalid class reference", mID, mTag.mPtr); + } + } + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced parent class is valid and return a reference to it. + */ + SQMOD_NODISCARD PvClass & ValidClass() const + { + ValidateClass(); + // Acquire a reference and return it + return *(mClass.lock().get()); + } + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced manger is valid. + */ + void ValidateManager() const; + + /* -------------------------------------------------------------------------------------------- + * Make sure the referenced manger is valid and return a reference to it. + */ + SQMOD_NODISCARD PvManager & ValidManager() const; + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle query events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnQuery(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle gained events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnGained(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Find out the callback that must be invoked to handle lost events for a certain entry. + */ + SQMOD_NODISCARD const Function & GetOnLost(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the current value of an entry for this particular unit. + */ + SQMOD_NODISCARD SQInteger GetEntryValue(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Retrieve the inherited value of an entry for this particular unit. + */ + SQMOD_NODISCARD SQInteger GetInheritedEntryValue(SQInteger id) const; + + /* -------------------------------------------------------------------------------------------- + * Perform the actions necessary to handle a privilege gain event. + */ + void DoGained(SQInteger id, SQInteger value) const; + + /* -------------------------------------------------------------------------------------------- + * Perform the actions necessary to handle a privilege lost event. + */ + void DoLost(SQInteger id, SQInteger value) const; + + /* -------------------------------------------------------------------------------------------- + * Perform the actions necessary to handle a privilege change event. + */ + void DoChanged(SQInteger id, bool status, SQInteger value) const; + + /* -------------------------------------------------------------------------------------------- + * Assign a status value. Does not care if a parent (class or global) has the same status. + * Later if the parent changes this status, we will keep having this status value. + */ + void AssignStatus(SQInteger id, SQInteger value); + + /* -------------------------------------------------------------------------------------------- + * Remove a status value. If the specified status value is not assigned, nothing happens. + */ + void RemoveStatus(SQInteger id); + + /* -------------------------------------------------------------------------------------------- + * Assign a status value. If a parent (class or global) has the same value, nothing changes. + * Same as AssignStatus but the status will not be enforced if we have it (inherited or not). + */ + void ModifyStatus(SQInteger id, SQInteger value); + + /* -------------------------------------------------------------------------------------------- + * Assign a new class. + */ + void AssignClass(const std::shared_ptr< PvClass > & cls); + + /* -------------------------------------------------------------------------------------------- + * Check if this unit has a certain privilege. + */ + SQMOD_NODISCARD bool Can(SQInteger id) const; +}; + +/* ------------------------------------------------------------------------------------------------ + * Privilege unit wrapper. Mostly calls functions of the actual implementation. + * Used to avoid having to involve hard script references that can persist. + * Breaks away from usual formatting. +*/ +struct SqPvUnit +{ + PvUnit::Ptr mI; + // -------------------------------------------------------------------------------------------- + explicit SqPvUnit(PvUnit::Ptr i) : mI(std::move(i)) { } // NOLINT(modernize-pass-by-value) + explicit SqPvUnit(const PvUnit::Ref & i) : mI(i) { } + SqPvUnit(const SqPvUnit & o) = default; + SqPvUnit(SqPvUnit && o) noexcept = default; + ~SqPvUnit() = default; + SqPvUnit & operator = (const SqPvUnit & o) = default; + SqPvUnit & operator = (SqPvUnit && o) noexcept = default; + // -------------------------------------------------------------------------------------------- + void Validate() const { if (mI.expired()) STHROWF("Invalid unit reference"); } + SQMOD_NODISCARD PvUnit & Valid() const { Validate(); return *(mI.lock().get()); } + // -------------------------------------------------------------------------------------------- + void ValidateCls() const { Validate(); if (mI.lock()->mClass.expired()) STHROWF("Invalid class reference"); } + SQMOD_NODISCARD PvClass & ValidCls() const { ValidateCls(); return *(mI.lock()->mClass.lock().get()); } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD SQInteger GetID() const { return Valid().mID; } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD auto ToString() const { return Valid().mTag.mObj; } + SQMOD_NODISCARD auto GetTag() const { return Valid().mTag.mObj; } + void SetTag(StackStrF & tag) const { Valid().SetTag(tag); } + SQMOD_NODISCARD SqPvUnit & ApplyTag(StackStrF & tag) { SetTag(tag); return *this; } + SQMOD_NODISCARD LightObj & GetData() const { return Valid().mData; } + void SetData(LightObj & data) const { Valid().mData = data; } + // -------------------------------------------------------------------------------------------- + void SetOnQuery(Function & func) const { Valid().mOnQuery = std::move(func); } + void SetOnLost(Function & func) const { Valid().mOnLost = std::move(func); } + void SetOnGained(Function & func) const { Valid().mOnGained = std::move(func); } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD SQInteger GetAuthority() const { return Valid().mAuthority; } + void SetAuthority(SQInteger a) const { Valid().mAuthority = a; } + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD LightObj GetClass() const; + void SetClass(const SqPvClass & cls) const; + // -------------------------------------------------------------------------------------------- + SQMOD_NODISCARD LightObj GetManager() const; + // -------------------------------------------------------------------------------------------- + bool Can(LightObj & obj) const; +}; + +} // Namespace:: SqMod diff --git a/module/Core/VecMap.hpp b/module/Core/VecMap.hpp new file mode 100644 index 00000000..eedfeaaf --- /dev/null +++ b/module/Core/VecMap.hpp @@ -0,0 +1,258 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "SqBase.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include +#include +#include +#include + +/* ------------------------------------------------------------------------------------------------ + * Hybrid associative container combining the performance of a vector and usefulness of a map container. + * This class works under the assumption that you are not stupid enough to modify the keys yourself. + * It does not try to replace std::map but rather provide a few helper methods to avoid duplicate code. +*/ +template < class Key, class T, class Pred = std::equal_to< Key > > struct VecMap +{ + // -------------------------------------------------------------------------------------------- + using key_type = Key; + using mapped_type = T; + using value_type = std::pair< Key, T >; + using storage_type = std::vector< value_type >; + using key_equal = Pred; + using pointer = typename storage_type::pointer; + using const_pointer = typename storage_type::const_pointer; + using reference = typename storage_type::reference; + using const_reference = typename storage_type::const_reference; + using size_type = typename storage_type::size_type; + using difference_type = typename storage_type::difference_type; + using iterator = typename storage_type::iterator; + using const_iterator = typename storage_type::const_iterator; + using insert_return_type = std::pair< iterator, pointer >; + + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + VecMap() noexcept(noexcept(storage_type())) = default; + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. Does exactly what you expect it to do. + */ + VecMap(const VecMap & o) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. Does exactly what you expect it to do. + */ + VecMap(VecMap && o) noexcept = default; + + /* -------------------------------------------------------------------------------------------- + * Sub-script operator. + */ + mapped_type & operator [] (const key_type & key) + { + Pred p; + for (auto & e : m_Storage) + { + if (p(e.first, key)) return e.second; + } + m_Storage.emplace_back(key, mapped_type{}); + return m_Storage.back().second; + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve an iterator to the beginning. See: std::vector::begin() + */ + iterator begin() noexcept { return m_Storage.begin(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve an iterator to the beginning (const). See: std::vector::[c]begin() + */ + const_iterator begin() const noexcept { return m_Storage.begin(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve an iterator to the beginning (const). See: std::vector::cbegin() + */ + const_iterator cbegin() const noexcept { return m_Storage.cbegin(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve an iterator to the beginning. See: std::vector::end() + */ + iterator end() noexcept { return m_Storage.end(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve an iterator to the beginning (const). See: std::vector::[c]end() + */ + const_iterator end() const noexcept { return m_Storage.end(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve an iterator to the beginning (const). See: std::vector::cend() + */ + const_iterator cend() const noexcept { return m_Storage.cend(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve a reverse iterator to the beginning. See: std::vector::rbegin() + */ + iterator rbegin() noexcept { return m_Storage.rbegin(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve a reverse iterator to the beginning (const). See: std::vector::[c]rbegin() + */ + const_iterator rbegin() const noexcept { return m_Storage.rbegin(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve a reverse iterator to the beginning (const). See: std::vector::crbegin() + */ + const_iterator crbegin() const noexcept { return m_Storage.crbegin(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve a reverse iterator to the beginning. See: std::vector::rend() + */ + iterator rend() noexcept { return m_Storage.rend(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve a reverse iterator to the beginning (const). See: std::vector::[c]rend() + */ + const_iterator rend() const noexcept { return m_Storage.rend(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve a reverse iterator to the beginning (const). See: std::vector::crend() + */ + const_iterator crend() const noexcept { return m_Storage.crend(); } + + /* -------------------------------------------------------------------------------------------- + * Check if elements are stored in the container. + */ + SQMOD_NODISCARD bool empty() const noexcept { return m_Storage.empty(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements stored in the container. + */ + size_type size() const noexcept { return m_Storage.size(); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements that can be stored in the container. + */ + size_type max_size() const noexcept { return m_Storage.max_size(); } + + /* -------------------------------------------------------------------------------------------- + * Reserve space for a specific amount of elements. + */ + void reserve(size_type n) { m_Storage.reserve(n); } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements that can be held in currently allocated storage. + */ + size_type capacity() const noexcept { return m_Storage.capacity(); } + + /* -------------------------------------------------------------------------------------------- + * Reduce memory usage by freeing unused memory. + */ + void conform() { m_Storage.shrink_to_fit(); } + + /* -------------------------------------------------------------------------------------------- + * Discard all stored elements. + */ + void clear() noexcept { m_Storage.clear(); } + + /* -------------------------------------------------------------------------------------------- + * Locate a an element with a specific key and obtain an iterator to it's location. + */ + iterator find(const key_type & key) noexcept + { + return std::find_if(m_Storage.begin(), m_Storage.end(), + [&, p = Pred()](reference e) -> bool { return p(e.first, key); }); + } + + /* -------------------------------------------------------------------------------------------- + * Locate a an element with a specific key and obtain an iterator to it's location. + */ + const_iterator find(const key_type & key) const noexcept + { + return std::find_if(m_Storage.cbegin(), m_Storage.cend(), + [&, p = Pred()](const_reference e) -> bool { return p(e.first, key); }); + } + + /* -------------------------------------------------------------------------------------------- + * Check if an element with a specific key exists in the container. + */ + bool exists(const key_type & key) const noexcept { return find(key) != m_Storage.cend(); } + + /* -------------------------------------------------------------------------------------------- + * Append a new element to the end of the container. + */ + template< class... Args > mapped_type & try_emplace(const key_type & k, Args&&... args) + { + auto itr = find(k); + if (itr != m_Storage.end()) + { + itr->second = mapped_type(std::forward< Args >(args)...); + return *itr; + } + m_Storage.emplace_back(k, std::forward< Args >(args)...); + return m_Storage.back(); + } + + /* -------------------------------------------------------------------------------------------- + * Append a new element to the end of the container. + */ + template< class... Args > mapped_type & try_emplace(key_type && k, Args&&... args) + { + auto itr = find(k); + if (itr != m_Storage.end()) + { + itr->second = mapped_type(std::forward< Args >(args)...); + return *itr; + } + m_Storage.emplace_back(std::move(k), std::forward< Args >(args)...); + return m_Storage.back(); + } + + /* -------------------------------------------------------------------------------------------- + * Remove the last element of the container. + */ + void pop_back() { m_Storage.pop_back(); } + + /* -------------------------------------------------------------------------------------------- + * Removes specified element from the container. Returns true if found and removed, false otherwise. + */ + bool erase(const key_type & key) + { + auto itr = find(key); + if (itr != m_Storage.end()) + { + m_Storage.erase(itr); + return true; + } + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Removes specified element from the container. Returns iterator to the next element. + */ + iterator erase(iterator pos) { return m_Storage.erase(pos); } + + /* -------------------------------------------------------------------------------------------- + * Removes specified element from the container. Returns iterator to the next element. + */ + iterator erase(const_iterator pos) { return m_Storage.erase(pos); } + + /* -------------------------------------------------------------------------------------------- + * Append a new element to the end of the container. + * Available for internal use when the search was already performed. + */ + template< class... Args > mapped_type & emplace_back( Args&&... args ) + { + m_Storage.emplace_back(std::forward< Args >(args)...); + return m_Storage.back().second; + } + +private: + + /* -------------------------------------------------------------------------------------------- + * Internal container used to store elements. + */ + storage_type m_Storage; +}; diff --git a/module/Register.cpp b/module/Register.cpp index bf03769c..9f941f79 100644 --- a/module/Register.cpp +++ b/module/Register.cpp @@ -94,7 +94,7 @@ bool RegisterAPI(HSQUIRRELVM vm) Register_Log(vm); Register_Core(vm); Register_Command(vm); - //Register_Privilege(vm); + Register_Privilege(vm); Register_Routine(vm); Register_Tasks(vm);