diff options
author | sapier <Sapier at GMX dot net> | 2013-05-25 00:51:02 +0200 |
---|---|---|
committer | Kahrl <kahrl@gmx.net> | 2013-05-25 00:51:02 +0200 |
commit | ab433775777c4f5055bcf4d2a1cffc506c4f9961 (patch) | |
tree | 9f80c74c95cbb1efa15c0c36df8d5e35235e5554 /src/script/cpp_api | |
parent | 865f380c91ced850d1a499c91aa2ab5489624802 (diff) | |
download | minetest-ab433775777c4f5055bcf4d2a1cffc506c4f9961.tar.gz minetest-ab433775777c4f5055bcf4d2a1cffc506c4f9961.tar.bz2 minetest-ab433775777c4f5055bcf4d2a1cffc506c4f9961.zip |
Move scriptapi to separate folder (by sapier)
On the lua side, notably minetest.env:<function>(<args>) should now
be replaced by minetest.<function>(<args>).
The old way is and will stay supported for a long time.
Also:
Update and clean up lua_api.txt (by celeron55)
Move EnvRef to lua and remove add_rat and add_firefly (by kahrl)
Add separate src/util/CMakeLists.txt, other minor fixes (by kahrl)
Diffstat (limited to 'src/script/cpp_api')
-rw-r--r-- | src/script/cpp_api/CMakeLists.txt | 11 | ||||
-rw-r--r-- | src/script/cpp_api/s_base.cpp | 264 | ||||
-rw-r--r-- | src/script/cpp_api/s_base.h | 167 | ||||
-rw-r--r-- | src/script/cpp_api/s_entity.cpp | 267 | ||||
-rw-r--r-- | src/script/cpp_api/s_entity.h | 50 | ||||
-rw-r--r-- | src/script/cpp_api/s_env.cpp | 132 | ||||
-rw-r--r-- | src/script/cpp_api/s_env.h | 40 | ||||
-rw-r--r-- | src/script/cpp_api/s_inventory.cpp | 260 | ||||
-rw-r--r-- | src/script/cpp_api/s_inventory.h | 71 | ||||
-rw-r--r-- | src/script/cpp_api/s_item.cpp | 164 | ||||
-rw-r--r-- | src/script/cpp_api/s_item.h | 55 | ||||
-rw-r--r-- | src/script/cpp_api/s_node.cpp | 233 | ||||
-rw-r--r-- | src/script/cpp_api/s_node.h | 62 | ||||
-rw-r--r-- | src/script/cpp_api/s_nodemeta.cpp | 261 | ||||
-rw-r--r-- | src/script/cpp_api/s_nodemeta.h | 67 | ||||
-rw-r--r-- | src/script/cpp_api/s_player.cpp | 113 | ||||
-rw-r--r-- | src/script/cpp_api/s_player.h | 44 | ||||
-rw-r--r-- | src/script/cpp_api/scriptapi.cpp | 291 | ||||
-rw-r--r-- | src/script/cpp_api/scriptapi.h | 82 |
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_ */ |