Skip to content

Commit

Permalink
Changed how data pushed to lua is validated to use weak_ptr based imp…
Browse files Browse the repository at this point in the history
…lementation

* Removed additional indirection when pushing 64 bit integers and ObjectGuids
  • Loading branch information
Shauren committed Mar 14, 2024
1 parent 6127346 commit 480875c
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 156 deletions.
139 changes: 72 additions & 67 deletions ElunaTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ extern "C"
#include "Globals/SharedDefines.h"
#endif

#include "UniqueTrackablePtr.h"

#define TRACKABLE_PTR_NAMESPACE ::Trinity::

class ElunaGlobal
{
public:
Expand Down Expand Up @@ -99,61 +103,86 @@ class ElunaGlobal
class ElunaObject
{
public:
template<typename T>
ElunaObject(Eluna* E, T* obj, bool manageMemory);
ElunaObject(Eluna* E, char const* tname) : E(E), type_name(tname)
{
}

~ElunaObject()
virtual ~ElunaObject()
{
}

// Get wrapped object pointer
void* GetObj() const { return object; }
virtual void* GetObj() const = 0;
// Returns whether the object is valid or not
bool IsValid() const { return !callstackid || callstackid == E->GetCallstackId(); }
// Returns whether the object can be invalidated or not
bool CanInvalidate() const { return _invalidate; }
virtual bool IsValid() const = 0;
// Returns pointer to the wrapped object's type name
const char* GetTypeName() const { return type_name; }

// Sets the object pointer that is wrapped
void SetObj(void* obj)
{
ASSERT(obj);
object = obj;
SetValid(true);
}
// Sets the object pointer to valid or invalid
void SetValid(bool valid)
{
ASSERT(!valid || (valid && object));
if (valid)
if (CanInvalidate())
callstackid = E->GetCallstackId();
else
callstackid = 0;
else
callstackid = 1;
}
// Sets whether the pointer will be invalidated at end of calls
void SetValidation(bool invalidate)
private:
Eluna* E;
const char* type_name;
};

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

template <typename T>
TRACKABLE_PTR_NAMESPACE unique_weak_ptr<T> GetWeakPtrFor(T const* obj)
{
return TRACKABLE_PTR_NAMESPACE static_pointer_cast<T>(GetWeakPtrForObjectImpl(obj));
}

template <typename T>
class ElunaObjectImpl : public ElunaObject
{
public:
ElunaObjectImpl(Eluna* E, T* obj, char const* tname) : ElunaObject(E, tname), _obj(GetWeakPtrFor(obj))
{
_invalidate = invalidate;
}
// Invalidates the pointer if it should be invalidated
void Invalidate()

void* GetObj() const override { return _obj.lock().get(); }
bool IsValid() const override { return !_obj.expired(); }

private:
TRACKABLE_PTR_NAMESPACE unique_weak_ptr<T> _obj;
};

template <typename T>
class ElunaObjectValueImpl : public ElunaObject
{
public:
ElunaObjectValueImpl(Eluna* E, T* obj, char const* tname) : ElunaObject(E, tname), _obj(std::move(*obj))
{
if (CanInvalidate())
callstackid = 1;
}

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

private:
Eluna* E;
uint64 callstackid;
bool _invalidate;
void* object;
const char* type_name;
T _obj;
};

#define MAKE_ELUNA_OBJECT_VALUE_IMPL(type) \
template <> \
class ElunaObjectImpl<type> : public ElunaObjectValueImpl<type> \
{ \
public: \
using ElunaObjectValueImpl::ElunaObjectValueImpl; \
}

MAKE_ELUNA_OBJECT_VALUE_IMPL(long long);
MAKE_ELUNA_OBJECT_VALUE_IMPL(unsigned long long);
MAKE_ELUNA_OBJECT_VALUE_IMPL(ObjectGuid);
MAKE_ELUNA_OBJECT_VALUE_IMPL(WorldPacket);
MAKE_ELUNA_OBJECT_VALUE_IMPL(ElunaQuery);

template<typename T>
struct ElunaRegister
{
Expand All @@ -167,13 +196,12 @@ class ElunaTemplate
{
public:
static const char* tname;
static bool manageMemory;

// name will be used as type name
// If gc is true, lua will handle the memory management for object pushed
// gc should be used if pushing for example WorldPacket,
// that will only be needed on lua side and will not be managed by TC/mangos/<core>
static void Register(Eluna* E, const char* name, bool gc = false)
static void Register(Eluna* E, const char* name)
{
ASSERT(E);
ASSERT(name);
Expand All @@ -186,7 +214,6 @@ class ElunaTemplate
lua_pop(E->L, 1);

tname = name;
manageMemory = gc;

// create metatable for userdata of this type
luaL_newmetatable(E->L, tname);
Expand Down Expand Up @@ -265,10 +292,6 @@ class ElunaTemplate
lua_pushcfunction(E->L, GetType);
lua_setfield(E->L, metatable, "GetObjectType");

// special method to decide object invalidation at end of call
lua_pushcfunction(E->L, SetInvalidation);
lua_setfield(E->L, metatable, "SetInvalidation");

// pop metatable
lua_pop(E->L, 1);
}
Expand Down Expand Up @@ -333,15 +356,17 @@ class ElunaTemplate
return 1;
}

typedef ElunaObjectImpl<T> ElunaObjectType;

// Create new userdata
ElunaObject* elunaObject = static_cast<ElunaObject*>(lua_newuserdata(L, sizeof(ElunaObject)));
ElunaObjectType* elunaObject = static_cast<ElunaObjectType*>(lua_newuserdata(L, sizeof(ElunaObjectType)));
if (!elunaObject)
{
ELUNA_LOG_ERROR("%s could not create new userdata", tname);
lua_pushnil(L);
return 1;
}
new (elunaObject) ElunaObject(E, const_cast<T*>(obj), manageMemory);
new (elunaObject) ElunaObjectType(E, const_cast<T*>(obj), tname);

// Set metatable for it
lua_pushstring(L, tname);
Expand Down Expand Up @@ -388,17 +413,6 @@ class ElunaTemplate
return 1;
}

static int SetInvalidation(lua_State* L)
{
Eluna* E = Eluna::GetEluna(L);

ElunaObject* elunaObj = E->CHECKOBJ<ElunaObject>(1);
bool invalidate = E->CHECKVAL<bool>(2);

elunaObj->SetValidation(invalidate);
return 0;
}

static int thunk(lua_State* L)
{
ElunaRegister<T>* l = static_cast<ElunaRegister<T>*>(lua_touserdata(L, lua_upvalueindex(1)));
Expand Down Expand Up @@ -429,8 +443,6 @@ class ElunaTemplate

// Get object pointer (and check type, no error)
ElunaObject* obj = E->CHECKOBJ<ElunaObject>(1, false);
if (obj && manageMemory)
delete static_cast<T*>(obj->GetObj());
obj->~ElunaObject();
return 0;
}
Expand Down Expand Up @@ -464,13 +476,6 @@ class ElunaTemplate
static int MethodUnimpl(lua_State* L) { luaL_error(L, "attempt to call a method that is not implemented for this emulator"); return 0; }
};

template<typename T>
ElunaObject::ElunaObject(Eluna* E, T* obj, bool manageMemory) : E(E), callstackid(1), _invalidate(!manageMemory), object(obj), type_name(ElunaTemplate<T>::tname)
{
SetValid(true);
}

template<typename T> const char* ElunaTemplate<T>::tname = NULL;
template<typename T> bool ElunaTemplate<T>::manageMemory = false;

#endif
4 changes: 2 additions & 2 deletions InstanceHooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ using namespace Hooks;
if (!MapEventBindings->HasBindingsFor(mapKey) && !InstanceEventBindings->HasBindingsFor(instanceKey))\
return;\
PushInstanceData(AI);\
HookPush(AI->instance)
HookPush<Map>(AI->instance)

#define START_HOOK_WITH_RETVAL(EVENT, AI, RETVAL) \
auto mapKey = EntryKey<InstanceEvents>(EVENT, AI->instance->GetId());\
auto instanceKey = EntryKey<InstanceEvents>(EVENT, AI->instance->GetInstanceId());\
if (!MapEventBindings->HasBindingsFor(mapKey) && !InstanceEventBindings->HasBindingsFor(instanceKey))\
return RETVAL;\
PushInstanceData(AI);\
HookPush(AI->instance)
HookPush<Map>(AI->instance)

void Eluna::OnInitialize(ElunaInstanceAI* ai)
{
Expand Down
25 changes: 8 additions & 17 deletions LuaEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,16 +313,6 @@ void Eluna::RunScripts()
OnLuaStateOpen();
}

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

void Eluna::Report(lua_State* _L)
{
const char* msg = lua_tostring(_L, -1);
Expand Down Expand Up @@ -423,11 +413,13 @@ void Eluna::Push()
}
void Eluna::Push(const long long l)
{
ElunaTemplate<long long>::Push(this, new long long(l));
// pushing pointer to local is fine, a copy of value will be stored, not pointer itself
ElunaTemplate<long long>::Push(this, &l);
}
void Eluna::Push(const unsigned long long l)
{
ElunaTemplate<unsigned long long>::Push(this, new unsigned long long(l));
// pushing pointer to local is fine, a copy of value will be stored, not pointer itself
ElunaTemplate<unsigned long long>::Push(this, &l);
}
void Eluna::Push(const long l)
{
Expand Down Expand Up @@ -544,7 +536,8 @@ void Eluna::Push(Object const* obj)
}
void Eluna::Push(ObjectGuid const guid)
{
ElunaTemplate<unsigned long long>::Push(this, new unsigned long long(guid.GetRawValue()));
// pushing pointer to local is fine, a copy of value will be stored, not pointer itself
ElunaTemplate<ObjectGuid>::Push(this, &guid);
}

static int CheckIntegerRange(lua_State* luastate, int narg, int min, int max)
Expand Down Expand Up @@ -650,7 +643,8 @@ template<> unsigned long Eluna::CHECKVAL<unsigned long>(int narg)
}
template<> ObjectGuid Eluna::CHECKVAL<ObjectGuid>(int narg)
{
return ObjectGuid(uint64((CHECKVAL<unsigned long long>(narg))));
ObjectGuid* guid = CHECKOBJ<ObjectGuid>(narg, true);
return guid ? *guid : ObjectGuid();
}

template<> Object* Eluna::CHECKOBJ<Object>(int narg, bool error)
Expand Down Expand Up @@ -1008,9 +1002,6 @@ 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)

if (event_level == 0)
InvalidateObjects();
}

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

// 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;
// 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 @@ -196,7 +191,6 @@ class ELUNA_GAME_API Eluna
void CloseLua();
void DestroyBindStores();
void CreateBindStores();
void InvalidateObjects();

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

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

Expand Down
Loading

0 comments on commit 480875c

Please sign in to comment.