Skip to content

Commit

Permalink
Change how data pushed to lua is validated to use weak_ptr (#469)
Browse files Browse the repository at this point in the history
- Makes use of TrinityCore/TrinityCore@a79b42b
- Removed additional indirection when pushing 64 bit integers and ObjectGuids
- This allows storing direct object references in scripts

Other cores will use the old validation scheme until they have similar implementations for weak pointers in place.
  • Loading branch information
Shauren authored Jul 13, 2024
1 parent 1c5a94d commit f4a1203
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 20 deletions.
82 changes: 72 additions & 10 deletions ElunaTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ extern "C"
#include "Globals/SharedDefines.h"
#endif


#ifdef TRINITY
#include "UniqueTrackablePtr.h"

#define TRACKABLE_PTR_NAMESPACE ::Trinity::
#endif

class ElunaGlobal
{
public:
Expand Down Expand Up @@ -108,47 +115,101 @@ class ElunaObject
}

// Get wrapped object pointer
virtual void* GetObj() const = 0;
// Returns whether the object is valid or not
virtual bool IsValid() const = 0;
virtual void* GetObjIfValid() const = 0;
// Returns pointer to the wrapped object's type name
const char* GetTypeName() const { return type_name; }
#ifndef TRINITY
// Invalidates the pointer if it should be invalidated
virtual void Invalidate() = 0;
#endif

protected:
Eluna* E;
const char* type_name;
};

#ifdef TRINITY
template <typename T>
struct ElunaConstrainedObjectRef
{
TRACKABLE_PTR_NAMESPACE unique_weak_ptr<T> Obj;
Map const* BoundMap = nullptr;
};

ElunaConstrainedObjectRef<Aura> GetWeakPtrFor(Aura const* obj);
ElunaConstrainedObjectRef<Battleground> GetWeakPtrFor(Battleground const* obj);
ElunaConstrainedObjectRef<Group> GetWeakPtrFor(Group const* obj);
ElunaConstrainedObjectRef<Guild> GetWeakPtrFor(Guild const* obj);
ElunaConstrainedObjectRef<Map> GetWeakPtrFor(Map const* obj);
ElunaConstrainedObjectRef<Object> GetWeakPtrForObjectImpl(Object const* obj);
ElunaConstrainedObjectRef<Quest> GetWeakPtrFor(Quest const* obj);
ElunaConstrainedObjectRef<Spell> GetWeakPtrFor(Spell const* obj);
ElunaConstrainedObjectRef<Vehicle> GetWeakPtrFor(Vehicle const* obj);

template <typename T>
ElunaConstrainedObjectRef<T> GetWeakPtrFor(T const* obj)
{
ElunaConstrainedObjectRef<Object> ref = GetWeakPtrForObjectImpl(obj);
return { TRACKABLE_PTR_NAMESPACE static_pointer_cast<T>(ref.Obj), ref.BoundMap };
}

#endif

template <typename T>
class ElunaObjectImpl : public ElunaObject
{
public:
#ifdef TRINITY
ElunaObjectImpl(Eluna* E, T const* obj, char const* tname) : ElunaObject(E, tname), _obj(GetWeakPtrFor(obj))
{
}

void* GetObjIfValid() const override
{
if (TRACKABLE_PTR_NAMESPACE unique_strong_ref_ptr<T> obj = _obj.Obj.lock())
if (!E->GetBoundMap() || !_obj.BoundMap || E->GetBoundMap() == _obj.BoundMap)
return obj.get();

return nullptr;
}
#else
ElunaObjectImpl(Eluna* E, T* obj, char const* tname) : ElunaObject(E, tname), _obj(obj), callstackid(E->GetCallstackId())
{
}

void* GetObj() const override { return _obj; }
bool IsValid() const override { return callstackid == E->GetCallstackId(); }
void* GetObjIfValid() const override
{
if (callstackid == E->GetCallstackId())
return _obj;

return nullptr;
}

void Invalidate() override { callstackid = 1; }
#endif

private:
#ifdef TRINITY
ElunaConstrainedObjectRef<T> _obj;
#else
void* _obj;
uint64 callstackid;
#endif
};

template <typename T>
class ElunaObjectValueImpl : public ElunaObject
{
public:
ElunaObjectValueImpl(Eluna* E, T* obj, char const* tname) : ElunaObject(E, tname), _obj(*obj /*always a copy, what gets passed here might be pointing to something not owned by us*/)
ElunaObjectValueImpl(Eluna* E, T const* obj, char const* tname) : ElunaObject(E, tname), _obj(*obj /*always a copy, what gets passed here might be pointing to something not owned by us*/)
{
}

void* GetObj() const override { return const_cast<T*>(&_obj); }
bool IsValid() const override { return true; }
void* GetObjIfValid() const override { return const_cast<T*>(&_obj); }

#ifndef TRINITY
void Invalidate() override { }
#endif

private:
T _obj;
Expand Down Expand Up @@ -375,7 +436,8 @@ class ElunaTemplate
if (!elunaObj)
return NULL;

if (!elunaObj->IsValid())
void* obj = elunaObj->GetObjIfValid();
if (!obj)
{
char buff[256];
snprintf(buff, 256, "%s expected, got pointer to nonexisting (invalidated) object (%s). Check your code.", tname, luaL_typename(L, narg));
Expand All @@ -389,7 +451,7 @@ class ElunaTemplate
}
return NULL;
}
return static_cast<T*>(elunaObj->GetObj());
return static_cast<T*>(obj);
}

static int GetType(lua_State* L)
Expand Down
8 changes: 4 additions & 4 deletions LuaEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,13 @@ void Eluna::RunScripts()
OnLuaStateOpen();
}

#ifndef TRINITY
void Eluna::InvalidateObjects()
{
++callstackid;
#ifdef TRINITY
ASSERT(callstackid, "Callstackid overflow");
#else
ASSERT(callstackid && "Callstackid overflow");
#endif
}
#endif

void Eluna::Report(lua_State* _L)
{
Expand Down Expand Up @@ -989,8 +987,10 @@ void Eluna::CleanUpStack(int number_of_arguments)
lua_pop(L, number_of_arguments + 1); // Add 1 because the caller doesn't know about `event_id`.
// Stack: (empty)

#ifndef TRINITY
if (event_level == 0)
InvalidateObjects();
#endif
}

/*
Expand Down
6 changes: 6 additions & 0 deletions LuaEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,13 @@ class ELUNA_GAME_API Eluna
// Indicates that the lua state should be reloaded
bool reload = false;

#ifndef TRINITY
// A counter for lua event stacks that occur (see event_level).
// This is used to determine whether an object belongs to the current call stack or not.
// 0 is reserved for always belonging to the call stack
// 1 is reserved for a non valid callstackid
uint64 callstackid = 2;
#endif
// A counter for the amount of nested events. When the event_level
// reaches 0 we are about to return back to C++. At this point the
// objects used during the event stack are invalidated.
Expand All @@ -200,7 +202,9 @@ class ELUNA_GAME_API Eluna
void CloseLua();
void DestroyBindStores();
void CreateBindStores();
#ifndef TRINITY
void InvalidateObjects();
#endif

// Use ReloadEluna() to make eluna reload
// This is called on world update to reload eluna
Expand Down Expand Up @@ -345,7 +349,9 @@ class ELUNA_GAME_API Eluna

void RunScripts();
bool HasLuaState() const { return L != NULL; }
#ifndef TRINITY
uint64 GetCallstackId() const { return callstackid; }
#endif
int Register(uint8 reg, uint32 entry, ObjectGuid guid, uint32 instanceId, uint32 event_id, int functionRef, uint32 shots);
void UpdateEluna(uint32 diff);

Expand Down
23 changes: 23 additions & 0 deletions LuaFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,29 @@ extern "C"
#include "VehicleMethods.h"
#include "BattleGroundMethods.h"

#ifdef TRINITY
ElunaConstrainedObjectRef<Aura> GetWeakPtrFor(Aura const* obj) { return { obj->GetWeakPtr(), obj->GetOwner()->GetMap() }; }
ElunaConstrainedObjectRef<Battleground> GetWeakPtrFor(Battleground const* obj) { return { obj->GetWeakPtr(), obj->GetBgMap() }; }
ElunaConstrainedObjectRef<Group> GetWeakPtrFor(Group const* obj) { return { obj->GetWeakPtr(), nullptr }; }
ElunaConstrainedObjectRef<Guild> GetWeakPtrFor(Guild const* obj) { return { obj->GetWeakPtr(), nullptr }; }
ElunaConstrainedObjectRef<Map> GetWeakPtrFor(Map const* obj) { return { obj->GetWeakPtr(), obj }; }
ElunaConstrainedObjectRef<Object> GetWeakPtrForObjectImpl(Object const* obj)
{
if (obj->isType(TYPEMASK_WORLDOBJECT))
return { obj->GetWeakPtr(), static_cast<WorldObject const*>(obj)->GetMap() };

if (obj->GetTypeId() == TYPEID_ITEM)
if (Player const* player = static_cast<Item const*>(obj)->GetOwner())
return { obj->GetWeakPtr(), player->GetMap() };

// possibly dangerous item
return { obj->GetWeakPtr(), nullptr };
}
ElunaConstrainedObjectRef<Quest> GetWeakPtrFor(Quest const* obj) { return { obj->GetWeakPtr(), nullptr }; }
ElunaConstrainedObjectRef<Spell> GetWeakPtrFor(Spell const* obj) { return { obj->GetWeakPtr(), obj->GetCaster()->GetMap() }; }
ElunaConstrainedObjectRef<Vehicle> GetWeakPtrFor(Vehicle const* obj) { return { obj->GetWeakPtr(), obj->GetBase()->GetMap() }; }
#endif

// Template by Mud from http://stackoverflow.com/questions/4484437/lua-integer-type/4485511#4485511
template<> int ElunaTemplate<unsigned long long>::Add(lua_State* L) { Eluna* E = Eluna::GetEluna(L); E->Push(E->CHECKVAL<unsigned long long>(1) + E->CHECKVAL<unsigned long long>(2)); return 1; }
template<> int ElunaTemplate<unsigned long long>::Substract(lua_State* L) { Eluna* E = Eluna::GetEluna(L); E->Push(E->CHECKVAL<unsigned long long>(1) - E->CHECKVAL<unsigned long long>(2)); return 1; }
Expand Down
2 changes: 2 additions & 0 deletions hooks/ServerHooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,9 @@ void Eluna::OnTimedEvent(int funcRef, uint32 delay, uint32 calls, WorldObject* o
ExecuteCall(4, 0);

ASSERT(!event_level);
#ifndef TRINITY
InvalidateObjects();
#endif
}

void Eluna::OnGameEventStart(uint32 eventid)
Expand Down
3 changes: 1 addition & 2 deletions methods/TrinityCore/AuraMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,9 @@ namespace LuaAura
/**
* Remove this [Aura] from the [Unit] it is applied to.
*/
int Remove(Eluna* E, Aura* aura)
int Remove(Eluna* /*E*/, Aura* aura)
{
aura->Remove();
E->CHECKOBJ<ElunaObject>(1)->Invalidate();
return 0;
}

Expand Down
1 change: 0 additions & 1 deletion methods/TrinityCore/GameObjectMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,6 @@ namespace LuaGameObject
go->SetRespawnTime(0);
go->Delete();

E->CHECKOBJ<ElunaObject>(1)->Invalidate();
return 0;
}

Expand Down
3 changes: 0 additions & 3 deletions methods/TrinityCore/PlayerMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -3163,10 +3163,7 @@ namespace LuaPlayer
}
else
{
bool all = itemCount >= item->GetCount();
player->DestroyItemCount(item, itemCount, true);
if (all)
E->CHECKOBJ<ElunaObject>(2)->Invalidate();
}
return 0;
}
Expand Down

0 comments on commit f4a1203

Please sign in to comment.