aboutsummaryrefslogtreecommitdiff
path: root/src/script/cpp_api
diff options
context:
space:
mode:
authorWeblate <42@minetest.ru>2013-06-01 13:18:52 +0200
committerWeblate <42@minetest.ru>2013-06-01 13:18:52 +0200
commit1dfc2e02b3c358af4bb20bb3cb60ac7bd3ed1124 (patch)
tree54660f353f3342f89b6259ea6a5dfedfa20dc16f /src/script/cpp_api
parentec039a3d123120f9918812b13f5971e94546b9af (diff)
parent64627817fcca52f20948c24b60ce192b218f6ce2 (diff)
downloadminetest-1dfc2e02b3c358af4bb20bb3cb60ac7bd3ed1124.tar.gz
minetest-1dfc2e02b3c358af4bb20bb3cb60ac7bd3ed1124.tar.bz2
minetest-1dfc2e02b3c358af4bb20bb3cb60ac7bd3ed1124.zip
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'src/script/cpp_api')
-rw-r--r--src/script/cpp_api/CMakeLists.txt11
-rw-r--r--src/script/cpp_api/s_base.cpp264
-rw-r--r--src/script/cpp_api/s_base.h167
-rw-r--r--src/script/cpp_api/s_entity.cpp267
-rw-r--r--src/script/cpp_api/s_entity.h50
-rw-r--r--src/script/cpp_api/s_env.cpp132
-rw-r--r--src/script/cpp_api/s_env.h40
-rw-r--r--src/script/cpp_api/s_inventory.cpp260
-rw-r--r--src/script/cpp_api/s_inventory.h71
-rw-r--r--src/script/cpp_api/s_item.cpp164
-rw-r--r--src/script/cpp_api/s_item.h55
-rw-r--r--src/script/cpp_api/s_node.cpp233
-rw-r--r--src/script/cpp_api/s_node.h62
-rw-r--r--src/script/cpp_api/s_nodemeta.cpp261
-rw-r--r--src/script/cpp_api/s_nodemeta.h67
-rw-r--r--src/script/cpp_api/s_player.cpp113
-rw-r--r--src/script/cpp_api/s_player.h44
-rw-r--r--src/script/cpp_api/scriptapi.cpp291
-rw-r--r--src/script/cpp_api/scriptapi.h82
19 files changed, 2634 insertions, 0 deletions
diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt
new file mode 100644
index 000000000..6f5b51a49
--- /dev/null
+++ b/src/script/cpp_api/CMakeLists.txt
@@ -0,0 +1,11 @@
+set(SCRIPT_CPP_API_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_base.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_entity.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_env.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_inventory.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_item.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_node.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_nodemeta.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_player.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/scriptapi.cpp
+ PARENT_SCOPE)
diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp
new file mode 100644
index 000000000..e2e586357
--- /dev/null
+++ b/src/script/cpp_api/s_base.cpp
@@ -0,0 +1,264 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <stdio.h>
+#include <cstdarg>
+
+extern "C" {
+#include "lua.h"
+#include "lauxlib.h"
+}
+
+#include "cpp_api/s_base.h"
+#include "lua_api/l_object.h"
+#include "serverobject.h"
+
+ScriptApiBase::ScriptApiBase() :
+ m_luastackmutex(),
+#ifdef LOCK_DEBUG
+ m_locked(false),
+#endif
+ m_luastack(0),
+ m_server(0),
+ m_environment(0)
+{
+
+}
+
+
+void ScriptApiBase::realityCheck()
+{
+ int top = lua_gettop(m_luastack);
+ if(top >= 30){
+ dstream<<"Stack is over 30:"<<std::endl;
+ stackDump(dstream);
+ scriptError("Stack is over 30 (reality check)");
+ }
+}
+
+void ScriptApiBase::scriptError(const char *fmt, ...)
+{
+ va_list argp;
+ va_start(argp, fmt);
+ char buf[10000];
+ vsnprintf(buf, 10000, fmt, argp);
+ va_end(argp);
+ //errorstream<<"SCRIPT ERROR: "<<buf;
+ throw LuaError(m_luastack, buf);
+}
+
+void ScriptApiBase::stackDump(std::ostream &o)
+{
+ int i;
+ int top = lua_gettop(m_luastack);
+ for (i = 1; i <= top; i++) { /* repeat for each level */
+ int t = lua_type(m_luastack, i);
+ switch (t) {
+
+ case LUA_TSTRING: /* strings */
+ o<<"\""<<lua_tostring(m_luastack, i)<<"\"";
+ break;
+
+ case LUA_TBOOLEAN: /* booleans */
+ o<<(lua_toboolean(m_luastack, i) ? "true" : "false");
+ break;
+
+ case LUA_TNUMBER: /* numbers */ {
+ char buf[10];
+ snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
+ o<<buf;
+ break; }
+
+ default: /* other values */
+ o<<lua_typename(m_luastack, t);
+ break;
+
+ }
+ o<<" ";
+ }
+ o<<std::endl;
+}
+
+// Push the list of callbacks (a lua table).
+// Then push nargs arguments.
+// Then call this function, which
+// - runs the callbacks
+// - removes the table and arguments from the lua stack
+// - pushes the return value, computed depending on mode
+void ScriptApiBase::runCallbacks(int nargs,RunCallbacksMode mode)
+{
+ lua_State *L = getStack();
+
+ // Insert the return value into the lua stack, below the table
+ assert(lua_gettop(L) >= nargs + 1);
+ lua_pushnil(L);
+ lua_insert(L, -(nargs + 1) - 1);
+ // Stack now looks like this:
+ // ... <return value = nil> <table> <arg#1> <arg#2> ... <arg#n>
+
+ int rv = lua_gettop(L) - nargs - 1;
+ int table = rv + 1;
+ int arg = table + 1;
+
+ luaL_checktype(L, table, LUA_TTABLE);
+
+ // Foreach
+ lua_pushnil(L);
+ bool first_loop = true;
+ while(lua_next(L, table) != 0){
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ // Call function
+ for(int i = 0; i < nargs; i++)
+ lua_pushvalue(L, arg+i);
+ if(lua_pcall(L, nargs, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+
+ // Move return value to designated space in stack
+ // Or pop it
+ if(first_loop){
+ // Result of first callback is always moved
+ lua_replace(L, rv);
+ first_loop = false;
+ } else {
+ // Otherwise, what happens depends on the mode
+ if(mode == RUN_CALLBACKS_MODE_FIRST)
+ lua_pop(L, 1);
+ else if(mode == RUN_CALLBACKS_MODE_LAST)
+ lua_replace(L, rv);
+ else if(mode == RUN_CALLBACKS_MODE_AND ||
+ mode == RUN_CALLBACKS_MODE_AND_SC){
+ if((bool)lua_toboolean(L, rv) == true &&
+ (bool)lua_toboolean(L, -1) == false)
+ lua_replace(L, rv);
+ else
+ lua_pop(L, 1);
+ }
+ else if(mode == RUN_CALLBACKS_MODE_OR ||
+ mode == RUN_CALLBACKS_MODE_OR_SC){
+ if((bool)lua_toboolean(L, rv) == false &&
+ (bool)lua_toboolean(L, -1) == true)
+ lua_replace(L, rv);
+ else
+ lua_pop(L, 1);
+ }
+ else
+ assert(0);
+ }
+
+ // Handle short circuit modes
+ if(mode == RUN_CALLBACKS_MODE_AND_SC &&
+ (bool)lua_toboolean(L, rv) == false)
+ break;
+ else if(mode == RUN_CALLBACKS_MODE_OR_SC &&
+ (bool)lua_toboolean(L, rv) == true)
+ break;
+
+ // value removed, keep key for next iteration
+ }
+
+ // Remove stuff from stack, leaving only the return value
+ lua_settop(L, rv);
+
+ // Fix return value in case no callbacks were called
+ if(first_loop){
+ if(mode == RUN_CALLBACKS_MODE_AND ||
+ mode == RUN_CALLBACKS_MODE_AND_SC){
+ lua_pop(L, 1);
+ lua_pushboolean(L, true);
+ }
+ else if(mode == RUN_CALLBACKS_MODE_OR ||
+ mode == RUN_CALLBACKS_MODE_OR_SC){
+ lua_pop(L, 1);
+ lua_pushboolean(L, false);
+ }
+ }
+}
+
+void ScriptApiBase::addObjectReference(ServerActiveObject *cobj)
+{
+ SCRIPTAPI_PRECHECKHEADER
+ //infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
+
+ // Create object on stack
+ ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
+ int object = lua_gettop(L);
+
+ // Get minetest.object_refs table
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "object_refs");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ int objectstable = lua_gettop(L);
+
+ // object_refs[id] = object
+ lua_pushnumber(L, cobj->getId()); // Push id
+ lua_pushvalue(L, object); // Copy object to top of stack
+ lua_settable(L, objectstable);
+}
+
+void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj)
+{
+ SCRIPTAPI_PRECHECKHEADER
+ //infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
+
+ // Get minetest.object_refs table
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "object_refs");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ int objectstable = lua_gettop(L);
+
+ // Get object_refs[id]
+ lua_pushnumber(L, cobj->getId()); // Push id
+ lua_gettable(L, objectstable);
+ // Set object reference to NULL
+ ObjectRef::set_null(L);
+ lua_pop(L, 1); // pop object
+
+ // Set object_refs[id] = nil
+ lua_pushnumber(L, cobj->getId()); // Push id
+ lua_pushnil(L);
+ lua_settable(L, objectstable);
+}
+
+// Creates a new anonymous reference if cobj=NULL or id=0
+void ScriptApiBase::objectrefGetOrCreate(
+ ServerActiveObject *cobj)
+{
+ lua_State *L = getStack();
+
+ if(cobj == NULL || cobj->getId() == 0){
+ ObjectRef::create(L, cobj);
+ } else {
+ objectrefGet(cobj->getId());
+ }
+}
+
+void ScriptApiBase::objectrefGet(u16 id)
+{
+ lua_State *L = getStack();
+
+ // Get minetest.object_refs[i]
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "object_refs");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_pushnumber(L, id);
+ lua_gettable(L, -2);
+ lua_remove(L, -2); // object_refs
+ lua_remove(L, -2); // minetest
+}
diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h
new file mode 100644
index 000000000..da4e17d89
--- /dev/null
+++ b/src/script/cpp_api/s_base.h
@@ -0,0 +1,167 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_BASE_H_
+#define S_BASE_H_
+
+#include <iostream>
+
+#include "irrlichttypes.h"
+#include "jmutex.h"
+#include "jmutexautolock.h"
+#include "common/c_types.h"
+#include "debug.h"
+
+#define LOCK_DEBUG
+
+class Server;
+class Environment;
+class ServerActiveObject;
+class LuaABM;
+class InvRef;
+class ModApiBase;
+class ModApiEnvMod;
+class ObjectRef;
+class NodeMetaRef;
+
+
+/* definitions */
+// 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.
+enum RunCallbacksMode
+{
+ // Returns the return value of the first callback
+ // Returns nil if list of callbacks is empty
+ RUN_CALLBACKS_MODE_FIRST,
+ // Returns the return value of the last callback
+ // Returns nil if list of callbacks is empty
+ RUN_CALLBACKS_MODE_LAST,
+ // If any callback returns a false value, the first such is returned
+ // Otherwise, the first callback's return value (trueish) is returned
+ // Returns true if list of callbacks is empty
+ RUN_CALLBACKS_MODE_AND,
+ // Like above, but stops calling callbacks (short circuit)
+ // after seeing the first false value
+ RUN_CALLBACKS_MODE_AND_SC,
+ // If any callback returns a true value, the first such is returned
+ // Otherwise, the first callback's return value (falseish) is returned
+ // Returns false if list of callbacks is empty
+ RUN_CALLBACKS_MODE_OR,
+ // Like above, but stops calling callbacks (short circuit)
+ // after seeing the first true value
+ RUN_CALLBACKS_MODE_OR_SC,
+ // Note: "a true value" and "a false value" refer to values that
+ // are converted by lua_toboolean to true or false, respectively.
+};
+
+
+class ScriptApiBase {
+public:
+
+ /* object */
+ void addObjectReference(ServerActiveObject *cobj);
+ void removeObjectReference(ServerActiveObject *cobj);
+
+ ScriptApiBase();
+
+protected:
+ friend class LuaABM;
+ friend class InvRef;
+ friend class ObjectRef;
+ friend class NodeMetaRef;
+ friend class ModApiBase;
+ friend class ModApiEnvMod;
+
+
+ inline lua_State* getStack()
+ { return m_luastack; }
+
+ bool setStack(lua_State* stack) {
+ if (m_luastack == 0) {
+ m_luastack = stack;
+ return true;
+ }
+ return false;
+ }
+
+ void realityCheck();
+ void scriptError(const char *fmt, ...);
+ void stackDump(std::ostream &o);
+ void runCallbacks(int nargs,RunCallbacksMode mode);
+
+ inline Server* getServer() { return m_server; }
+ void setServer(Server* server) { m_server = server; }
+
+ Environment* getEnv() { return m_environment; }
+ void setEnv(Environment* env) { m_environment = env; }
+
+ void objectrefGetOrCreate(ServerActiveObject *cobj);
+ void objectrefGet(u16 id);
+
+ JMutex m_luastackmutex;
+#ifdef LOCK_DEBUG
+ bool m_locked;
+#endif
+private:
+ lua_State* m_luastack;
+
+ Server* m_server;
+ Environment* m_environment;
+
+
+};
+
+#ifdef LOCK_DEBUG
+class LockChecker {
+public:
+ LockChecker(bool* variable) {
+ assert(*variable == false);
+
+ m_variable = variable;
+ *m_variable = true;
+ }
+ ~LockChecker() {
+ *m_variable = false;
+ }
+private:
+bool* m_variable;
+};
+
+#define LOCK_CHECK LockChecker(&(this->m_locked))
+#else
+#define LOCK_CHECK while(0)
+#endif
+
+#define LUA_STACK_AUTOLOCK JMutexAutoLock(this->m_luastackmutex)
+
+#define SCRIPTAPI_PRECHECKHEADER \
+ LUA_STACK_AUTOLOCK; \
+ LOCK_CHECK; \
+ realityCheck(); \
+ lua_State *L = getStack(); \
+ assert(lua_checkstack(L, 20)); \
+ StackUnroller stack_unroller(L);
+
+#define PLAYER_TO_SA(p) p->getEnv()->getScriptIface()
+#define ENV_TO_SA(env) env->getScriptIface()
+#define SERVER_TO_SA(srv) srv->getScriptIface()
+
+#endif /* S_BASE_H_ */
diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp
new file mode 100644
index 000000000..2a5a6066d
--- /dev/null
+++ b/src/script/cpp_api/s_entity.cpp
@@ -0,0 +1,267 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cpp_api/s_entity.h"
+#include "log.h"
+#include "object_properties.h"
+#include "common/c_converter.h"
+#include "common/c_content.h"
+
+extern "C" {
+#include "lauxlib.h"
+}
+
+bool ScriptApiEntity::luaentity_Add(u16 id, const char *name)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ verbosestream<<"scriptapi_luaentity_add: id="<<id<<" name=\""
+ <<name<<"\""<<std::endl;
+
+ // Get minetest.registered_entities[name]
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_entities");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_pushstring(L, name);
+ lua_gettable(L, -2);
+ // Should be a table, which we will use as a prototype
+ //luaL_checktype(L, -1, LUA_TTABLE);
+ if(lua_type(L, -1) != LUA_TTABLE){
+ errorstream<<"LuaEntity name \""<<name<<"\" not defined"<<std::endl;
+ return false;
+ }
+ int prototype_table = lua_gettop(L);
+ //dump2(L, "prototype_table");
+
+ // Create entity object
+ lua_newtable(L);
+ int object = lua_gettop(L);
+
+ // Set object metatable
+ lua_pushvalue(L, prototype_table);
+ lua_setmetatable(L, -2);
+
+ // Add object reference
+ // This should be userdata with metatable ObjectRef
+ objectrefGet(id);
+ luaL_checktype(L, -1, LUA_TUSERDATA);
+ if(!luaL_checkudata(L, -1, "ObjectRef"))
+ luaL_typerror(L, -1, "ObjectRef");
+ lua_setfield(L, -2, "object");
+
+ // minetest.luaentities[id] = object
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "luaentities");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_pushnumber(L, id); // Push id
+ lua_pushvalue(L, object); // Copy object to top of stack
+ lua_settable(L, -3);
+
+ return true;
+}
+
+void ScriptApiEntity::luaentity_Activate(u16 id,
+ const std::string &staticdata, u32 dtime_s)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ verbosestream<<"scriptapi_luaentity_activate: id="<<id<<std::endl;
+
+ // Get minetest.luaentities[id]
+ luaentity_get(L,id);
+ int object = lua_gettop(L);
+
+ // Get on_activate function
+ lua_pushvalue(L, object);
+ lua_getfield(L, -1, "on_activate");
+ if(!lua_isnil(L, -1)){
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ lua_pushvalue(L, object); // self
+ lua_pushlstring(L, staticdata.c_str(), staticdata.size());
+ lua_pushinteger(L, dtime_s);
+ // Call with 3 arguments, 0 results
+ if(lua_pcall(L, 3, 0, 0))
+ scriptError("error running function on_activate: %s\n",
+ lua_tostring(L, -1));
+ }
+}
+
+void ScriptApiEntity::luaentity_Remove(u16 id)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ verbosestream<<"scriptapi_luaentity_rm: id="<<id<<std::endl;
+
+ // Get minetest.luaentities table
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "luaentities");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ int objectstable = lua_gettop(L);
+
+ // Set luaentities[id] = nil
+ lua_pushnumber(L, id); // Push id
+ lua_pushnil(L);
+ lua_settable(L, objectstable);
+
+ lua_pop(L, 2); // pop luaentities, minetest
+}
+
+std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ //infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl;
+
+ // Get minetest.luaentities[id]
+ luaentity_get(L,id);
+ int object = lua_gettop(L);
+
+ // Get get_staticdata function
+ lua_pushvalue(L, object);
+ lua_getfield(L, -1, "get_staticdata");
+ if(lua_isnil(L, -1))
+ return "";
+
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ lua_pushvalue(L, object); // self
+ // Call with 1 arguments, 1 results
+ if(lua_pcall(L, 1, 1, 0))
+ scriptError("error running function get_staticdata: %s\n",
+ lua_tostring(L, -1));
+
+ size_t len=0;
+ const char *s = lua_tolstring(L, -1, &len);
+ return std::string(s, len);
+}
+
+void ScriptApiEntity::luaentity_GetProperties(u16 id,
+ ObjectProperties *prop)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ //infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl;
+
+ // Get minetest.luaentities[id]
+ luaentity_get(L,id);
+ //int object = lua_gettop(L);
+
+ // Set default values that differ from ObjectProperties defaults
+ prop->hp_max = 10;
+
+ /* Read stuff */
+
+ prop->hp_max = getintfield_default(L, -1, "hp_max", 10);
+
+ getboolfield(L, -1, "physical", prop->physical);
+
+ getfloatfield(L, -1, "weight", prop->weight);
+
+ lua_getfield(L, -1, "collisionbox");
+ if(lua_istable(L, -1))
+ prop->collisionbox = read_aabb3f(L, -1, 1.0);
+ lua_pop(L, 1);
+
+ getstringfield(L, -1, "visual", prop->visual);
+
+ getstringfield(L, -1, "mesh", prop->mesh);
+
+ // Deprecated: read object properties directly
+ read_object_properties(L, -1, prop);
+
+ // Read initial_properties
+ lua_getfield(L, -1, "initial_properties");
+ read_object_properties(L, -1, prop);
+ lua_pop(L, 1);
+}
+
+void ScriptApiEntity::luaentity_Step(u16 id, float dtime)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+
+ // Get minetest.luaentities[id]
+ luaentity_get(L,id);
+ int object = lua_gettop(L);
+ // State: object is at top of stack
+ // Get step function
+ lua_getfield(L, -1, "on_step");
+ if(lua_isnil(L, -1))
+ return;
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ lua_pushvalue(L, object); // self
+ lua_pushnumber(L, dtime); // dtime
+ // Call with 2 arguments, 0 results
+ if(lua_pcall(L, 2, 0, 0))
+ scriptError("error running function 'on_step': %s\n", lua_tostring(L, -1));
+}
+
+// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch,
+// tool_capabilities, direction)
+void ScriptApiEntity::luaentity_Punch(u16 id,
+ ServerActiveObject *puncher, float time_from_last_punch,
+ const ToolCapabilities *toolcap, v3f dir)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+
+ // Get minetest.luaentities[id]
+ luaentity_get(L,id);
+ int object = lua_gettop(L);
+ // State: object is at top of stack
+ // Get function
+ lua_getfield(L, -1, "on_punch");
+ if(lua_isnil(L, -1))
+ return;
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ lua_pushvalue(L, object); // self
+ objectrefGetOrCreate(puncher); // Clicker reference
+ lua_pushnumber(L, time_from_last_punch);
+ push_tool_capabilities(L, *toolcap);
+ push_v3f(L, dir);
+ // Call with 5 arguments, 0 results
+ if(lua_pcall(L, 5, 0, 0))
+ scriptError("error running function 'on_punch': %s\n", lua_tostring(L, -1));
+}
+
+// Calls entity:on_rightclick(ObjectRef clicker)
+void ScriptApiEntity::luaentity_Rightclick(u16 id,
+ ServerActiveObject *clicker)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
+
+ // Get minetest.luaentities[id]
+ luaentity_get(L,id);
+ int object = lua_gettop(L);
+ // State: object is at top of stack
+ // Get function
+ lua_getfield(L, -1, "on_rightclick");
+ if(lua_isnil(L, -1))
+ return;
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ lua_pushvalue(L, object); // self
+ objectrefGetOrCreate(clicker); // Clicker reference
+ // Call with 2 arguments, 0 results
+ if(lua_pcall(L, 2, 0, 0))
+ scriptError("error running function 'on_rightclick': %s\n", lua_tostring(L, -1));
+}
+
diff --git a/src/script/cpp_api/s_entity.h b/src/script/cpp_api/s_entity.h
new file mode 100644
index 000000000..b95b6b4b4
--- /dev/null
+++ b/src/script/cpp_api/s_entity.h
@@ -0,0 +1,50 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_ENTITY_H_
+#define S_ENTITY_H_
+
+#include "cpp_api/s_base.h"
+#include "irr_v3d.h"
+
+class ObjectProperties;
+class ToolCapabilities;
+
+class ScriptApiEntity
+ : virtual public ScriptApiBase
+{
+public:
+ bool luaentity_Add(u16 id, const char *name);
+ void luaentity_Activate(u16 id,
+ const std::string &staticdata, u32 dtime_s);
+ void luaentity_Remove(u16 id);
+ std::string luaentity_GetStaticdata(u16 id);
+ void luaentity_GetProperties(u16 id,
+ ObjectProperties *prop);
+ void luaentity_Step(u16 id, float dtime);
+ void luaentity_Punch(u16 id,
+ ServerActiveObject *puncher, float time_from_last_punch,
+ const ToolCapabilities *toolcap, v3f dir);
+ void luaentity_Rightclick(u16 id,
+ ServerActiveObject *clicker);
+};
+
+
+
+#endif /* S_ENTITY_H_ */
diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp
new file mode 100644
index 000000000..334d196bc
--- /dev/null
+++ b/src/script/cpp_api/s_env.cpp
@@ -0,0 +1,132 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cpp_api/s_env.h"
+#include "common/c_converter.h"
+#include "log.h"
+#include "environment.h"
+#include "lua_api/l_env.h"
+
+extern "C" {
+#include "lauxlib.h"
+}
+
+void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
+ u32 blockseed)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_generateds
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_generateds");
+ // Call callbacks
+ push_v3s16(L, minp);
+ push_v3s16(L, maxp);
+ lua_pushnumber(L, blockseed);
+ runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void ScriptApiEnv::environment_Step(float dtime)
+{
+ SCRIPTAPI_PRECHECKHEADER
+ //infostream<<"scriptapi_environment_step"<<std::endl;
+
+ // Get minetest.registered_globalsteps
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_globalsteps");
+ // Call callbacks
+ lua_pushnumber(L, dtime);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
+{
+ SCRIPTAPI_PRECHECKHEADER
+ verbosestream<<"scriptapi_add_environment"<<std::endl;
+ setEnv(env);
+
+ /*
+ Add ActiveBlockModifiers to environment
+ */
+
+ // Get minetest.registered_abms
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_abms");
+ luaL_checktype(L, -1, LUA_TTABLE);
+ int registered_abms = lua_gettop(L);
+
+ if(lua_istable(L, registered_abms)){
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while(lua_next(L, table) != 0){
+ // key at index -2 and value at index -1
+ int id = lua_tonumber(L, -2);
+ int current_abm = lua_gettop(L);
+
+ std::set<std::string> trigger_contents;
+ lua_getfield(L, current_abm, "nodenames");
+ if(lua_istable(L, -1)){
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while(lua_next(L, table) != 0){
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ trigger_contents.insert(lua_tostring(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ } else if(lua_isstring(L, -1)){
+ trigger_contents.insert(lua_tostring(L, -1));
+ }
+ lua_pop(L, 1);
+
+ std::set<std::string> required_neighbors;
+ lua_getfield(L, current_abm, "neighbors");
+ if(lua_istable(L, -1)){
+ int table = lua_gettop(L);
+ lua_pushnil(L);
+ while(lua_next(L, table) != 0){
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ required_neighbors.insert(lua_tostring(L, -1));
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ } else if(lua_isstring(L, -1)){
+ required_neighbors.insert(lua_tostring(L, -1));
+ }
+ lua_pop(L, 1);
+
+ float trigger_interval = 10.0;
+ getfloatfield(L, current_abm, "interval", trigger_interval);
+
+ int trigger_chance = 50;
+ getintfield(L, current_abm, "chance", trigger_chance);
+
+ LuaABM *abm = new LuaABM(L, id, trigger_contents,
+ required_neighbors, trigger_interval, trigger_chance);
+
+ env->addActiveBlockModifier(abm);
+
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ }
+ lua_pop(L, 1);
+}
diff --git a/src/script/cpp_api/s_env.h b/src/script/cpp_api/s_env.h
new file mode 100644
index 000000000..c5b739eb4
--- /dev/null
+++ b/src/script/cpp_api/s_env.h
@@ -0,0 +1,40 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_ENV_H_
+#define S_ENV_H_
+
+#include "cpp_api/s_base.h"
+#include "irr_v3d.h"
+
+class ServerEnvironment;
+
+class ScriptApiEnv
+ : virtual public ScriptApiBase
+{
+public:
+ // On environment step
+ void environment_Step(float dtime);
+ // After generating a piece of map
+ void environment_OnGenerated(v3s16 minp, v3s16 maxp,u32 blockseed);
+
+ void initializeEnvironment(ServerEnvironment *env);
+};
+
+#endif /* S_ENV_H_ */
diff --git a/src/script/cpp_api/s_inventory.cpp b/src/script/cpp_api/s_inventory.cpp
new file mode 100644
index 000000000..2402d198c
--- /dev/null
+++ b/src/script/cpp_api/s_inventory.cpp
@@ -0,0 +1,260 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cpp_api/s_inventory.h"
+#include "inventorymanager.h"
+#include "lua_api/l_inventory.h"
+#include "lua_api/l_item.h"
+#include "log.h"
+
+// Return number of accepted items to be moved
+int ScriptApiDetached::detached_inventory_AllowMove(
+ const std::string &name,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getDetachedInventoryCallback(name, "allow_move"))
+ return count;
+
+ // function(inv, from_list, from_index, to_list, to_index, count, player)
+ // inv
+ InventoryLocation loc;
+ loc.setDetached(name);
+ InvRef::create(L, loc);
+ // from_list
+ lua_pushstring(L, from_list.c_str());
+ // from_index
+ lua_pushinteger(L, from_index + 1);
+ // to_list
+ lua_pushstring(L, to_list.c_str());
+ // to_index
+ lua_pushinteger(L, to_index + 1);
+ // count
+ lua_pushinteger(L, count);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 7, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnumber(L, -1))
+ throw LuaError(L, "allow_move should return a number");
+ return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be put
+int ScriptApiDetached::detached_inventory_AllowPut(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getDetachedInventoryCallback(name, "allow_put"))
+ return stack.count; // All will be accepted
+
+ // Call function(inv, listname, index, stack, player)
+ // inv
+ InventoryLocation loc;
+ loc.setDetached(name);
+ InvRef::create(L, loc);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnumber(L, -1))
+ throw LuaError(L, "allow_put should return a number");
+ return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be taken
+int ScriptApiDetached::detached_inventory_AllowTake(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getDetachedInventoryCallback(name, "allow_take"))
+ return stack.count; // All will be accepted
+
+ // Call function(inv, listname, index, stack, player)
+ // inv
+ InventoryLocation loc;
+ loc.setDetached(name);
+ InvRef::create(L, loc);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnumber(L, -1))
+ throw LuaError(L, "allow_take should return a number");
+ return luaL_checkinteger(L, -1);
+}
+
+// Report moved items
+void ScriptApiDetached::detached_inventory_OnMove(
+ const std::string &name,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getDetachedInventoryCallback(name, "on_move"))
+ return;
+
+ // function(inv, from_list, from_index, to_list, to_index, count, player)
+ // inv
+ InventoryLocation loc;
+ loc.setDetached(name);
+ InvRef::create(L, loc);
+ // from_list
+ lua_pushstring(L, from_list.c_str());
+ // from_index
+ lua_pushinteger(L, from_index + 1);
+ // to_list
+ lua_pushstring(L, to_list.c_str());
+ // to_index
+ lua_pushinteger(L, to_index + 1);
+ // count
+ lua_pushinteger(L, count);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 7, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+// Report put items
+void ScriptApiDetached::detached_inventory_OnPut(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getDetachedInventoryCallback(name, "on_put"))
+ return;
+
+ // Call function(inv, listname, index, stack, player)
+ // inv
+ InventoryLocation loc;
+ loc.setDetached(name);
+ InvRef::create(L, loc);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+// Report taken items
+void ScriptApiDetached::detached_inventory_OnTake(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getDetachedInventoryCallback(name, "on_take"))
+ return;
+
+ // Call function(inv, listname, index, stack, player)
+ // inv
+ InventoryLocation loc;
+ loc.setDetached(name);
+ InvRef::create(L, loc);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+// Retrieves minetest.detached_inventories[name][callbackname]
+// If that is nil or on error, return false and stack is unchanged
+// If that is a function, returns true and pushes the
+// function onto the stack
+bool ScriptApiDetached::getDetachedInventoryCallback(
+ const std::string &name, const char *callbackname)
+{
+ lua_State *L = getStack();
+
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "detached_inventories");
+ lua_remove(L, -2);
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_getfield(L, -1, name.c_str());
+ lua_remove(L, -2);
+ // Should be a table
+ if(lua_type(L, -1) != LUA_TTABLE)
+ {
+ errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
+ lua_pop(L, 1);
+ return false;
+ }
+ lua_getfield(L, -1, callbackname);
+ lua_remove(L, -2);
+ // Should be a function or nil
+ if(lua_type(L, -1) == LUA_TFUNCTION)
+ {
+ return true;
+ }
+ else if(lua_isnil(L, -1))
+ {
+ lua_pop(L, 1);
+ return false;
+ }
+ else
+ {
+ errorstream<<"Detached inventory \""<<name<<"\" callback \""
+ <<callbackname<<"\" is not a function"<<std::endl;
+ lua_pop(L, 1);
+ return false;
+ }
+}
diff --git a/src/script/cpp_api/s_inventory.h b/src/script/cpp_api/s_inventory.h
new file mode 100644
index 000000000..bf3b5de85
--- /dev/null
+++ b/src/script/cpp_api/s_inventory.h
@@ -0,0 +1,71 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_INVENTORY_H_
+#define S_INVENTORY_H_
+
+#include "cpp_api/s_base.h"
+
+class ItemStack;
+
+class ScriptApiDetached
+ : virtual public ScriptApiBase
+{
+public:
+ /* Detached inventory callbacks */
+ // Return number of accepted items to be moved
+ int detached_inventory_AllowMove(
+ const std::string &name,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player);
+ // Return number of accepted items to be put
+ int detached_inventory_AllowPut(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+ // Return number of accepted items to be taken
+ int detached_inventory_AllowTake(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+ // Report moved items
+ void detached_inventory_OnMove(
+ const std::string &name,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player);
+ // Report put items
+ void detached_inventory_OnPut(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+ // Report taken items
+ void detached_inventory_OnTake(
+ const std::string &name,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+private:
+ bool getDetachedInventoryCallback(
+ const std::string &name, const char *callbackname);
+};
+
+
+
+#endif /* S_INVENTORY_H_ */
diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp
new file mode 100644
index 000000000..6937ebbeb
--- /dev/null
+++ b/src/script/cpp_api/s_item.cpp
@@ -0,0 +1,164 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cpp_api/s_item.h"
+#include "common/c_converter.h"
+#include "common/c_content.h"
+#include "lua_api/l_item.h"
+#include "server.h"
+
+bool ScriptApiItem::item_OnDrop(ItemStack &item,
+ ServerActiveObject *dropper, v3f pos)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getItemCallback(item.name.c_str(), "on_drop"))
+ return false;
+
+ // Call function
+ LuaItemStack::create(L, item);
+ objectrefGetOrCreate(dropper);
+ pushFloatPos(L, pos);
+ if(lua_pcall(L, 3, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnil(L, -1))
+ item = read_item(L,-1, getServer());
+ return true;
+}
+
+bool ScriptApiItem::item_OnPlace(ItemStack &item,
+ ServerActiveObject *placer, const PointedThing &pointed)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getItemCallback(item.name.c_str(), "on_place"))
+ return false;
+
+ // Call function
+ LuaItemStack::create(L, item);
+ objectrefGetOrCreate(placer);
+ pushPointedThing(pointed);
+ if(lua_pcall(L, 3, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnil(L, -1))
+ item = read_item(L,-1, getServer());
+ return true;
+}
+
+bool ScriptApiItem::item_OnUse(ItemStack &item,
+ ServerActiveObject *user, const PointedThing &pointed)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Push callback function on stack
+ if(!getItemCallback(item.name.c_str(), "on_use"))
+ return false;
+
+ // Call function
+ LuaItemStack::create(L, item);
+ objectrefGetOrCreate(user);
+ pushPointedThing(pointed);
+ if(lua_pcall(L, 3, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnil(L, -1))
+ item = read_item(L,-1, getServer());
+ return true;
+}
+
+// Retrieves minetest.registered_items[name][callbackname]
+// If that is nil or on error, return false and stack is unchanged
+// If that is a function, returns true and pushes the
+// function onto the stack
+// If minetest.registered_items[name] doesn't exist, minetest.nodedef_default
+// is tried instead so unknown items can still be manipulated to some degree
+bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname)
+{
+ lua_State* L = getStack();
+
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_items");
+ lua_remove(L, -2);
+ luaL_checktype(L, -1, LUA_TTABLE);
+ lua_getfield(L, -1, name);
+ lua_remove(L, -2);
+ // Should be a table
+ if(lua_type(L, -1) != LUA_TTABLE)
+ {
+ // Report error and clean up
+ errorstream<<"Item \""<<name<<"\" not defined"<<std::endl;
+ lua_pop(L, 1);
+
+ // Try minetest.nodedef_default instead
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "nodedef_default");
+ lua_remove(L, -2);
+ luaL_checktype(L, -1, LUA_TTABLE);
+ }
+ lua_getfield(L, -1, callbackname);
+ lua_remove(L, -2);
+ // Should be a function or nil
+ if(lua_type(L, -1) == LUA_TFUNCTION)
+ {
+ return true;
+ }
+ else if(lua_isnil(L, -1))
+ {
+ lua_pop(L, 1);
+ return false;
+ }
+ else
+ {
+ errorstream<<"Item \""<<name<<"\" callback \""
+ <<callbackname<<" is not a function"<<std::endl;
+ lua_pop(L, 1);
+ return false;
+ }
+}
+
+void ScriptApiItem::pushPointedThing(const PointedThing& pointed)
+{
+ lua_State* L = getStack();
+
+ lua_newtable(L);
+ if(pointed.type == POINTEDTHING_NODE)
+ {
+ lua_pushstring(L, "node");
+ lua_setfield(L, -2, "type");
+ push_v3s16(L, pointed.node_undersurface);
+ lua_setfield(L, -2, "under");
+ push_v3s16(L, pointed.node_abovesurface);
+ lua_setfield(L, -2, "above");
+ }
+ else if(pointed.type == POINTEDTHING_OBJECT)
+ {
+ lua_pushstring(L, "object");
+ lua_setfield(L, -2, "type");
+ objectrefGet(pointed.object_id);
+ lua_setfield(L, -2, "ref");
+ }
+ else
+ {
+ lua_pushstring(L, "nothing");
+ lua_setfield(L, -2, "type");
+ }
+}
+
+
diff --git a/src/script/cpp_api/s_item.h b/src/script/cpp_api/s_item.h
new file mode 100644
index 000000000..28ac444f6
--- /dev/null
+++ b/src/script/cpp_api/s_item.h
@@ -0,0 +1,55 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_ITEM_H_
+#define S_ITEM_H_
+
+#include "cpp_api/s_base.h"
+#include "irr_v3d.h"
+
+class PointedThing;
+class ItemStack;
+class ServerActiveObject;
+class ItemDefinition;
+class LuaItemStack;
+class ModApiItemMod;
+
+class ScriptApiItem
+: virtual public ScriptApiBase
+{
+public:
+ bool item_OnDrop(ItemStack &item,
+ ServerActiveObject *dropper, v3f pos);
+ bool item_OnPlace(ItemStack &item,
+ ServerActiveObject *placer, const PointedThing &pointed);
+ bool item_OnUse(ItemStack &item,
+ ServerActiveObject *user, const PointedThing &pointed);
+
+protected:
+ friend class LuaItemStack;
+ friend class ModApiItemMod;
+
+ bool getItemCallback(const char *name, const char *callbackname);
+private:
+ void pushPointedThing(const PointedThing& pointed);
+
+};
+
+
+#endif /* S_ITEM_H_ */
diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp
new file mode 100644
index 000000000..969e2f4d9
--- /dev/null
+++ b/src/script/cpp_api/s_node.cpp
@@ -0,0 +1,233 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cpp_api/s_node.h"
+#include "common/c_converter.h"
+#include "common/c_content.h"
+#include "nodedef.h"
+#include "server.h"
+
+
+struct EnumString ScriptApiNode::es_DrawType[] =
+ {
+ {NDT_NORMAL, "normal"},
+ {NDT_AIRLIKE, "airlike"},
+ {NDT_LIQUID, "liquid"},
+ {NDT_FLOWINGLIQUID, "flowingliquid"},
+ {NDT_GLASSLIKE, "glasslike"},
+ {NDT_GLASSLIKE_FRAMED, "glasslike_framed"},
+ {NDT_ALLFACES, "allfaces"},
+ {NDT_ALLFACES_OPTIONAL, "allfaces_optional"},
+ {NDT_TORCHLIKE, "torchlike"},
+ {NDT_SIGNLIKE, "signlike"},
+ {NDT_PLANTLIKE, "plantlike"},
+ {NDT_FENCELIKE, "fencelike"},
+ {NDT_RAILLIKE, "raillike"},
+ {NDT_NODEBOX, "nodebox"},
+ {0, NULL},
+ };
+
+struct EnumString ScriptApiNode::es_ContentParamType2[] =
+ {
+ {CPT2_NONE, "none"},
+ {CPT2_FULL, "full"},
+ {CPT2_FLOWINGLIQUID, "flowingliquid"},
+ {CPT2_FACEDIR, "facedir"},
+ {CPT2_WALLMOUNTED, "wallmounted"},
+ {0, NULL},
+ };
+
+struct EnumString ScriptApiNode::es_LiquidType[] =
+ {
+ {LIQUID_NONE, "none"},
+ {LIQUID_FLOWING, "flowing"},
+ {LIQUID_SOURCE, "source"},
+ {0, NULL},
+ };
+
+struct EnumString ScriptApiNode::es_ContentParamType[] =
+ {
+ {CPT_NONE, "none"},
+ {CPT_LIGHT, "light"},
+ {0, NULL},
+ };
+
+struct EnumString ScriptApiNode::es_NodeBoxType[] =
+ {
+ {NODEBOX_REGULAR, "regular"},
+ {NODEBOX_FIXED, "fixed"},
+ {NODEBOX_WALLMOUNTED, "wallmounted"},
+ {0, NULL},
+ };
+
+ScriptApiNode::ScriptApiNode() {
+}
+
+ScriptApiNode::~ScriptApiNode() {
+}
+
+bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
+ ServerActiveObject *puncher)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(), "on_punch"))
+ return false;
+
+ // Call function
+ push_v3s16(L, p);
+ pushnode(L, node, ndef);
+ objectrefGetOrCreate(puncher);
+ if(lua_pcall(L, 3, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ return true;
+}
+
+bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node,
+ ServerActiveObject *digger)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(), "on_dig"))
+ return false;
+
+ // Call function
+ push_v3s16(L, p);
+ pushnode(L, node, ndef);
+ objectrefGetOrCreate(digger);
+ if(lua_pcall(L, 3, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ return true;
+}
+
+void ScriptApiNode::node_on_construct(v3s16 p, MapNode node)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(), "on_construct"))
+ return;
+
+ // Call function
+ push_v3s16(L, p);
+ if(lua_pcall(L, 1, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(), "on_destruct"))
+ return;
+
+ // Call function
+ push_v3s16(L, p);
+ if(lua_pcall(L, 1, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(), "after_destruct"))
+ return;
+
+ // Call function
+ push_v3s16(L, p);
+ pushnode(L, node, ndef);
+ if(lua_pcall(L, 2, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(), "on_timer"))
+ return false;
+
+ // Call function
+ push_v3s16(L, p);
+ lua_pushnumber(L,dtime);
+ if(lua_pcall(L, 2, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if((bool)lua_isboolean(L,-1) && (bool)lua_toboolean(L,-1) == true)
+ return true;
+
+ return false;
+}
+
+void ScriptApiNode::node_on_receive_fields(v3s16 p,
+ const std::string &formname,
+ const std::map<std::string, std::string> &fields,
+ ServerActiveObject *sender)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // If node doesn't exist, we don't know what callback to call
+ MapNode node = getEnv()->getMap().getNodeNoEx(p);
+ if(node.getContent() == CONTENT_IGNORE)
+ return;
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(), "on_receive_fields"))
+ return;
+
+ // Call function
+ // param 1
+ push_v3s16(L, p);
+ // param 2
+ lua_pushstring(L, formname.c_str());
+ // param 3
+ lua_newtable(L);
+ for(std::map<std::string, std::string>::const_iterator
+ i = fields.begin(); i != fields.end(); i++){
+ const std::string &name = i->first;
+ const std::string &value = i->second;
+ lua_pushstring(L, name.c_str());
+ lua_pushlstring(L, value.c_str(), value.size());
+ lua_settable(L, -3);
+ }
+ // param 4
+ objectrefGetOrCreate(sender);
+ if(lua_pcall(L, 4, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h
new file mode 100644
index 000000000..bff6072b9
--- /dev/null
+++ b/src/script/cpp_api/s_node.h
@@ -0,0 +1,62 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_NODE_H_
+#define S_NODE_H_
+
+#include <map>
+
+#include "irr_v3d.h"
+#include "cpp_api/s_base.h"
+#include "cpp_api/s_nodemeta.h"
+
+class MapNode;
+class ServerActiveObject;
+
+class ScriptApiNode
+ : virtual public ScriptApiBase,
+ public ScriptApiNodemeta
+{
+public:
+ ScriptApiNode();
+ virtual ~ScriptApiNode();
+
+ bool node_on_punch(v3s16 p, MapNode node,
+ ServerActiveObject *puncher);
+ bool node_on_dig(v3s16 p, MapNode node,
+ ServerActiveObject *digger);
+ void node_on_construct(v3s16 p, MapNode node);
+ void node_on_destruct(v3s16 p, MapNode node);
+ void node_after_destruct(v3s16 p, MapNode node);
+ bool node_on_timer(v3s16 p, MapNode node, f32 dtime);
+ void node_on_receive_fields(v3s16 p,
+ const std::string &formname,
+ const std::map<std::string, std::string> &fields,
+ ServerActiveObject *sender);
+public:
+ static struct EnumString es_DrawType[];
+ static struct EnumString es_ContentParamType[];
+ static struct EnumString es_ContentParamType2[];
+ static struct EnumString es_LiquidType[];
+ static struct EnumString es_NodeBoxType[];
+};
+
+
+
+#endif /* S_NODE_H_ */
diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp
new file mode 100644
index 000000000..56cea8e5f
--- /dev/null
+++ b/src/script/cpp_api/s_nodemeta.cpp
@@ -0,0 +1,261 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cpp_api/s_nodemeta.h"
+#include "common/c_converter.h"
+#include "nodedef.h"
+#include "mapnode.h"
+#include "server.h"
+#include "lua_api/l_item.h"
+
+extern "C" {
+#include "lauxlib.h"
+}
+
+// Return number of accepted items to be moved
+int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // If node doesn't exist, we don't know what callback to call
+ MapNode node = getEnv()->getMap().getNodeNoEx(p);
+ if(node.getContent() == CONTENT_IGNORE)
+ return 0;
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(),
+ "allow_metadata_inventory_move"))
+ return count;
+
+ // function(pos, from_list, from_index, to_list, to_index, count, player)
+ // pos
+ push_v3s16(L, p);
+ // from_list
+ lua_pushstring(L, from_list.c_str());
+ // from_index
+ lua_pushinteger(L, from_index + 1);
+ // to_list
+ lua_pushstring(L, to_list.c_str());
+ // to_index
+ lua_pushinteger(L, to_index + 1);
+ // count
+ lua_pushinteger(L, count);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 7, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnumber(L, -1))
+ throw LuaError(L, "allow_metadata_inventory_move should return a number");
+ return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be put
+int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // If node doesn't exist, we don't know what callback to call
+ MapNode node = getEnv()->getMap().getNodeNoEx(p);
+ if(node.getContent() == CONTENT_IGNORE)
+ return 0;
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(),
+ "allow_metadata_inventory_put"))
+ return stack.count;
+
+ // Call function(pos, listname, index, stack, player)
+ // pos
+ push_v3s16(L, p);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnumber(L, -1))
+ throw LuaError(L, "allow_metadata_inventory_put should return a number");
+ return luaL_checkinteger(L, -1);
+}
+
+// Return number of accepted items to be taken
+int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // If node doesn't exist, we don't know what callback to call
+ MapNode node = getEnv()->getMap().getNodeNoEx(p);
+ if(node.getContent() == CONTENT_IGNORE)
+ return 0;
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(),
+ "allow_metadata_inventory_take"))
+ return stack.count;
+
+ // Call function(pos, listname, index, count, player)
+ // pos
+ push_v3s16(L, p);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ if(!lua_isnumber(L, -1))
+ throw LuaError(L, "allow_metadata_inventory_take should return a number");
+ return luaL_checkinteger(L, -1);
+}
+
+// Report moved items
+void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // If node doesn't exist, we don't know what callback to call
+ MapNode node = getEnv()->getMap().getNodeNoEx(p);
+ if(node.getContent() == CONTENT_IGNORE)
+ return;
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(),
+ "on_metadata_inventory_move"))
+ return;
+
+ // function(pos, from_list, from_index, to_list, to_index, count, player)
+ // pos
+ push_v3s16(L, p);
+ // from_list
+ lua_pushstring(L, from_list.c_str());
+ // from_index
+ lua_pushinteger(L, from_index + 1);
+ // to_list
+ lua_pushstring(L, to_list.c_str());
+ // to_index
+ lua_pushinteger(L, to_index + 1);
+ // count
+ lua_pushinteger(L, count);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 7, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+// Report put items
+void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // If node doesn't exist, we don't know what callback to call
+ MapNode node = getEnv()->getMap().getNodeNoEx(p);
+ if(node.getContent() == CONTENT_IGNORE)
+ return;
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(),
+ "on_metadata_inventory_put"))
+ return;
+
+ // Call function(pos, listname, index, stack, player)
+ // pos
+ push_v3s16(L, p);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+// Report taken items
+void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ INodeDefManager *ndef = getServer()->ndef();
+
+ // If node doesn't exist, we don't know what callback to call
+ MapNode node = getEnv()->getMap().getNodeNoEx(p);
+ if(node.getContent() == CONTENT_IGNORE)
+ return;
+
+ // Push callback function on stack
+ if(!getItemCallback(ndef->get(node).name.c_str(),
+ "on_metadata_inventory_take"))
+ return;
+
+ // Call function(pos, listname, index, stack, player)
+ // pos
+ push_v3s16(L, p);
+ // listname
+ lua_pushstring(L, listname.c_str());
+ // index
+ lua_pushinteger(L, index + 1);
+ // stack
+ LuaItemStack::create(L, stack);
+ // player
+ objectrefGetOrCreate(player);
+ if(lua_pcall(L, 5, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+ScriptApiNodemeta::ScriptApiNodemeta() {
+}
+
+ScriptApiNodemeta::~ScriptApiNodemeta() {
+}
+
+
+
diff --git a/src/script/cpp_api/s_nodemeta.h b/src/script/cpp_api/s_nodemeta.h
new file mode 100644
index 000000000..9be126c62
--- /dev/null
+++ b/src/script/cpp_api/s_nodemeta.h
@@ -0,0 +1,67 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_NODEMETA_H_
+#define S_NODEMETA_H_
+
+#include "cpp_api/s_base.h"
+#include "cpp_api/s_item.h"
+#include "irr_v3d.h"
+
+class ItemStack;
+
+class ScriptApiNodemeta
+ : virtual public ScriptApiBase,
+ public ScriptApiItem
+{
+public:
+ ScriptApiNodemeta();
+ virtual ~ScriptApiNodemeta();
+
+ // Return number of accepted items to be moved
+ int nodemeta_inventory_AllowMove(v3s16 p,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player);
+ // Return number of accepted items to be put
+ int nodemeta_inventory_AllowPut(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+ // Return number of accepted items to be taken
+ int nodemeta_inventory_AllowTake(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+ // Report moved items
+ void nodemeta_inventory_OnMove(v3s16 p,
+ const std::string &from_list, int from_index,
+ const std::string &to_list, int to_index,
+ int count, ServerActiveObject *player);
+ // Report put items
+ void nodemeta_inventory_OnPut(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+ // Report taken items
+ void nodemeta_inventory_OnTake(v3s16 p,
+ const std::string &listname, int index, ItemStack &stack,
+ ServerActiveObject *player);
+private:
+
+};
+
+#endif /* S_NODEMETA_H_ */
diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp
new file mode 100644
index 000000000..e736d745d
--- /dev/null
+++ b/src/script/cpp_api/s_player.cpp
@@ -0,0 +1,113 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "cpp_api/s_player.h"
+
+void ScriptApiPlayer::on_newplayer(ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_newplayers
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_newplayers");
+ // Call callbacks
+ objectrefGetOrCreate(player);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_dieplayers
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_dieplayers");
+ // Call callbacks
+ objectrefGetOrCreate(player);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
+}
+
+bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_respawnplayers
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_respawnplayers");
+ // Call callbacks
+ objectrefGetOrCreate(player);
+ runCallbacks(1, RUN_CALLBACKS_MODE_OR);
+ bool positioning_handled_by_some = lua_toboolean(L, -1);
+ return positioning_handled_by_some;
+}
+
+void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_joinplayers
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_joinplayers");
+ // Call callbacks
+ objectrefGetOrCreate(player);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_leaveplayers
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_leaveplayers");
+ // Call callbacks
+ objectrefGetOrCreate(player);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
+}
+
+void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player,
+ const std::string &formname,
+ const std::map<std::string, std::string> &fields)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_chat_messages
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_player_receive_fields");
+ // Call callbacks
+ // param 1
+ objectrefGetOrCreate(player);
+ // param 2
+ lua_pushstring(L, formname.c_str());
+ // param 3
+ lua_newtable(L);
+ for(std::map<std::string, std::string>::const_iterator
+ i = fields.begin(); i != fields.end(); i++){
+ const std::string &name = i->first;
+ const std::string &value = i->second;
+ lua_pushstring(L, name.c_str());
+ lua_pushlstring(L, value.c_str(), value.size());
+ lua_settable(L, -3);
+ }
+ runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC);
+}
+ScriptApiPlayer::~ScriptApiPlayer() {
+}
+
+
diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h
new file mode 100644
index 000000000..663e3c2ab
--- /dev/null
+++ b/src/script/cpp_api/s_player.h
@@ -0,0 +1,44 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef S_PLAYER_H_
+#define S_PLAYER_H_
+
+#include "cpp_api/s_base.h"
+
+
+class ScriptApiPlayer
+ : virtual public ScriptApiBase
+{
+public:
+ virtual ~ScriptApiPlayer();
+
+ void on_newplayer(ServerActiveObject *player);
+ void on_dieplayer(ServerActiveObject *player);
+ bool on_respawnplayer(ServerActiveObject *player);
+ void on_joinplayer(ServerActiveObject *player);
+ void on_leaveplayer(ServerActiveObject *player);
+
+ void on_playerReceiveFields(ServerActiveObject *player,
+ const std::string &formname,
+ const std::map<std::string, std::string> &fields);
+};
+
+
+#endif /* S_PLAYER_H_ */
diff --git a/src/script/cpp_api/scriptapi.cpp b/src/script/cpp_api/scriptapi.cpp
new file mode 100644
index 000000000..b6d376b1f
--- /dev/null
+++ b/src/script/cpp_api/scriptapi.cpp
@@ -0,0 +1,291 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+extern "C" {
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+}
+
+
+#include "scriptapi.h"
+#include "common/c_converter.h"
+#include "lua_api/l_base.h"
+#include "log.h"
+#include "mods.h"
+
+int script_ErrorHandler(lua_State *L) {
+ lua_getfield(L, LUA_GLOBALSINDEX, "debug");
+ if (!lua_istable(L, -1)) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ lua_getfield(L, -1, "traceback");
+ if (!lua_isfunction(L, -1)) {
+ lua_pop(L, 2);
+ return 1;
+ }
+ lua_pushvalue(L, 1);
+ lua_pushinteger(L, 2);
+ lua_call(L, 2, 1);
+ return 1;
+}
+
+
+bool ScriptApi::getAuth(const std::string &playername,
+ std::string *dst_password, std::set<std::string> *dst_privs)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ getAuthHandler();
+ lua_getfield(L, -1, "get_auth");
+ if(lua_type(L, -1) != LUA_TFUNCTION)
+ throw LuaError(L, "Authentication handler missing get_auth");
+ lua_pushstring(L, playername.c_str());
+ if(lua_pcall(L, 1, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+
+ // nil = login not allowed
+ if(lua_isnil(L, -1))
+ return false;
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+ std::string password;
+ bool found = getstringfield(L, -1, "password", password);
+ if(!found)
+ throw LuaError(L, "Authentication handler didn't return password");
+ if(dst_password)
+ *dst_password = password;
+
+ lua_getfield(L, -1, "privileges");
+ if(!lua_istable(L, -1))
+ throw LuaError(L,
+ "Authentication handler didn't return privilege table");
+ if(dst_privs)
+ readPrivileges(-1, *dst_privs);
+ lua_pop(L, 1);
+
+ return true;
+}
+
+void ScriptApi::getAuthHandler()
+{
+ lua_State *L = getStack();
+
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_auth_handler");
+ if(lua_isnil(L, -1)){
+ lua_pop(L, 1);
+ lua_getfield(L, -1, "builtin_auth_handler");
+ }
+ if(lua_type(L, -1) != LUA_TTABLE)
+ throw LuaError(L, "Authentication handler table not valid");
+}
+
+void ScriptApi::readPrivileges(int index,std::set<std::string> &result)
+{
+ lua_State *L = getStack();
+
+ result.clear();
+ lua_pushnil(L);
+ if(index < 0)
+ index -= 1;
+ while(lua_next(L, index) != 0){
+ // key at index -2 and value at index -1
+ std::string key = luaL_checkstring(L, -2);
+ bool value = lua_toboolean(L, -1);
+ if(value)
+ result.insert(key);
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+}
+
+void ScriptApi::createAuth(const std::string &playername,
+ const std::string &password)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ getAuthHandler();
+ lua_getfield(L, -1, "create_auth");
+ if(lua_type(L, -1) != LUA_TFUNCTION)
+ throw LuaError(L, "Authentication handler missing create_auth");
+ lua_pushstring(L, playername.c_str());
+ lua_pushstring(L, password.c_str());
+ if(lua_pcall(L, 2, 0, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+}
+
+bool ScriptApi::setPassword(const std::string &playername,
+ const std::string &password)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ getAuthHandler();
+ lua_getfield(L, -1, "set_password");
+ if(lua_type(L, -1) != LUA_TFUNCTION)
+ throw LuaError(L, "Authentication handler missing set_password");
+ lua_pushstring(L, playername.c_str());
+ lua_pushstring(L, password.c_str());
+ if(lua_pcall(L, 2, 1, 0))
+ scriptError("error: %s", lua_tostring(L, -1));
+ return lua_toboolean(L, -1);
+}
+
+bool ScriptApi::on_chat_message(const std::string &name,
+ const std::string &message)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get minetest.registered_on_chat_messages
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_chat_messages");
+ // Call callbacks
+ lua_pushstring(L, name.c_str());
+ lua_pushstring(L, message.c_str());
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
+ bool ate = lua_toboolean(L, -1);
+ return ate;
+}
+
+void ScriptApi::on_shutdown()
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get registered shutdown hooks
+ lua_getglobal(L, "minetest");
+ lua_getfield(L, -1, "registered_on_shutdown");
+ // Call callbacks
+ runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
+}
+
+bool ScriptApi::loadMod(const std::string &scriptpath,const std::string &modname)
+{
+ ModNameStorer modnamestorer(getStack(), modname);
+
+ if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){
+ errorstream<<"Error loading mod \""<<modname
+ <<"\": modname does not follow naming conventions: "
+ <<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
+ return false;
+ }
+
+ bool success = false;
+
+ try{
+ success = scriptLoad(scriptpath.c_str());
+ }
+ catch(LuaError &e){
+ errorstream<<"Error loading mod \""<<modname
+ <<"\": "<<e.what()<<std::endl;
+ }
+
+ return success;
+}
+
+ScriptApi::ScriptApi() {
+ assert("Invalid call to default constructor of scriptapi!" == 0);
+}
+
+ScriptApi::ScriptApi(Server* server)
+{
+
+ setServer(server);
+ setStack(luaL_newstate());
+ assert(getStack());
+
+ //TODO add security
+
+ luaL_openlibs(getStack());
+
+ SCRIPTAPI_PRECHECKHEADER
+
+ lua_pushlightuserdata(L, this);
+ lua_setfield(L, LUA_REGISTRYINDEX, "scriptapi");
+
+ lua_newtable(L);
+ lua_setglobal(L, "minetest");
+
+
+ for (std::vector<ModApiBase*>::iterator i = m_mod_api_modules->begin();
+ i != m_mod_api_modules->end(); i++) {
+ //initializers are called within minetest global table!
+ lua_getglobal(L, "minetest");
+ int top = lua_gettop(L);
+ bool ModInitializedSuccessfull = (*i)->Initialize(L,top);
+ assert(ModInitializedSuccessfull);
+ }
+
+ infostream << "SCRIPTAPI: initialized " << m_mod_api_modules->size()
+ << " modules" << std::endl;
+
+ // Get the main minetest table
+ lua_getglobal(L, "minetest");
+
+ // Add tables to minetest
+ lua_newtable(L);
+ lua_setfield(L, -2, "object_refs");
+
+ lua_newtable(L);
+ lua_setfield(L, -2, "luaentities");
+}
+
+ScriptApi::~ScriptApi() {
+ lua_close(getStack());
+}
+
+bool ScriptApi::scriptLoad(const char *path)
+{
+ lua_State* L = getStack();
+ setStack(0);
+
+ verbosestream<<"Loading and running script from "<<path<<std::endl;
+
+ lua_pushcfunction(L, script_ErrorHandler);
+ int errorhandler = lua_gettop(L);
+
+ int ret = luaL_loadfile(L, path) || lua_pcall(L, 0, 0, errorhandler);
+ if(ret){
+ errorstream<<"========== ERROR FROM LUA ==========="<<std::endl;
+ errorstream<<"Failed to load and run script from "<<std::endl;
+ errorstream<<path<<":"<<std::endl;
+ errorstream<<std::endl;
+ errorstream<<lua_tostring(L, -1)<<std::endl;
+ errorstream<<std::endl;
+ errorstream<<"=======END OF ERROR FROM LUA ========"<<std::endl;
+ lua_pop(L, 1); // Pop error message from stack
+ lua_pop(L, 1); // Pop the error handler from stack
+ return false;
+ }
+ lua_pop(L, 1); // Pop the error handler from stack
+ return true;
+}
+
+bool ScriptApi::registerModApiModule(ModApiBase* ptr) {
+ if (ScriptApi::m_mod_api_modules == 0)
+ ScriptApi::m_mod_api_modules = new std::vector<ModApiBase*>();
+
+ assert(ScriptApi::m_mod_api_modules != 0);
+
+ ScriptApi::m_mod_api_modules->push_back(ptr);
+
+ return true;
+}
+
+std::vector<ModApiBase*>* ScriptApi::m_mod_api_modules = 0;
diff --git a/src/script/cpp_api/scriptapi.h b/src/script/cpp_api/scriptapi.h
new file mode 100644
index 000000000..bbd0bdda7
--- /dev/null
+++ b/src/script/cpp_api/scriptapi.h
@@ -0,0 +1,82 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef SCRIPTAPI_H_
+#define SCRIPTAPI_H_
+
+#include <map>
+#include <set>
+#include <vector>
+
+#include "cpp_api/s_base.h"
+#include "cpp_api/s_player.h"
+#include "cpp_api/s_env.h"
+#include "cpp_api/s_node.h"
+#include "cpp_api/s_inventory.h"
+#include "cpp_api/s_entity.h"
+
+class ModApiBase;
+
+/*****************************************************************************/
+/* Scriptapi <-> Core Interface */
+/*****************************************************************************/
+
+class ScriptApi
+ : virtual public ScriptApiBase,
+ public ScriptApiPlayer,
+ public ScriptApiEnv,
+ public ScriptApiNode,
+ public ScriptApiDetached,
+ public ScriptApiEntity
+{
+public:
+ ScriptApi();
+ ScriptApi(Server* server);
+ ~ScriptApi();
+
+ // Returns true if script handled message
+ bool on_chat_message(const std::string &name, const std::string &message);
+
+ /* server */
+ void on_shutdown();
+
+ /* auth */
+ bool getAuth(const std::string &playername,
+ std::string *dst_password, std::set<std::string> *dst_privs);
+ void createAuth(const std::string &playername,
+ const std::string &password);
+ bool setPassword(const std::string &playername,
+ const std::string &password);
+
+ /** register a lua api module to scriptapi */
+ static bool registerModApiModule(ModApiBase* prototype);
+ /** load a mod **/
+ bool loadMod(const std::string &scriptpath,const std::string &modname);
+
+private:
+ void getAuthHandler();
+ void readPrivileges(int index,std::set<std::string> &result);
+
+ bool scriptLoad(const char *path);
+
+ static std::vector<ModApiBase*>* m_mod_api_modules;
+
+};
+
+#endif /* SCRIPTAPI_H_ */