mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
265 lines
8.2 KiB
C++
265 lines
8.2 KiB
C++
#pragma once
|
|
|
|
#include "Lua.hpp"
|
|
|
|
#include "util/exception.h"
|
|
#include "util/utilities.h"
|
|
#include "rbx/Debug.h"
|
|
#include "rbx/atomic.h"
|
|
|
|
namespace RBX { namespace Lua {
|
|
|
|
void newweaktable(lua_State *L, const char *mode);
|
|
|
|
template<class C, int (C::*Func)(lua_State*)>
|
|
int memberFunctionProxy(lua_State* thread)
|
|
{
|
|
C* c = reinterpret_cast<C*>(lua_touserdata(thread, lua_upvalueindex(1)));
|
|
return (c->*Func)(thread);
|
|
}
|
|
|
|
template<class C, int (C::*Func)(lua_State*)>
|
|
void pushMemberFunction(lua_State* L, C* c)
|
|
{
|
|
lua_pushlightuserdata(L, (void*)c);
|
|
lua_pushcclosure(L, &memberFunctionProxy<C, Func>, 1);
|
|
}
|
|
|
|
// TODO: Use traits pattern rather than bool __eq
|
|
template<class Class, bool __eq = true>
|
|
class Bridge
|
|
{
|
|
public:
|
|
// 0 arg constructor
|
|
static Class* pushNewObject(lua_State *L)
|
|
{
|
|
Class* data = (Class*)lua_newuserdata(L, sizeof(Class));
|
|
new(data) Class;
|
|
luaL_getmetatable(L, className);
|
|
lua_setmetatable(L, -2);
|
|
return data;
|
|
}
|
|
// 1 arg constructor
|
|
template<typename Param1>
|
|
static Class* pushNewObject(lua_State *L, Param1 param1)
|
|
{
|
|
Class* data = (Class*)lua_newuserdata(L, sizeof(Class));
|
|
new(data) Class(param1);
|
|
luaL_getmetatable(L, className);
|
|
lua_setmetatable(L, -2);
|
|
return data;
|
|
}
|
|
// 2 arg constructor
|
|
template<typename Param1, typename Param2>
|
|
static Class* pushNewObject(lua_State *L, Param1 param1, Param2 param2)
|
|
{
|
|
Class* data = (Class*)lua_newuserdata(L, sizeof(Class));
|
|
new(data) Class(param1, param2);
|
|
luaL_getmetatable(L, className);
|
|
lua_setmetatable(L, -2);
|
|
return data;
|
|
}
|
|
|
|
// Throws an exception if index doesn't hold the right type
|
|
static Class& getObject(lua_State *L, unsigned int index) {
|
|
void *ud = luaL_checkudata(L, index, className);
|
|
return *reinterpret_cast<Class*>(ud);
|
|
}
|
|
|
|
// Returns false if index doesn't hold the right type (leaving value unchanged)
|
|
template<typename V>
|
|
static bool getValue(lua_State *L, unsigned int index, V& value) {
|
|
|
|
// A re-implementation of luaL_checkudata that doesn't throw an exception
|
|
void *p = lua_touserdata(L, index);
|
|
if (p != NULL) { /* value is a userdata? */
|
|
if (lua_getmetatable(L, index)) { /* does it have a metatable? */
|
|
lua_getfield(L, LUA_REGISTRYINDEX, className); /* get correct metatable */
|
|
if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
|
|
lua_pop(L, 2); /* remove both metatables */
|
|
value = *reinterpret_cast<Class*>(p);
|
|
return true;
|
|
}
|
|
else
|
|
lua_pop(L, 2); /* remove both metatables */
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void registerClass (lua_State *L);
|
|
|
|
/// gcc craps out with the error while it is called from ScriptContext.cpp for registerClass fns for the Bridge e.g EventBridge::registerClass(globalState);
|
|
// error: 'static int RBX::Lua::Bridge<Class, __eq>::on_index(lua_State*) [with Class = RBX::Lua::EventInstance, bool __eq = true]' is protected
|
|
#ifdef _WIN32
|
|
protected:
|
|
#endif
|
|
|
|
// The following members must be implemented by client:
|
|
static int on_index(const Class& object, const char* name, lua_State *L);
|
|
static void on_newindex(Class& object, const char* name, lua_State *L);
|
|
|
|
// This member may be specialized when StringConverter has no implementation for Class:
|
|
static int on_tostring(const Class& object, lua_State *L);
|
|
|
|
static const char* className;
|
|
|
|
static int on_gc(lua_State *L) {
|
|
getObject(L, 1).~Class();
|
|
return 0;
|
|
}
|
|
|
|
static int on_tostring(lua_State *L) {
|
|
return on_tostring(getObject(L, 1), L);
|
|
}
|
|
|
|
static int on_newindex(lua_State *L) {
|
|
const char* name = lua_checkstring_secure(L, 2);
|
|
on_newindex(getObject(L, 1), name, L);
|
|
return 0;
|
|
}
|
|
|
|
static int on_index(lua_State *L) {
|
|
const char* name = lua_checkstring_secure(L, 2);
|
|
return on_index(getObject(L, 1), name, L);
|
|
}
|
|
|
|
static int on_eq(lua_State *L) {
|
|
lua_pushboolean(L, getObject(L, 1) == getObject(L, 2));
|
|
return 1;
|
|
}
|
|
};
|
|
|
|
// This class hides parent on purpose
|
|
template<class Class>
|
|
class SharedPtrBridge : protected Bridge<boost::shared_ptr<Class>, false>
|
|
{
|
|
public:
|
|
static void registerClass (lua_State *L)
|
|
{
|
|
Bridge<boost::shared_ptr<Class>, false>::registerClass(L);
|
|
}
|
|
|
|
static void registerClassLibrary (lua_State *L) {
|
|
// Declare the UserData re-use table
|
|
lua_pushlightuserdata(L, (void*)&push);
|
|
newweaktable(L, "v");
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
}
|
|
|
|
static void push(lua_State *L, boost::shared_ptr<Class> instance)
|
|
{
|
|
if (instance==NULL)
|
|
lua_pushnil(L);
|
|
else
|
|
{
|
|
#ifdef _DEBUG
|
|
int i = lua_gettop(L);
|
|
#endif
|
|
// Matt Campbell at Serotek Corporation had a great idea. Create only
|
|
// a single instance of the userdata and re-use it. This way the userdata
|
|
// can be used in table keys, and we don't need an __eq operator
|
|
// Also see http://lua-users.org/lists/lua-l/2004-07/msg00391.html
|
|
|
|
lua_pushlightuserdata(L, (void*)&push ); /* Registry mapping for weak table. Key is arbitrary. */
|
|
lua_rawget(L, LUA_REGISTRYINDEX); // Stack: t
|
|
RBXASSERT(!lua_isnil( L, -1 )); // Did you forget to call registerClassLibrary??
|
|
|
|
// Now the top of the stack is our lookup table
|
|
// See if we already have a UserData for this instance
|
|
lua_pushlightuserdata (L, (void*)instance.get()); // Stack: t, i
|
|
lua_rawget (L, -2); // Stack: t, t[i]
|
|
|
|
if( lua_isnil( L, -1 ) ) { // Stack: t, nil or I
|
|
lua_pop (L, 1); // Stack: t
|
|
Bridge<boost::shared_ptr<Class>, false>::pushNewObject(L, instance); // Stack: t, I
|
|
/* store the value for later use */
|
|
lua_pushlightuserdata (L, (void*)instance.get()); // Stack: t, I, i
|
|
lua_pushvalue (L, -2); // Stack: t, I, i, I
|
|
lua_rawset (L, -4); // Stack: t, I
|
|
}
|
|
lua_remove (L, -2); // Stack: I
|
|
#ifdef _DEBUG
|
|
RBXASSERT(lua_gettop(L) == i + 1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static boost::shared_ptr<Class> getPtr(lua_State *L, unsigned int index)
|
|
{
|
|
if (lua_isnil(L, index))
|
|
return boost::shared_ptr<Class>();
|
|
else
|
|
return RBX::Lua::Bridge<boost::shared_ptr<Class>, false>::getObject(L, index);
|
|
}
|
|
|
|
template<typename V>
|
|
static bool getPtr(lua_State *L, unsigned int index, V& value) {
|
|
if (lua_isnil(L, index))
|
|
{
|
|
value = boost::shared_ptr<Class>();
|
|
return true;
|
|
}
|
|
else
|
|
return Bridge<boost::shared_ptr<Class>, false>::getValue(L, index, value);
|
|
}
|
|
|
|
};
|
|
|
|
// This class hides Bridge on purpose
|
|
template<class T>
|
|
class SingletonBridge : protected Bridge<T, false>
|
|
{
|
|
public:
|
|
static void registerClass (lua_State *L) {
|
|
Bridge<T, false>::registerClass(L);
|
|
}
|
|
|
|
static void registerClassLibrary (lua_State *L) {
|
|
// Declare the UserData re-use table
|
|
lua_pushlightuserdata(L, (void*)&push);
|
|
lua_newtable(L); // Not a weak table, unlike SharedPtrBridge
|
|
lua_rawset(L, LUA_REGISTRYINDEX);
|
|
}
|
|
|
|
static void push(lua_State *L, T item)
|
|
{
|
|
#ifdef _DEBUG
|
|
int i = lua_gettop(L);
|
|
#endif
|
|
// Matt Campbell at Serotek Corporation had a great idea. Create only
|
|
// a single instance of the userdata and re-use it. This way the userdata
|
|
// can be used in table keys
|
|
// Also see http://lua-users.org/lists/lua-l/2004-07/msg00391.html
|
|
|
|
lua_pushlightuserdata(L, (void*)&push); /* Registry mapping for table. Key is arbitrary. */
|
|
lua_rawget(L, LUA_REGISTRYINDEX); // Stack: t
|
|
RBXASSERT(!lua_isnil( L, -1 )); // Did you forget to call registerClassLibrary??
|
|
|
|
// Now the top of the stack is our lookup table
|
|
// See if we already have a UserData for this instance
|
|
lua_pushlightuserdata (L, (void*)item); // Stack: t, i
|
|
lua_rawget (L, -2); // Stack: t, t[i]
|
|
|
|
// TODO: only allow explicit, one-time declaration
|
|
if( lua_isnil( L, -1 ) ) { // Stack: t, nil or I
|
|
lua_pop (L, 1); // Stack: t
|
|
Bridge<T, false>::pushNewObject(L, item); // Stack: t, I
|
|
/* store the value for later use */
|
|
lua_pushlightuserdata (L, (void*)item); // Stack: t, I, i
|
|
lua_pushvalue (L, -2); // Stack: t, I, i, I
|
|
lua_rawset (L, -4); // Stack: t, I
|
|
}
|
|
lua_remove (L, -2); // Stack: I
|
|
|
|
#ifdef _DEBUG
|
|
RBXASSERT(lua_gettop(L) == i + 1);
|
|
#endif
|
|
}
|
|
};
|
|
|
|
} }
|
|
|
|
|
|
|