Skip to content
Permalink
Browse files

Proof of concept for a client-side Lua API

  • Loading branch information...
Jeija committed Oct 28, 2012
1 parent 956c810 commit e1c520d147975faf5a159cea707af20a5e3ed859
Showing with 241 additions and 87 deletions.
  1. +9 −0 clientapitest.lua
  2. +1 −0 src/CMakeLists.txt
  3. +20 −1 src/client.cpp
  4. +4 −0 src/client.h
  5. +103 −0 src/clientapi.cpp
  6. +7 −0 src/clientapi.h
  7. +70 −2 src/script.cpp
  8. +26 −0 src/script.h
  9. +0 −84 src/scriptapi.cpp
  10. +1 −0 src/scriptapi.h
@@ -0,0 +1,9 @@
print("---Client Lua API started---")

client.on_step = function (dtime)
print("Hello World!".." | "..dtime .."\n")
end

function onstep ()
print("Hello World\n")
end
@@ -232,6 +232,7 @@ endif()
set(minetest_SRCS
${common_SRCS}
${sound_SRCS}
clientapi.cpp
localplayer.cpp
sky.cpp
clientmap.cpp
@@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "sound.h"
#include "util/string.h"
#include "hex.h"
#include "clientapi.h"

static std::string getMediaCacheDir()
{
@@ -265,7 +266,8 @@ Client::Client(
m_time_of_day_set(false),
m_last_time_of_day_f(-1),
m_time_of_day_update_timer(0),
m_removed_sounds_check_timer(0)
m_removed_sounds_check_timer(0),
m_lua(NULL)
{
m_packetcounter_timer = 0.0;
//m_delete_unused_sectors_timer = 0.0;
@@ -290,6 +292,17 @@ Client::Client(

m_env.addPlayer(player);
}

infostream<<"Client: Initializing Lua"<<std::endl;
m_lua = script_init();
assert(m_lua);
// Export API
clientapi_export(m_lua, this);
// Load and run builtin.lua
infostream<<"Client: Loading clientapitest.lua"<<std::endl;
bool success = clientapi_loadmod(m_lua, "/home/florian/prgm/minetest/minetest-epsilon/clientapitest.lua", "test");
if (success) printf("succes\n");
else printf("fail\n");
}

Client::~Client()
@@ -527,6 +540,12 @@ void Client::step(float dtime)
Do stuff if connected
*/


/*
Call ClientAPI Lua step
*/
clientapi_step(m_lua, dtime);

/*
Run Map's timers and unload unused data
*/
@@ -34,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filecache.h"
#include "localplayer.h"
#include "util/pointedthing.h"
#include "script.h"
#include "clientapi.h"

struct MeshMakeData;
class MapBlockMesh;
@@ -397,6 +399,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
// Detached inventories
// key = name
std::map<std::string, Inventory*> m_detached_inventories;

lua_State *m_lua;
};

#endif // !CLIENT_HEADER
@@ -0,0 +1,103 @@
#include "clientapi.h"

#include <iostream>
#include <list>
extern "C" {
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
}

#include "client.h"
#include "tool.h"
#include "util/string.h"
#include "log.h"

void clientapi_step(lua_State *L, float dtime)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
//infostream<<"scriptapi_environment_step"<<std::endl;
StackUnroller stack_unroller(L);

// Get minetest.registered_globalsteps
lua_getglobal(L, "client");
lua_getfield(L, -1, "on_step");
luaL_checktype(L, -1, LUA_TFUNCTION);
// Call on_step
lua_pushnumber(L, dtime);
lua_pcall(L, 1, 0, 0);
printf("called\n");
}

static int l_debug(lua_State *L)
{
std::string text = lua_tostring(L, 1);
dstream << text << std::endl;
return 0;
}

static const struct luaL_Reg client_f [] = {
{"debug", l_debug},
{NULL, NULL}
};

bool clientapi_loadmod(lua_State *L, const std::string &scriptpath,
const std::string &modname)
{
ModNameStorer modnamestorer(L, modname);

if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz"
"0123456789_")){
errorstream<<"Error loading client mod \""<<modname
<<"\": modname does not follow naming conventions: "
<<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
return false;
}

bool success = false;

try{
success = script_load(L, scriptpath.c_str());
}
catch(LuaError &e){
errorstream<<"Error loading mod \""<<modname
<<"\": "<<e.what()<<std::endl;
}

return success;
}

void clientapi_export(lua_State *L, Client *client)
{
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);

// Store server as light userdata in registry
// lua_pushlightuserdata(L, client);
// lua_setfield(L, LUA_REGISTRYINDEX, "minetest_server");

// Register global functions in table minetest
lua_newtable(L);
luaL_register(L, NULL, client_f);
lua_setglobal(L, "client");

// Get the main minetest table
lua_getglobal(L, "client");

// Add tables to minetest
// lua_newtable(L);
// lua_setfield(L, -2, "object_refs");
// lua_newtable(L);
// lua_setfield(L, -2, "luaentities");

// Register wrappers
// LuaItemStack::Register(L);
// InvRef::Register(L);
// NodeMetaRef::Register(L);
// NodeTimerRef::Register(L);
// ObjectRef::Register(L);
// EnvRef::Register(L);
// LuaPseudoRandom::Register(L);
// LuaPerlinNoise::Register(L);
}
@@ -0,0 +1,7 @@
class Client;
typedef struct lua_State lua_State;
#include <string>

void clientapi_step(lua_State *L, float dtime);
void clientapi_export(lua_State *L, Client *client);
bool clientapi_loadmod(lua_State *L, const std::string &scriptpath, const std::string &modname);
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cstdlib>
#include "log.h"
#include <iostream>
#include "debug.h"

extern "C" {
#include <lua.h>
@@ -38,6 +39,75 @@ LuaError::LuaError(lua_State *L, const std::string &s)
m_s += script_get_backtrace(L);
}

StackUnroller::StackUnroller(lua_State *L):
m_lua(L),
m_original_top(-1)
{
m_original_top = lua_gettop(m_lua); // store stack height
}


StackUnroller::~StackUnroller()
{
lua_settop(m_lua, m_original_top); // restore stack height
}

ModNameStorer::ModNameStorer(lua_State *L_, const std::string modname):
L(L_)
{
// Store current modname in registry
lua_pushstring(L, modname.c_str());
lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
}
ModNameStorer::~ModNameStorer()
{
// Clear current modname in registry
lua_pushnil(L);
lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname");
}

void stackDump(lua_State *L, std::ostream &o)
{
int i;
int top = lua_gettop(L);
for (i = 1; i <= top; i++) { /* repeat for each level */
int t = lua_type(L, i);
switch (t) {

case LUA_TSTRING: /* strings */
o<<"\""<<lua_tostring(L, i)<<"\"";
break;

case LUA_TBOOLEAN: /* booleans */
o<<(lua_toboolean(L, i) ? "true" : "false");
break;

case LUA_TNUMBER: /* numbers */ {
char buf[10];
snprintf(buf, 10, "%g", lua_tonumber(L, i));
o<<buf;
break; }

default: /* other values */
o<<lua_typename(L, t);
break;

}
o<<" ";
}
o<<std::endl;
}

void realitycheck(lua_State *L)
{
int top = lua_gettop(L);
if(top >= 30){
dstream<<"Stack is over 30:"<<std::endl;
stackDump(L, dstream);
script_error(L, "Stack is over 30 (reality check)");
}
}

std::string script_get_backtrace(lua_State *L)
{
std::string s;
@@ -122,5 +192,3 @@ void script_deinit(lua_State *L)
{
lua_close(L);
}


@@ -39,11 +39,37 @@ class LuaError : public std::exception
std::string m_s;
};

class StackUnroller
{
private:
lua_State *m_lua;
int m_original_top;
public:
StackUnroller(lua_State *L);
~StackUnroller();
};

class ModNameStorer
{
private:
lua_State *L;
public:
ModNameStorer(lua_State *L_, const std::string modname);
~ModNameStorer();
};

lua_State* script_init();
void script_deinit(lua_State *L);
std::string script_get_backtrace(lua_State *L);
void script_error(lua_State *L, const char *fmt, ...);
bool script_load(lua_State *L, const char *path);
void stackDump(lua_State *L, std::ostream &o);
void realitycheck(lua_State *L);

// What scriptapi_run_callbacks does with the return values of callbacks.
// Regardless of the mode, if only one callback is defined,
// its return value is the total return value.
// Modes only affect the case where 0 or >= 2 callbacks are defined.

#endif

Oops, something went wrong.

1 comment on commit e1c520d

@rubenwardy

This comment has been minimized.

Copy link

rubenwardy commented on e1c520d Feb 7, 2013

Awesome!

Please sign in to comment.
You can’t perform that action at this time.