Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ElunaTemplate refactor #492

Merged
merged 12 commits into from
Aug 7, 2024
156 changes: 67 additions & 89 deletions ElunaTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,80 +26,6 @@ extern "C"
#include "UniqueTrackablePtr.h"
#endif

class ElunaGlobal
{
public:
struct ElunaRegister
{
const char* name;
int(*func)(Eluna*);
MethodRegisterState regState = METHOD_REG_ALL;
};

static int thunk(lua_State* L)
{
ElunaRegister* l = static_cast<ElunaRegister*>(lua_touserdata(L, lua_upvalueindex(1)));
Eluna* E = static_cast<Eluna*>(lua_touserdata(L, lua_upvalueindex(2)));
int top = lua_gettop(L);
int expected = l->func(E);
int args = lua_gettop(L) - top;
if (args < 0 || args > expected)
{
ELUNA_LOG_ERROR("[Eluna]: %s returned unexpected amount of arguments %i out of %i. Report to devs", l->name, args, expected);
ASSERT(false);
}
lua_settop(L, top + expected);
return expected;
}

static void SetMethods(Eluna* E, ElunaRegister* methodTable)
{
ASSERT(E);
ASSERT(methodTable);

lua_pushglobaltable(E->L);

for (; methodTable && methodTable->name; ++methodTable)
{
lua_pushstring(E->L, methodTable->name);

// if the method should not be registered, push a closure to error output function
if (methodTable->regState == METHOD_REG_NONE)
{
lua_pushcclosure(E->L, MethodUnimpl, 0);
lua_rawset(E->L, -3);
continue;
}

// if we're in multistate mode, we need to check whether a method is flagged as a world or a map specific method
if (!E->GetCompatibilityMode() && methodTable->regState != METHOD_REG_ALL)
{
// if the method should not be registered, push a closure to error output function
if ((E->GetBoundMapId() == -1 && methodTable->regState == METHOD_REG_MAP) ||
(E->GetBoundMapId() != -1 && methodTable->regState == METHOD_REG_WORLD))
{
lua_pushcclosure(E->L, MethodWrongState, 0);
lua_rawset(E->L, -3);
continue;
}
}

// push method table and Eluna object pointers as light user data
lua_pushlightuserdata(E->L, (void*)methodTable);
lua_pushlightuserdata(E->L, (void*)E);

// push a closure to the thunk function with 2 upvalues (method table and Eluna object)
lua_pushcclosure(E->L, thunk, 2);
lua_rawset(E->L, -3);
}

lua_remove(E->L, -1);
}

static int MethodWrongState(lua_State* L) { luaL_error(L, "attempt to call a method that does not exist for state: %d", Eluna::GetEluna(L)->GetBoundMapId()); return 0; }
static int MethodUnimpl(lua_State* L) { luaL_error(L, "attempt to call a method that is not implemented for this emulator"); return 0; }
};

class ElunaObject
{
public:
Expand Down Expand Up @@ -226,15 +152,27 @@ MAKE_ELUNA_OBJECT_VALUE_IMPL(ObjectGuid);
MAKE_ELUNA_OBJECT_VALUE_IMPL(WorldPacket);
MAKE_ELUNA_OBJECT_VALUE_IMPL(ElunaQuery);

template<typename T>
template<typename T = void>
struct ElunaRegister
{
const char* name;
int(*mfunc)(Eluna*, T*);
MethodRegisterState regState = METHOD_REG_ALL;
std::variant<std::monostate, int(*)(Eluna*, T*), int(*)(Eluna*)> mfunc;
MethodRegisterState regState;

// constructor for non-globals (with T*)
ElunaRegister(const char* name, int(*func)(Eluna*, T*), MethodRegisterState state = METHOD_REG_ALL)
: name(name), mfunc(func), regState(state) {}

// constructor for globals (without T*)
ElunaRegister(const char* name, int(*func)(Eluna*), MethodRegisterState state = METHOD_REG_ALL)
: name(name), mfunc(func), regState(state) {}

// constructor for nullptr functions and METHOD_REG_NONE (unimplemented methods)
ElunaRegister(const char* name = nullptr, MethodRegisterState state = METHOD_REG_NONE)
: name(name), mfunc(std::monostate{}), regState(state) {}
};

template<typename T>
template<typename T = void>
class ElunaTemplate
{
public:
Expand All @@ -260,7 +198,7 @@ class ElunaTemplate

// create metatable for userdata of this type
luaL_newmetatable(E->L, tname);
int metatable = lua_gettop(E->L);
int metatable = lua_gettop(E->L);

// push methodtable to stack to be accessed and modified by users
lua_pushvalue(E->L, metatable);
Expand Down Expand Up @@ -339,17 +277,35 @@ class ElunaTemplate
lua_pop(E->L, 1);
}

template<typename C>
template<typename C = void>
Foereaper marked this conversation as resolved.
Show resolved Hide resolved
static void SetMethods(Eluna* E, ElunaRegister<C>* methodTable)
Foereaper marked this conversation as resolved.
Show resolved Hide resolved
{
ASSERT(E);
ASSERT(tname);
ASSERT(methodTable);

// get metatable
lua_pushstring(E->L, tname);
lua_rawget(E->L, LUA_REGISTRYINDEX);
ASSERT(lua_istable(E->L, -1));
// determine if the method table functions are global or non-global
bool isGlobal = false;
Foereaper marked this conversation as resolved.
Show resolved Hide resolved
const auto& firstMethod = methodTable[0];
std::visit([&isGlobal](auto&& func)
{
using FuncType = std::decay_t<decltype(func)>;
if constexpr (std::is_same_v<FuncType, int(*)(Eluna*)>)
isGlobal = true;
}, firstMethod.mfunc);

if (isGlobal)
{
lua_pushglobaltable(E->L);
}
else
{
ASSERT(tname);

// get metatable
lua_pushstring(E->L, tname);
lua_rawget(E->L, LUA_REGISTRYINDEX);
ASSERT(lua_istable(E->L, -1));
}

// load all core-specific methods
for (; methodTable && methodTable->name; ++methodTable)
Expand Down Expand Up @@ -462,12 +418,34 @@ class ElunaTemplate
ElunaRegister<T>* l = static_cast<ElunaRegister<T>*>(lua_touserdata(L, lua_upvalueindex(1)));
Eluna* E = static_cast<Eluna*>(lua_touserdata(L, lua_upvalueindex(2)));

T* obj = E->CHECKOBJ<T>(1); // get self
if (!obj)
return 0;
// determine if the method table functions are global or non-global
constexpr bool isGlobal = std::is_same_v<T, void>;

// we only check self if the method is not a global
T* obj;
if constexpr (!isGlobal)
{
obj = E->CHECKOBJ<T>(1);
if (!obj)
return 0;
}

int top = lua_gettop(L);
int expected = l->mfunc(E, obj);

int expected = 0;
if constexpr (isGlobal)
{
auto func = std::get_if<int(*)(Eluna*)>(&l->mfunc);
if (func)
expected = (*func)(E);
}
else
{
auto func = std::get_if<int(*)(Eluna*, T*)>(&l->mfunc);
if (func)
expected = (*func)(E, obj);
}

int args = lua_gettop(L) - top;
if (args < 0 || args > expected)
{
Expand Down
2 changes: 1 addition & 1 deletion LuaFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ template<> int ElunaTemplate<ObjectGuid>::ToString(lua_State* L)

void RegisterFunctions(Eluna* E)
{
ElunaGlobal::SetMethods(E, LuaGlobalFunctions::GlobalMethods);
ElunaTemplate<>::SetMethods(E, LuaGlobalFunctions::GlobalMethods);

ElunaTemplate<Object>::Register(E, "Object");
ElunaTemplate<Object>::SetMethods(E, LuaObject::ObjectMethods);
Expand Down
2 changes: 1 addition & 1 deletion methods/CMangos/AuraMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ namespace LuaAura
// Other
{ "Remove", &LuaAura::Remove },

{ NULL, NULL, METHOD_REG_NONE }
{ nullptr, METHOD_REG_NONE }
};
};
#endif
2 changes: 1 addition & 1 deletion methods/CMangos/BattleGroundMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ namespace LuaBattleGround
{ "GetWinner", &LuaBattleGround::GetWinner },
{ "GetStatus", &LuaBattleGround::GetStatus },

{ NULL, NULL, METHOD_REG_NONE }
{ nullptr, METHOD_REG_NONE }
};
};
#endif
2 changes: 1 addition & 1 deletion methods/CMangos/CorpseMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ namespace LuaCorpse
{ "ResetGhostTime", &LuaCorpse::ResetGhostTime },
{ "SaveToDB", &LuaCorpse::SaveToDB },

{ NULL, NULL, METHOD_REG_NONE }
{ nullptr, METHOD_REG_NONE }
};
};
#endif
46 changes: 23 additions & 23 deletions methods/CMangos/CreatureMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -1222,7 +1222,7 @@ namespace LuaCreature
#ifndef CATA
{ "GetShieldBlockValue", &LuaCreature::GetShieldBlockValue },
#else
{ "GetShieldBlockValue", nullptr, METHOD_REG_NONE },
{ "GetShieldBlockValue", METHOD_REG_NONE },
#endif

// Setters
Expand All @@ -1243,7 +1243,7 @@ namespace LuaCreature
#ifndef CATA
{ "SetDisableReputationGain", &LuaCreature::SetDisableReputationGain },
#else
{ "SetDisableReputationGain", nullptr, METHOD_REG_NONE },
{ "SetDisableReputationGain", METHOD_REG_NONE },
#endif

// Boolean
Expand Down Expand Up @@ -1271,7 +1271,7 @@ namespace LuaCreature
#ifndef CATA
{ "IsReputationGainDisabled", &LuaCreature::IsReputationGainDisabled },
#else
{ "IsReputationGainDisabled", nullptr, METHOD_REG_NONE },
{ "IsReputationGainDisabled", METHOD_REG_NONE },
#endif

// Other
Expand All @@ -1291,26 +1291,26 @@ namespace LuaCreature
{ "RemoveFromWorld", &LuaCreature::RemoveFromWorld },

// Not implemented methods
{ "GetWaypointPath", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "GetLootMode", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "SetRegeneratingHealth", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "SetLootMode", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "SetReactState", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "IsDungeonBoss", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "IsTrigger", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "CanStartAttack", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "IsDamageEnoughForLootingAndReward", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "HasLootMode", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "AddLootMode", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "ResetLootMode", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "RemoveLootMode", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "GetThreat", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "ClearThreat", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "ResetAllThreat", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "FixateTarget", nullptr, METHOD_REG_NONE }, // TC/Acore
{ "ClearFixate", nullptr, METHOD_REG_NONE }, // TC/Acore

{ NULL, NULL, METHOD_REG_NONE }
{ "GetWaypointPath", METHOD_REG_NONE }, // TC/Acore
{ "GetLootMode", METHOD_REG_NONE }, // TC/Acore
{ "SetRegeneratingHealth", METHOD_REG_NONE }, // TC/Acore
{ "SetLootMode", METHOD_REG_NONE }, // TC/Acore
{ "SetReactState", METHOD_REG_NONE }, // TC/Acore
{ "IsDungeonBoss", METHOD_REG_NONE }, // TC/Acore
{ "IsTrigger", METHOD_REG_NONE }, // TC/Acore
{ "CanStartAttack", METHOD_REG_NONE }, // TC/Acore
{ "IsDamageEnoughForLootingAndReward", METHOD_REG_NONE }, // TC/Acore
{ "HasLootMode", METHOD_REG_NONE }, // TC/Acore
{ "AddLootMode", METHOD_REG_NONE }, // TC/Acore
{ "ResetLootMode", METHOD_REG_NONE }, // TC/Acore
{ "RemoveLootMode", METHOD_REG_NONE }, // TC/Acore
{ "GetThreat", METHOD_REG_NONE }, // TC/Acore
{ "ClearThreat", METHOD_REG_NONE }, // TC/Acore
{ "ResetAllThreat", METHOD_REG_NONE }, // TC/Acore
{ "FixateTarget", METHOD_REG_NONE }, // TC/Acore
{ "ClearFixate", METHOD_REG_NONE }, // TC/Acore

{ nullptr, METHOD_REG_NONE }
};
};
#endif
2 changes: 1 addition & 1 deletion methods/CMangos/ElunaQueryMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ namespace LuaQuery
{ "NextRow", &LuaQuery::NextRow },
{ "IsNull", &LuaQuery::IsNull },

{ NULL, NULL, METHOD_REG_NONE }
{ nullptr, METHOD_REG_NONE }
};
};
#undef RESULT
Expand Down
4 changes: 2 additions & 2 deletions methods/CMangos/GameObjectMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,9 +332,9 @@ namespace LuaGameObject
{ "SaveToDB", &LuaGameObject::SaveToDB },

// Not implemented methods
{ "IsDestructible", nullptr, METHOD_REG_NONE }, // Not implemented
{ "IsDestructible", METHOD_REG_NONE }, // Not implemented

{ NULL, NULL, METHOD_REG_NONE }
{ nullptr, METHOD_REG_NONE }
};
};
#endif
4 changes: 2 additions & 2 deletions methods/CMangos/GlobalMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -3180,7 +3180,7 @@ namespace LuaGlobalFunctions
return 0;
}

ElunaGlobal::ElunaRegister GlobalMethods[] =
ElunaRegister<> GlobalMethods[] =
{
// Hooks
{ "RegisterPacketEvent", &LuaGlobalFunctions::RegisterPacketEvent },
Expand Down Expand Up @@ -3297,7 +3297,7 @@ namespace LuaGlobalFunctions
{ "StartGameEvent", &LuaGlobalFunctions::StartGameEvent },
{ "StopGameEvent", &LuaGlobalFunctions::StopGameEvent },

{ NULL, NULL, METHOD_REG_NONE }
{ nullptr, METHOD_REG_NONE }
};
}
#endif
12 changes: 6 additions & 6 deletions methods/CMangos/GroupMethods.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,20 +405,20 @@ namespace LuaGroup
#if defined WOTLK
{ "IsLFGGroup", &LuaGroup::IsLFGGroup },
#else
{ "IsLFGGroup", nullptr, METHOD_REG_NONE },
{ "IsLFGGroup", METHOD_REG_NONE },
#endif

// Other
{ "SendPacket", &LuaGroup::SendPacket },
{ "ConvertToRaid", &LuaGroup::ConvertToRaid, METHOD_REG_WORLD }, // World state method only in multistate

// Not implemented methods
{ "IsBFGroup", nullptr, METHOD_REG_NONE }, // not implemented
{ "ConvertToLFG", nullptr, METHOD_REG_NONE }, // not implemented
{ "GetMemberFlags", nullptr, METHOD_REG_NONE }, // not implemented
{ "SetMemberFlag", nullptr, METHOD_REG_NONE }, // not implemented
{ "IsBFGroup", METHOD_REG_NONE }, // not implemented
{ "ConvertToLFG", METHOD_REG_NONE }, // not implemented
{ "GetMemberFlags", METHOD_REG_NONE }, // not implemented
{ "SetMemberFlag", METHOD_REG_NONE }, // not implemented

{ NULL, NULL, METHOD_REG_NONE }
{ nullptr, METHOD_REG_NONE }
};
};

Expand Down
Loading
Loading