Add files via upload

This commit is contained in:
VoidCosmos 2021-09-19 16:04:31 +05:30 committed by GitHub
parent f37f0cf989
commit 2e17095765
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 9386 additions and 0 deletions

View File

@ -0,0 +1,11 @@
set(common_SCRIPT_COMMON_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/c_content.cpp
${CMAKE_CURRENT_SOURCE_DIR}/c_converter.cpp
${CMAKE_CURRENT_SOURCE_DIR}/c_types.cpp
${CMAKE_CURRENT_SOURCE_DIR}/c_internal.cpp
${CMAKE_CURRENT_SOURCE_DIR}/helper.cpp
PARENT_SCOPE)
set(client_SCRIPT_COMMON_SRCS
PARENT_SCOPE)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,208 @@
/*
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.
*/
/******************************************************************************/
/******************************************************************************/
/* WARNING!!!! do NOT add this header in any include file or any code file */
/* not being a script/modapi file!!!!!!!! */
/******************************************************************************/
/******************************************************************************/
#pragma once
extern "C" {
#include <lua.h>
}
#include <iostream>
#include <vector>
#include "irrlichttypes_bloated.h"
#include "util/string.h"
#include "itemgroup.h"
#include "itemdef.h"
#include "c_types.h"
#include "hud.h"
namespace Json { class Value; }
struct MapNode;
class NodeDefManager;
struct PointedThing;
struct ItemStack;
struct ItemDefinition;
struct ToolCapabilities;
struct ObjectProperties;
struct SimpleSoundSpec;
struct ServerSoundParams;
class Inventory;
struct NodeBox;
struct ContentFeatures;
struct TileDef;
class Server;
struct DigParams;
struct HitParams;
struct EnumString;
struct NoiseParams;
class Schematic;
class ServerActiveObject;
struct collisionMoveResult;
extern struct EnumString es_TileAnimationType[];
void read_content_features (lua_State *L, ContentFeatures &f,
int index);
void push_content_features (lua_State *L,
const ContentFeatures &c);
void push_nodebox (lua_State *L,
const NodeBox &box);
void push_box (lua_State *L,
const std::vector<aabb3f> &box);
void push_palette (lua_State *L,
const std::vector<video::SColor> *palette);
TileDef read_tiledef (lua_State *L, int index,
u8 drawtype);
void read_soundspec (lua_State *L, int index,
SimpleSoundSpec &spec);
NodeBox read_nodebox (lua_State *L, int index);
void read_server_sound_params (lua_State *L, int index,
ServerSoundParams &params);
void push_dig_params (lua_State *L,
const DigParams &params);
void push_hit_params (lua_State *L,
const HitParams &params);
ItemStack read_item (lua_State *L, int index, IItemDefManager *idef);
struct TileAnimationParams read_animation_definition(lua_State *L, int index);
void push_animation_definition(lua_State *L, struct TileAnimationParams anim);
ToolCapabilities read_tool_capabilities (lua_State *L, int table);
void push_tool_capabilities (lua_State *L,
const ToolCapabilities &prop);
void read_item_definition (lua_State *L, int index, const ItemDefinition &default_def,
ItemDefinition &def);
void push_item_definition (lua_State *L,
const ItemDefinition &i);
void push_item_definition_full (lua_State *L,
const ItemDefinition &i);
void read_object_properties (lua_State *L, int index,
ServerActiveObject *sao,
ObjectProperties *prop,
IItemDefManager *idef);
void push_object_properties (lua_State *L,
ObjectProperties *prop);
void push_inventory (lua_State *L,
Inventory *inventory);
void push_inventory_list (lua_State *L,
Inventory *inv,
const char *name);
void read_inventory_list (lua_State *L, int tableindex,
Inventory *inv, const char *name,
Server *srv, int forcesize=-1);
MapNode readnode (lua_State *L, int index,
const NodeDefManager *ndef);
void pushnode (lua_State *L, const MapNode &n,
const NodeDefManager *ndef);
void read_groups (lua_State *L, int index,
ItemGroupList &result);
void push_groups (lua_State *L,
const ItemGroupList &groups);
//TODO rename to "read_enum_field"
int getenumfield (lua_State *L, int table,
const char *fieldname,
const EnumString *spec,
int default_);
bool getflagsfield (lua_State *L, int table,
const char *fieldname,
FlagDesc *flagdesc,
u32 *flags, u32 *flagmask);
bool read_flags (lua_State *L, int index,
FlagDesc *flagdesc,
u32 *flags, u32 *flagmask);
void push_flags_string (lua_State *L, FlagDesc *flagdesc,
u32 flags, u32 flagmask);
u32 read_flags_table (lua_State *L, int table,
FlagDesc *flagdesc, u32 *flagmask);
void push_items (lua_State *L,
const std::vector<ItemStack> &items);
std::vector<ItemStack> read_items (lua_State *L,
int index,
Server* srv);
void push_soundspec (lua_State *L,
const SimpleSoundSpec &spec);
bool string_to_enum (const EnumString *spec,
int &result,
const std::string &str);
bool read_noiseparams (lua_State *L, int index,
NoiseParams *np);
void push_noiseparams (lua_State *L, NoiseParams *np);
void luaentity_get (lua_State *L,u16 id);
bool push_json_value (lua_State *L,
const Json::Value &value,
int nullindex);
void read_json_value (lua_State *L, Json::Value &root,
int index, u8 recursion = 0);
/*!
* Pushes a Lua `pointed_thing` to the given Lua stack.
* \param csm If true, a client side pointed thing is pushed
* \param hitpoint If true, the exact pointing location is also pushed
*/
void push_pointed_thing(lua_State *L, const PointedThing &pointed, bool csm =
false, bool hitpoint = false);
void push_objectRef (lua_State *L, const u16 id);
void read_hud_element (lua_State *L, HudElement *elem);
void push_hud_element (lua_State *L, HudElement *elem);
HudElementStat read_hud_change (lua_State *L, HudElement *elem, void **value);
void push_collision_move_result(lua_State *L, const collisionMoveResult &res);
void push_physics_override (lua_State *L, float speed, float jump, float gravity, bool sneak, bool sneak_glitch, bool new_move);

View File

@ -0,0 +1,681 @@
/*
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 "util/numeric.h"
#include "util/serialize.h"
#include "util/string.h"
#include "common/c_converter.h"
#include "common/c_internal.h"
#include "constants.h"
#include <set>
#define CHECK_TYPE(index, name, type) { \
int t = lua_type(L, (index)); \
if (t != (type)) { \
throw LuaError(std::string("Invalid ") + (name) + \
" (expected " + lua_typename(L, (type)) + \
" got " + lua_typename(L, t) + ")."); \
} \
}
#define CHECK_POS_COORD(name) CHECK_TYPE(-1, "position coordinate '" name "'", LUA_TNUMBER)
#define CHECK_FLOAT_RANGE(value, name) \
if (value < F1000_MIN || value > F1000_MAX) { \
std::ostringstream error_text; \
error_text << "Invalid float vector dimension range '" name "' " << \
"(expected " << F1000_MIN << " < " name " < " << F1000_MAX << \
" got " << value << ")." << std::endl; \
throw LuaError(error_text.str()); \
}
#define CHECK_POS_TAB(index) CHECK_TYPE(index, "position", LUA_TTABLE)
void push_float_string(lua_State *L, float value)
{
std::stringstream ss;
std::string str;
ss << value;
str = ss.str();
lua_pushstring(L, str.c_str());
}
void push_v3f(lua_State *L, v3f p)
{
lua_createtable(L, 0, 3);
lua_pushnumber(L, p.X);
lua_setfield(L, -2, "x");
lua_pushnumber(L, p.Y);
lua_setfield(L, -2, "y");
lua_pushnumber(L, p.Z);
lua_setfield(L, -2, "z");
}
void push_v2f(lua_State *L, v2f p)
{
lua_createtable(L, 0, 2);
lua_pushnumber(L, p.X);
lua_setfield(L, -2, "x");
lua_pushnumber(L, p.Y);
lua_setfield(L, -2, "y");
}
void push_v3_float_string(lua_State *L, v3f p)
{
lua_createtable(L, 0, 3);
push_float_string(L, p.X);
lua_setfield(L, -2, "x");
push_float_string(L, p.Y);
lua_setfield(L, -2, "y");
push_float_string(L, p.Z);
lua_setfield(L, -2, "z");
}
void push_v2_float_string(lua_State *L, v2f p)
{
lua_createtable(L, 0, 2);
push_float_string(L, p.X);
lua_setfield(L, -2, "x");
push_float_string(L, p.Y);
lua_setfield(L, -2, "y");
}
v2s16 read_v2s16(lua_State *L, int index)
{
v2s16 p;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
p.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
p.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
return p;
}
void push_v2s16(lua_State *L, v2s16 p)
{
lua_createtable(L, 0, 2);
lua_pushinteger(L, p.X);
lua_setfield(L, -2, "x");
lua_pushinteger(L, p.Y);
lua_setfield(L, -2, "y");
}
void push_v2s32(lua_State *L, v2s32 p)
{
lua_createtable(L, 0, 2);
lua_pushinteger(L, p.X);
lua_setfield(L, -2, "x");
lua_pushinteger(L, p.Y);
lua_setfield(L, -2, "y");
}
v2s32 read_v2s32(lua_State *L, int index)
{
v2s32 p;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
p.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
p.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
return p;
}
v2f read_v2f(lua_State *L, int index)
{
v2f p;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
p.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
p.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
return p;
}
v2f check_v2f(lua_State *L, int index)
{
v2f p;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
CHECK_POS_COORD("x");
p.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
CHECK_POS_COORD("y");
p.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
return p;
}
v3f read_v3f(lua_State *L, int index)
{
v3f pos;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
pos.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
pos.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "z");
pos.Z = lua_tonumber(L, -1);
lua_pop(L, 1);
return pos;
}
v3f check_v3f(lua_State *L, int index)
{
v3f pos;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
CHECK_POS_COORD("x");
pos.X = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.X, "x")
lua_pop(L, 1);
lua_getfield(L, index, "y");
CHECK_POS_COORD("y");
pos.Y = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.Y, "y")
lua_pop(L, 1);
lua_getfield(L, index, "z");
CHECK_POS_COORD("z");
pos.Z = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.Z, "z")
lua_pop(L, 1);
return pos;
}
v3d read_v3d(lua_State *L, int index)
{
v3d pos;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
pos.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
pos.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "z");
pos.Z = lua_tonumber(L, -1);
lua_pop(L, 1);
return pos;
}
v3d check_v3d(lua_State *L, int index)
{
v3d pos;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
CHECK_POS_COORD("x");
pos.X = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.X, "x")
lua_pop(L, 1);
lua_getfield(L, index, "y");
CHECK_POS_COORD("y");
pos.Y = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.Y, "y")
lua_pop(L, 1);
lua_getfield(L, index, "z");
CHECK_POS_COORD("z");
pos.Z = lua_tonumber(L, -1);
CHECK_FLOAT_RANGE(pos.Z, "z")
lua_pop(L, 1);
return pos;
}
void push_ARGB8(lua_State *L, video::SColor color)
{
lua_createtable(L, 0, 4);
lua_pushinteger(L, color.getAlpha());
lua_setfield(L, -2, "a");
lua_pushinteger(L, color.getRed());
lua_setfield(L, -2, "r");
lua_pushinteger(L, color.getGreen());
lua_setfield(L, -2, "g");
lua_pushinteger(L, color.getBlue());
lua_setfield(L, -2, "b");
}
void pushFloatPos(lua_State *L, v3f p)
{
p /= BS;
push_v3f(L, p);
}
v3f checkFloatPos(lua_State *L, int index)
{
return check_v3f(L, index) * BS;
}
void push_v3s16(lua_State *L, v3s16 p)
{
lua_createtable(L, 0, 3);
lua_pushinteger(L, p.X);
lua_setfield(L, -2, "x");
lua_pushinteger(L, p.Y);
lua_setfield(L, -2, "y");
lua_pushinteger(L, p.Z);
lua_setfield(L, -2, "z");
}
v3s16 read_v3s16(lua_State *L, int index)
{
// Correct rounding at <0
v3d pf = read_v3d(L, index);
return doubleToInt(pf, 1.0);
}
v3s16 check_v3s16(lua_State *L, int index)
{
// Correct rounding at <0
v3d pf = check_v3d(L, index);
return doubleToInt(pf, 1.0);
}
bool read_color(lua_State *L, int index, video::SColor *color)
{
if (lua_istable(L, index)) {
*color = read_ARGB8(L, index);
} else if (lua_isnumber(L, index)) {
color->set(lua_tonumber(L, index));
} else if (lua_isstring(L, index)) {
video::SColor parsed_color;
if (!parseColorString(lua_tostring(L, index), parsed_color, true))
return false;
*color = parsed_color;
} else {
return false;
}
return true;
}
video::SColor read_ARGB8(lua_State *L, int index)
{
video::SColor color(0);
CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
lua_getfield(L, index, "a");
color.setAlpha(lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0xFF);
lua_pop(L, 1);
lua_getfield(L, index, "r");
color.setRed(lua_tonumber(L, -1));
lua_pop(L, 1);
lua_getfield(L, index, "g");
color.setGreen(lua_tonumber(L, -1));
lua_pop(L, 1);
lua_getfield(L, index, "b");
color.setBlue(lua_tonumber(L, -1));
lua_pop(L, 1);
return color;
}
bool is_color_table(lua_State *L, int index)
{
// Check whole table in case of missing ColorSpec keys:
// This check does not remove the checked value from the stack.
// Only update the value if we know we have a valid ColorSpec key pair.
if (!lua_istable(L, index))
return false;
bool is_color_table = false;
lua_getfield(L, index, "r");
if (!is_color_table)
is_color_table = lua_isnumber(L, -1);
lua_getfield(L, index, "g");
if (!is_color_table)
is_color_table = lua_isnumber(L, -1);
lua_getfield(L, index, "b");
if (!is_color_table)
is_color_table = lua_isnumber(L, -1);
lua_pop(L, 3); // b, g, r values
return is_color_table;
}
aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
{
aabb3f box;
if(lua_istable(L, index)){
lua_rawgeti(L, index, 1);
box.MinEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
lua_rawgeti(L, index, 2);
box.MinEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
lua_rawgeti(L, index, 3);
box.MinEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
lua_rawgeti(L, index, 4);
box.MaxEdge.X = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
lua_rawgeti(L, index, 5);
box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
lua_rawgeti(L, index, 6);
box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
lua_pop(L, 1);
}
box.repair();
return box;
}
void push_aabb3f(lua_State *L, aabb3f box)
{
lua_createtable(L, 6, 0);
lua_pushnumber(L, box.MinEdge.X);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, box.MinEdge.Y);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, box.MinEdge.Z);
lua_rawseti(L, -2, 3);
lua_pushnumber(L, box.MaxEdge.X);
lua_rawseti(L, -2, 4);
lua_pushnumber(L, box.MaxEdge.Y);
lua_rawseti(L, -2, 5);
lua_pushnumber(L, box.MaxEdge.Z);
lua_rawseti(L, -2, 6);
}
std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
{
std::vector<aabb3f> boxes;
if(lua_istable(L, index)){
int n = lua_objlen(L, index);
// Check if it's a single box or a list of boxes
bool possibly_single_box = (n == 6);
for(int i = 1; i <= n && possibly_single_box; i++){
lua_rawgeti(L, index, i);
if(!lua_isnumber(L, -1))
possibly_single_box = false;
lua_pop(L, 1);
}
if(possibly_single_box){
// Read a single box
boxes.push_back(read_aabb3f(L, index, scale));
} else {
// Read a list of boxes
for(int i = 1; i <= n; i++){
lua_rawgeti(L, index, i);
boxes.push_back(read_aabb3f(L, -1, scale));
lua_pop(L, 1);
}
}
}
return boxes;
}
size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result)
{
if (index < 0)
index = lua_gettop(L) + 1 + index;
size_t num_strings = 0;
if (lua_istable(L, index)) {
lua_pushnil(L);
while (lua_next(L, index)) {
if (lua_isstring(L, -1)) {
result->push_back(lua_tostring(L, -1));
num_strings++;
}
lua_pop(L, 1);
}
} else if (lua_isstring(L, index)) {
result->push_back(lua_tostring(L, index));
num_strings++;
}
return num_strings;
}
/*
Table field getters
*/
#if defined(__MINGW32__) && !defined(__MINGW64__)
/* MinGW 32-bit somehow crashes in the std::set destructor when this
* variable is thread-local, so just don't do that. */
static std::set<u64> warned_msgs;
#endif
bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname)
{
#if !defined(__MINGW32__) || defined(__MINGW64__)
thread_local std::set<u64> warned_msgs;
#endif
int t = lua_type(L, index);
if (t == LUA_TNIL)
return false;
if (t == type)
return true;
// Check coercion types
if (type == LUA_TNUMBER) {
if (lua_isnumber(L, index))
return true;
} else if (type == LUA_TSTRING) {
if (lua_isstring(L, index))
return true;
}
// Types mismatch. Log unique line.
std::string backtrace = std::string("Invalid field ") + fieldname +
" (expected " + lua_typename(L, type) +
" got " + lua_typename(L, t) + ").\n" + script_get_backtrace(L);
u64 hash = murmur_hash_64_ua(backtrace.data(), backtrace.length(), 0xBADBABE);
if (warned_msgs.find(hash) == warned_msgs.end()) {
errorstream << backtrace << std::endl;
warned_msgs.insert(hash);
}
return false;
}
bool getstringfield(lua_State *L, int table,
const char *fieldname, std::string &result)
{
lua_getfield(L, table, fieldname);
bool got = false;
if (check_field_or_nil(L, -1, LUA_TSTRING, fieldname)) {
size_t len = 0;
const char *ptr = lua_tolstring(L, -1, &len);
if (ptr) {
result.assign(ptr, len);
got = true;
}
}
lua_pop(L, 1);
return got;
}
bool getfloatfield(lua_State *L, int table,
const char *fieldname, float &result)
{
lua_getfield(L, table, fieldname);
bool got = false;
if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)) {
result = lua_tonumber(L, -1);
got = true;
}
lua_pop(L, 1);
return got;
}
bool getboolfield(lua_State *L, int table,
const char *fieldname, bool &result)
{
lua_getfield(L, table, fieldname);
bool got = false;
if (check_field_or_nil(L, -1, LUA_TBOOLEAN, fieldname)){
result = lua_toboolean(L, -1);
got = true;
}
lua_pop(L, 1);
return got;
}
size_t getstringlistfield(lua_State *L, int table, const char *fieldname,
std::vector<std::string> *result)
{
lua_getfield(L, table, fieldname);
size_t num_strings_read = read_stringlist(L, -1, result);
lua_pop(L, 1);
return num_strings_read;
}
std::string getstringfield_default(lua_State *L, int table,
const char *fieldname, const std::string &default_)
{
std::string result = default_;
getstringfield(L, table, fieldname, result);
return result;
}
int getintfield_default(lua_State *L, int table,
const char *fieldname, int default_)
{
int result = default_;
getintfield(L, table, fieldname, result);
return result;
}
float getfloatfield_default(lua_State *L, int table,
const char *fieldname, float default_)
{
float result = default_;
getfloatfield(L, table, fieldname, result);
return result;
}
bool getboolfield_default(lua_State *L, int table,
const char *fieldname, bool default_)
{
bool result = default_;
getboolfield(L, table, fieldname, result);
return result;
}
v3s16 getv3s16field_default(lua_State *L, int table,
const char *fieldname, v3s16 default_)
{
getv3intfield(L, table, fieldname, default_);
return default_;
}
void setstringfield(lua_State *L, int table,
const char *fieldname, const std::string &value)
{
lua_pushlstring(L, value.c_str(), value.length());
if(table < 0)
table -= 1;
lua_setfield(L, table, fieldname);
}
void setintfield(lua_State *L, int table,
const char *fieldname, int value)
{
lua_pushinteger(L, value);
if(table < 0)
table -= 1;
lua_setfield(L, table, fieldname);
}
void setfloatfield(lua_State *L, int table,
const char *fieldname, float value)
{
lua_pushnumber(L, value);
if(table < 0)
table -= 1;
lua_setfield(L, table, fieldname);
}
void setboolfield(lua_State *L, int table,
const char *fieldname, bool value)
{
lua_pushboolean(L, value);
if(table < 0)
table -= 1;
lua_setfield(L, table, fieldname);
}
////
//// Array table slices
////
size_t write_array_slice_float(
lua_State *L,
int table_index,
float *data,
v3u16 data_size,
v3u16 slice_offset,
v3u16 slice_size)
{
v3u16 pmin, pmax(data_size);
if (slice_offset.X > 0) {
slice_offset.X--;
pmin.X = slice_offset.X;
pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X);
}
if (slice_offset.Y > 0) {
slice_offset.Y--;
pmin.Y = slice_offset.Y;
pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y);
}
if (slice_offset.Z > 0) {
slice_offset.Z--;
pmin.Z = slice_offset.Z;
pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z);
}
const u32 ystride = data_size.X;
const u32 zstride = data_size.X * data_size.Y;
u32 elem_index = 1;
for (u32 z = pmin.Z; z != pmax.Z; z++)
for (u32 y = pmin.Y; y != pmax.Y; y++)
for (u32 x = pmin.X; x != pmax.X; x++) {
u32 i = z * zstride + y * ystride + x;
lua_pushnumber(L, data[i]);
lua_rawseti(L, table_index, elem_index);
elem_index++;
}
return elem_index - 1;
}

View File

@ -0,0 +1,138 @@
/*
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.
*/
/******************************************************************************/
/******************************************************************************/
/* WARNING!!!! do NOT add this header in any include file or any code file */
/* not being a script/modapi file!!!!!!!! */
/******************************************************************************/
/******************************************************************************/
#pragma once
#include <vector>
#include <unordered_map>
#include "irrlichttypes_bloated.h"
#include "common/c_types.h"
extern "C" {
#include <lua.h>
}
std::string getstringfield_default(lua_State *L, int table,
const char *fieldname, const std::string &default_);
bool getboolfield_default(lua_State *L, int table,
const char *fieldname, bool default_);
float getfloatfield_default(lua_State *L, int table,
const char *fieldname, float default_);
int getintfield_default(lua_State *L, int table,
const char *fieldname, int default_);
bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname);
template<typename T>
bool getintfield(lua_State *L, int table,
const char *fieldname, T &result)
{
lua_getfield(L, table, fieldname);
bool got = false;
if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)){
result = lua_tointeger(L, -1);
got = true;
}
lua_pop(L, 1);
return got;
}
template<class T>
bool getv3intfield(lua_State *L, int index,
const char *fieldname, T &result)
{
lua_getfield(L, index, fieldname);
bool got = false;
if (lua_istable(L, -1)) {
got |= getintfield(L, -1, "x", result.X);
got |= getintfield(L, -1, "y", result.Y);
got |= getintfield(L, -1, "z", result.Z);
}
lua_pop(L, 1);
return got;
}
v3s16 getv3s16field_default(lua_State *L, int table,
const char *fieldname, v3s16 default_);
bool getstringfield(lua_State *L, int table,
const char *fieldname, std::string &result);
size_t getstringlistfield(lua_State *L, int table,
const char *fieldname,
std::vector<std::string> *result);
void read_groups(lua_State *L, int index,
std::unordered_map<std::string, int> &result);
bool getboolfield(lua_State *L, int table,
const char *fieldname, bool &result);
bool getfloatfield(lua_State *L, int table,
const char *fieldname, float &result);
void setstringfield(lua_State *L, int table,
const char *fieldname, const std::string &value);
void setintfield(lua_State *L, int table,
const char *fieldname, int value);
void setfloatfield(lua_State *L, int table,
const char *fieldname, float value);
void setboolfield(lua_State *L, int table,
const char *fieldname, bool value);
v3f checkFloatPos (lua_State *L, int index);
v3f check_v3f (lua_State *L, int index);
v3s16 check_v3s16 (lua_State *L, int index);
v3f read_v3f (lua_State *L, int index);
v2f read_v2f (lua_State *L, int index);
v2s16 read_v2s16 (lua_State *L, int index);
v2s32 read_v2s32 (lua_State *L, int index);
video::SColor read_ARGB8 (lua_State *L, int index);
bool read_color (lua_State *L, int index,
video::SColor *color);
bool is_color_table (lua_State *L, int index);
aabb3f read_aabb3f (lua_State *L, int index, f32 scale);
v3s16 read_v3s16 (lua_State *L, int index);
std::vector<aabb3f> read_aabb3f_vector (lua_State *L, int index, f32 scale);
size_t read_stringlist (lua_State *L, int index,
std::vector<std::string> *result);
void push_float_string (lua_State *L, float value);
void push_v3_float_string(lua_State *L, v3f p);
void push_v2_float_string(lua_State *L, v2f p);
void push_v2s16 (lua_State *L, v2s16 p);
void push_v2s32 (lua_State *L, v2s32 p);
void push_v3s16 (lua_State *L, v3s16 p);
void push_aabb3f (lua_State *L, aabb3f box);
void push_ARGB8 (lua_State *L, video::SColor color);
void pushFloatPos (lua_State *L, v3f p);
void push_v3f (lua_State *L, v3f p);
void push_v2f (lua_State *L, v2f p);
void warn_if_field_exists(lua_State *L, int table,
const char *fieldname,
const std::string &message);
size_t write_array_slice_float(lua_State *L, int table_index, float *data,
v3u16 data_size, v3u16 slice_offset, v3u16 slice_size);

View File

@ -0,0 +1,182 @@
/*
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 "common/c_internal.h"
#include "debug.h"
#include "log.h"
#include "porting.h"
#include "settings.h"
std::string script_get_backtrace(lua_State *L)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE);
lua_call(L, 0, 1);
std::string result = luaL_checkstring(L, -1);
lua_pop(L, 1);
return result;
}
int script_exception_wrapper(lua_State *L, lua_CFunction f)
{
try {
return f(L); // Call wrapped function and return result.
} catch (const char *s) { // Catch and convert exceptions.
lua_pushstring(L, s);
} catch (std::exception &e) {
lua_pushstring(L, e.what());
}
return lua_error(L); // Rethrow as a Lua error.
}
/*
* Note that we can't get tracebacks for LUA_ERRMEM or LUA_ERRERR (without
* hacking Lua internals). For LUA_ERRMEM, this is because memory errors will
* not execute the error handler, and by the time lua_pcall returns the
* execution stack will have already been unwound. For LUA_ERRERR, there was
* another error while trying to generate a backtrace from a LUA_ERRRUN. It is
* presumed there is an error with the internal Lua state and thus not possible
* to gather a coherent backtrace. Realistically, the best we can do here is
* print which C function performed the failing pcall.
*/
void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn)
{
if (pcall_result == 0)
return;
const char *err_type;
switch (pcall_result) {
case LUA_ERRRUN:
err_type = "Runtime";
break;
case LUA_ERRMEM:
err_type = "OOM";
break;
case LUA_ERRERR:
err_type = "Double fault";
break;
default:
err_type = "Unknown";
}
if (!mod)
mod = "??";
if (!fxn)
fxn = "??";
const char *err_descr = lua_tostring(L, -1);
if (!err_descr)
err_descr = "<no description>";
char buf[256];
porting::mt_snprintf(buf, sizeof(buf), "%s error from mod '%s' in callback %s(): ",
err_type, mod, fxn);
std::string err_msg(buf);
err_msg += err_descr;
if (pcall_result == LUA_ERRMEM) {
err_msg += "\nCurrent Lua memory usage: "
+ itos(lua_gc(L, LUA_GCCOUNT, 0) >> 10) + " MB";
}
throw LuaError(err_msg);
}
// Push the list of callbacks (a lua table).
// Then push nargs arguments.
// Then call this function, which
// - runs the callbacks
// - replaces the table and arguments with the return value,
// computed depending on mode
void script_run_callbacks_f(lua_State *L, int nargs,
RunCallbacksMode mode, const char *fxn)
{
FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
// Insert error handler
PUSH_ERROR_HANDLER(L);
int error_handler = lua_gettop(L) - nargs - 1;
lua_insert(L, error_handler);
// Insert run_callbacks between error handler and table
lua_getglobal(L, "core");
lua_getfield(L, -1, "run_callbacks");
lua_remove(L, -2);
lua_insert(L, error_handler + 1);
// Insert mode after table
lua_pushnumber(L, (int) mode);
lua_insert(L, error_handler + 3);
// Stack now looks like this:
// ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
int result = lua_pcall(L, nargs + 2, 1, error_handler);
if (result != 0)
script_error(L, result, NULL, fxn);
lua_remove(L, error_handler);
}
static void script_log(lua_State *L, const std::string &message,
std::ostream &log_to, bool do_error, int stack_depth)
{
lua_Debug ar;
log_to << message << " ";
if (lua_getstack(L, stack_depth, &ar)) {
FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed");
log_to << "(at " << ar.short_src << ":" << ar.currentline << ")";
} else {
log_to << "(at ?:?)";
}
log_to << std::endl;
if (do_error)
script_error(L, LUA_ERRRUN, NULL, NULL);
else
infostream << script_get_backtrace(L) << std::endl;
}
DeprecatedHandlingMode get_deprecated_handling_mode()
{
static thread_local bool configured = false;
static thread_local DeprecatedHandlingMode ret = DeprecatedHandlingMode::Ignore;
// Only read settings on first call
if (!configured) {
std::string value = g_settings->get("deprecated_lua_api_handling");
if (value == "log") {
ret = DeprecatedHandlingMode::Log;
} else if (value == "error") {
ret = DeprecatedHandlingMode::Error;
}
configured = true;
}
return ret;
}
void log_deprecated(lua_State *L, const std::string &message, int stack_depth)
{
DeprecatedHandlingMode mode = get_deprecated_handling_mode();
if (mode != DeprecatedHandlingMode::Ignore)
script_log(L, message, warningstream, mode == DeprecatedHandlingMode::Error, stack_depth);
}

View File

@ -0,0 +1,138 @@
/*
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.
*/
/******************************************************************************/
/******************************************************************************/
/* WARNING!!!! do NOT add this header in any include file or any code file */
/* not being a modapi file!!!!!!!! */
/******************************************************************************/
/******************************************************************************/
#pragma once
extern "C" {
#include <lua.h>
#include <lauxlib.h>
}
#include "config.h"
#include "common/c_types.h"
/*
Define our custom indices into the Lua registry table.
Lua 5.2 and above define the LUA_RIDX_LAST macro. Only numbers above that
may be used for custom indices, anything else is reserved.
Lua 5.1 / LuaJIT do not use any numeric indices (only string indices),
so we can use numeric indices freely.
*/
#ifdef LUA_RIDX_LAST
#define CUSTOM_RIDX_BASE ((LUA_RIDX_LAST)+1)
#else
#define CUSTOM_RIDX_BASE 1
#endif
#define CUSTOM_RIDX_SCRIPTAPI (CUSTOM_RIDX_BASE)
#define CUSTOM_RIDX_GLOBALS_BACKUP (CUSTOM_RIDX_BASE + 1)
#define CUSTOM_RIDX_CURRENT_MOD_NAME (CUSTOM_RIDX_BASE + 2)
#define CUSTOM_RIDX_BACKTRACE (CUSTOM_RIDX_BASE + 3)
// Determine if CUSTOM_RIDX_SCRIPTAPI will hold a light or full userdata
#if defined(__aarch64__) && USE_LUAJIT
/* LuaJIT has a 47-bit limit for lightuserdata on this platform and we cannot
* assume that the ScriptApi class was allocated at a fitting address. */
#define INDIRECT_SCRIPTAPI_RIDX 1
#else
#define INDIRECT_SCRIPTAPI_RIDX 0
#endif
// Pushes the error handler onto the stack and returns its index
#define PUSH_ERROR_HANDLER(L) \
(lua_rawgeti((L), LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE), lua_gettop((L)))
#define PCALL_RESL(L, RES) { \
int result_ = (RES); \
if (result_ != 0) { \
script_error((L), result_, NULL, __FUNCTION__); \
} \
}
#define script_run_callbacks(L, nargs, mode) \
script_run_callbacks_f((L), (nargs), (mode), __FUNCTION__)
// What script_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 readParam<bool> to true or false, respectively.
};
std::string script_get_backtrace(lua_State *L);
int script_exception_wrapper(lua_State *L, lua_CFunction f);
void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn);
void script_run_callbacks_f(lua_State *L, int nargs,
RunCallbacksMode mode, const char *fxn);
enum class DeprecatedHandlingMode {
Ignore,
Log,
Error
};
/**
* Reads `deprecated_lua_api_handling` in settings, returns cached value.
*
* @return DeprecatedHandlingMode
*/
DeprecatedHandlingMode get_deprecated_handling_mode();
/**
* Handles a deprecation warning based on user settings
*
* @param L Lua State
* @param message The deprecation method
* @param stack_depth How far on the stack to the first user function (ie: not builtin or core)
*/
void log_deprecated(lua_State *L, const std::string &message,
int stack_depth=1);

View File

@ -0,0 +1,34 @@
/*
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 <iostream>
#include "common/c_types.h"
#include "common/c_internal.h"
#include "itemdef.h"
struct EnumString es_ItemType[] =
{
{ITEM_NONE, "none"},
{ITEM_NODE, "node"},
{ITEM_CRAFT, "craft"},
{ITEM_TOOL, "tool"},
{0, NULL},
};

View File

@ -0,0 +1,61 @@
/*
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.
*/
#pragma once
extern "C" {
#include "lua.h"
}
#include <iostream>
#include "exceptions.h"
struct EnumString
{
int num;
const char *str;
};
class StackUnroller
{
private:
lua_State *m_lua;
int m_original_top;
public:
StackUnroller(lua_State *L):
m_lua(L),
m_original_top(-1)
{
m_original_top = lua_gettop(m_lua); // store stack height
}
~StackUnroller()
{
lua_settop(m_lua, m_original_top); // restore stack height
}
};
class LuaError : public ModError
{
public:
LuaError(const std::string &s) : ModError(s) {}
};
extern EnumString es_ItemType[];

View File

@ -0,0 +1,132 @@
/*
Minetest
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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 "helper.h"
#include <cmath>
#include <sstream>
#include <irr_v2d.h>
#include <irr_v3d.h>
#include "c_types.h"
#include "c_internal.h"
// imported from c_converter.cpp with pure C++ style
static inline void check_lua_type(lua_State *L, int index, const char *name, int type)
{
int t = lua_type(L, index);
if (t != type) {
std::string traceback = script_get_backtrace(L);
throw LuaError(std::string("Invalid ") + (name) + " (expected " +
lua_typename(L, (type)) + " got " + lua_typename(L, t) +
").\n" + traceback);
}
}
// imported from c_converter.cpp
#define CHECK_POS_COORD(name) \
check_lua_type(L, -1, "position coordinate '" name "'", LUA_TNUMBER)
#define CHECK_POS_TAB(index) check_lua_type(L, index, "position", LUA_TTABLE)
bool LuaHelper::isNaN(lua_State *L, int idx)
{
return lua_type(L, idx) == LUA_TNUMBER && std::isnan(lua_tonumber(L, idx));
}
/*
* Read template functions
*/
template <> bool LuaHelper::readParam(lua_State *L, int index)
{
return lua_toboolean(L, index) != 0;
}
template <> s16 LuaHelper::readParam(lua_State *L, int index)
{
return lua_tonumber(L, index);
}
template <> int LuaHelper::readParam(lua_State *L, int index)
{
return luaL_checkint(L, index);
}
template <> float LuaHelper::readParam(lua_State *L, int index)
{
if (isNaN(L, index))
throw LuaError("NaN value is not allowed.");
return (float)luaL_checknumber(L, index);
}
template <> v2s16 LuaHelper::readParam(lua_State *L, int index)
{
v2s16 p;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
CHECK_POS_COORD("x");
p.X = readParam<s16>(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
CHECK_POS_COORD("y");
p.Y = readParam<s16>(L, -1);
lua_pop(L, 1);
return p;
}
template <> v2f LuaHelper::readParam(lua_State *L, int index)
{
v2f p;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
CHECK_POS_COORD("x");
p.X = readParam<float>(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
CHECK_POS_COORD("y");
p.Y = readParam<float>(L, -1);
lua_pop(L, 1);
return p;
}
template <> v3f LuaHelper::readParam(lua_State *L, int index)
{
v3f p;
CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
CHECK_POS_COORD("x");
p.X = readParam<float>(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
CHECK_POS_COORD("y");
p.Y = readParam<float>(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "z");
CHECK_POS_COORD("z");
p.Z = readParam<float>(L, -1);
lua_pop(L, 1);
return p;
}
template <> std::string LuaHelper::readParam(lua_State *L, int index)
{
size_t length;
std::string result;
const char *str = luaL_checklstring(L, index, &length);
result.assign(str, length);
return result;
}

View File

@ -0,0 +1,57 @@
/*
Minetest
Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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.
*/
#pragma once
extern "C" {
#include <lua.h>
#include <lauxlib.h>
}
class LuaHelper
{
protected:
static bool isNaN(lua_State *L, int idx);
/**
* Read a value using a template type T from Lua State L and index
*
*
* @tparam T type to read from Lua
* @param L Lua state
* @param index Lua Index to read
* @return read value from Lua
*/
template <typename T> static T readParam(lua_State *L, int index);
/**
* Read a value using a template type T from Lua State L and index
*
* @tparam T type to read from Lua
* @param L Lua state
* @param index Lua Index to read
* @param default_value default value to apply if nil
* @return read value from Lua or default value if nil
*/
template <typename T>
static inline T readParam(lua_State *L, int index, const T &default_value)
{
return lua_isnoneornil(L, index) ? default_value : readParam<T>(L, index);
}
};

View File

@ -0,0 +1,21 @@
set(common_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp
${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_modchannels.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}/s_security.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_server.cpp
PARENT_SCOPE)
set(client_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_cheats.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp
PARENT_SCOPE)

View File

@ -0,0 +1,289 @@
/*
Minetest
Copyright (C) 2013 sapier, <sapier AT gmx DOT net>
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 <cstdio>
#include <cstdlib>
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include "server.h"
#include "s_async.h"
#include "log.h"
#include "filesys.h"
#include "porting.h"
#include "common/c_internal.h"
/******************************************************************************/
AsyncEngine::~AsyncEngine()
{
// Request all threads to stop
for (AsyncWorkerThread *workerThread : workerThreads) {
workerThread->stop();
}
// Wake up all threads
for (std::vector<AsyncWorkerThread *>::iterator it = workerThreads.begin();
it != workerThreads.end(); ++it) {
jobQueueCounter.post();
}
// Wait for threads to finish
for (AsyncWorkerThread *workerThread : workerThreads) {
workerThread->wait();
}
// Force kill all threads
for (AsyncWorkerThread *workerThread : workerThreads) {
delete workerThread;
}
jobQueueMutex.lock();
jobQueue.clear();
jobQueueMutex.unlock();
workerThreads.clear();
}
/******************************************************************************/
void AsyncEngine::registerStateInitializer(StateInitializer func)
{
stateInitializers.push_back(func);
}
/******************************************************************************/
void AsyncEngine::initialize(unsigned int numEngines)
{
initDone = true;
for (unsigned int i = 0; i < numEngines; i++) {
AsyncWorkerThread *toAdd = new AsyncWorkerThread(this,
std::string("AsyncWorker-") + itos(i));
workerThreads.push_back(toAdd);
toAdd->start();
}
}
/******************************************************************************/
unsigned int AsyncEngine::queueAsyncJob(const std::string &func,
const std::string &params)
{
jobQueueMutex.lock();
LuaJobInfo toAdd;
toAdd.id = jobIdCounter++;
toAdd.serializedFunction = func;
toAdd.serializedParams = params;
jobQueue.push_back(toAdd);
jobQueueCounter.post();
jobQueueMutex.unlock();
return toAdd.id;
}
/******************************************************************************/
LuaJobInfo AsyncEngine::getJob()
{
jobQueueCounter.wait();
jobQueueMutex.lock();
LuaJobInfo retval;
if (!jobQueue.empty()) {
retval = jobQueue.front();
jobQueue.pop_front();
retval.valid = true;
}
jobQueueMutex.unlock();
return retval;
}
/******************************************************************************/
void AsyncEngine::putJobResult(const LuaJobInfo &result)
{
resultQueueMutex.lock();
resultQueue.push_back(result);
resultQueueMutex.unlock();
}
/******************************************************************************/
void AsyncEngine::step(lua_State *L)
{
int error_handler = PUSH_ERROR_HANDLER(L);
lua_getglobal(L, "core");
resultQueueMutex.lock();
while (!resultQueue.empty()) {
LuaJobInfo jobDone = resultQueue.front();
resultQueue.pop_front();
lua_getfield(L, -1, "async_event_handler");
if (lua_isnil(L, -1)) {
FATAL_ERROR("Async event handler does not exist!");
}
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushinteger(L, jobDone.id);
lua_pushlstring(L, jobDone.serializedResult.data(),
jobDone.serializedResult.size());
PCALL_RESL(L, lua_pcall(L, 2, 0, error_handler));
}
resultQueueMutex.unlock();
lua_pop(L, 2); // Pop core and error handler
}
/******************************************************************************/
void AsyncEngine::pushFinishedJobs(lua_State* L) {
// Result Table
MutexAutoLock l(resultQueueMutex);
unsigned int index = 1;
lua_createtable(L, resultQueue.size(), 0);
int top = lua_gettop(L);
while (!resultQueue.empty()) {
LuaJobInfo jobDone = resultQueue.front();
resultQueue.pop_front();
lua_createtable(L, 0, 2); // Pre-allocate space for two map fields
int top_lvl2 = lua_gettop(L);
lua_pushstring(L, "jobid");
lua_pushnumber(L, jobDone.id);
lua_settable(L, top_lvl2);
lua_pushstring(L, "retval");
lua_pushlstring(L, jobDone.serializedResult.data(),
jobDone.serializedResult.size());
lua_settable(L, top_lvl2);
lua_rawseti(L, top, index++);
}
}
/******************************************************************************/
void AsyncEngine::prepareEnvironment(lua_State* L, int top)
{
for (StateInitializer &stateInitializer : stateInitializers) {
stateInitializer(L, top);
}
}
/******************************************************************************/
AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher,
const std::string &name) :
Thread(name),
ScriptApiBase(ScriptingType::Async),
jobDispatcher(jobDispatcher)
{
lua_State *L = getStack();
// Prepare job lua environment
lua_getglobal(L, "core");
int top = lua_gettop(L);
// Push builtin initialization type
lua_pushstring(L, "async");
lua_setglobal(L, "INIT");
jobDispatcher->prepareEnvironment(L, top);
}
/******************************************************************************/
AsyncWorkerThread::~AsyncWorkerThread()
{
sanity_check(!isRunning());
}
/******************************************************************************/
void* AsyncWorkerThread::run()
{
lua_State *L = getStack();
std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua";
try {
loadScript(script);
} catch (const ModError &e) {
errorstream << "Execution of async base environment failed: "
<< e.what() << std::endl;
FATAL_ERROR("Execution of async base environment failed");
}
int error_handler = PUSH_ERROR_HANDLER(L);
lua_getglobal(L, "core");
if (lua_isnil(L, -1)) {
FATAL_ERROR("Unable to find core within async environment!");
}
// Main loop
while (!stopRequested()) {
// Wait for job
LuaJobInfo toProcess = jobDispatcher->getJob();
if (!toProcess.valid || stopRequested()) {
continue;
}
lua_getfield(L, -1, "job_processor");
if (lua_isnil(L, -1)) {
FATAL_ERROR("Unable to get async job processor!");
}
luaL_checktype(L, -1, LUA_TFUNCTION);
// Call it
lua_pushlstring(L,
toProcess.serializedFunction.data(),
toProcess.serializedFunction.size());
lua_pushlstring(L,
toProcess.serializedParams.data(),
toProcess.serializedParams.size());
int result = lua_pcall(L, 2, 1, error_handler);
if (result) {
PCALL_RES(result);
toProcess.serializedResult = "";
} else {
// Fetch result
size_t length;
const char *retval = lua_tolstring(L, -1, &length);
toProcess.serializedResult = std::string(retval, length);
}
lua_pop(L, 1); // Pop retval
// Put job result
jobDispatcher->putJobResult(toProcess);
}
lua_pop(L, 2); // Pop core and error handler
return 0;
}

View File

@ -0,0 +1,156 @@
/*
Minetest
Copyright (C) 2013 sapier, <sapier AT gmx DOT net>
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.
*/
#pragma once
#include <vector>
#include <deque>
#include <map>
#include "threading/semaphore.h"
#include "threading/thread.h"
#include "lua.h"
#include "cpp_api/s_base.h"
// Forward declarations
class AsyncEngine;
// Declarations
// Data required to queue a job
struct LuaJobInfo
{
LuaJobInfo() = default;
// Function to be called in async environment
std::string serializedFunction = "";
// Parameter to be passed to function
std::string serializedParams = "";
// Result of function call
std::string serializedResult = "";
// JobID used to identify a job and match it to callback
unsigned int id = 0;
bool valid = false;
};
// Asynchronous working environment
class AsyncWorkerThread : public Thread, public ScriptApiBase {
public:
AsyncWorkerThread(AsyncEngine* jobDispatcher, const std::string &name);
virtual ~AsyncWorkerThread();
void *run();
private:
AsyncEngine *jobDispatcher = nullptr;
};
// Asynchornous thread and job management
class AsyncEngine {
friend class AsyncWorkerThread;
typedef void (*StateInitializer)(lua_State *L, int top);
public:
AsyncEngine() = default;
~AsyncEngine();
/**
* Register function to be called on new states
* @param func C function to be called
*/
void registerStateInitializer(StateInitializer func);
/**
* Create async engine tasks and lock function registration
* @param numEngines Number of async threads to be started
*/
void initialize(unsigned int numEngines);
/**
* Queue an async job
* @param func Serialized lua function
* @param params Serialized parameters
* @return jobid The job is queued
*/
unsigned int queueAsyncJob(const std::string &func, const std::string &params);
/**
* Engine step to process finished jobs
* the engine step is one way to pass events back, PushFinishedJobs another
* @param L The Lua stack
*/
void step(lua_State *L);
/**
* Push a list of finished jobs onto the stack
* @param L The Lua stack
*/
void pushFinishedJobs(lua_State *L);
protected:
/**
* Get a Job from queue to be processed
* this function blocks until a job is ready
* @return a job to be processed
*/
LuaJobInfo getJob();
/**
* Put a Job result back to result queue
* @param result result of completed job
*/
void putJobResult(const LuaJobInfo &result);
/**
* Initialize environment with current registred functions
* this function adds all functions registred by registerFunction to the
* passed lua stack
* @param L Lua stack to initialize
* @param top Stack position
*/
void prepareEnvironment(lua_State* L, int top);
private:
// Variable locking the engine against further modification
bool initDone = false;
// Internal store for registred state initializers
std::vector<StateInitializer> stateInitializers;
// Internal counter to create job IDs
unsigned int jobIdCounter = 0;
// Mutex to protect job queue
std::mutex jobQueueMutex;
// Job queue
std::deque<LuaJobInfo> jobQueue;
// Mutex to protect result queue
std::mutex resultQueueMutex;
// Result queue
std::deque<LuaJobInfo> resultQueue;
// List of current worker threads
std::vector<AsyncWorkerThread*> workerThreads;
// Counter semaphore for job dispatching
Semaphore jobQueueCounter;
};

View File

@ -0,0 +1,454 @@
/*
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_base.h"
#include "cpp_api/s_internal.h"
#include "cpp_api/s_security.h"
#include "lua_api/l_object.h"
#include "common/c_converter.h"
#include "server/player_sao.h"
#include "filesys.h"
#include "content/mods.h"
#include "porting.h"
#include "util/string.h"
#include "server.h"
#ifndef SERVER
#include "client/client.h"
#endif
extern "C" {
#include "lualib.h"
#if USE_LUAJIT
#include "luajit.h"
#endif
}
#include <cstdio>
#include <cstdarg>
#include "script/common/c_content.h"
#include <sstream>
class ModNameStorer
{
private:
lua_State *L;
public:
ModNameStorer(lua_State *L_, const std::string &mod_name):
L(L_)
{
// Store current mod name in registry
lua_pushstring(L, mod_name.c_str());
lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
}
~ModNameStorer()
{
// Clear current mod name from registry
lua_pushnil(L);
lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
}
};
/*
ScriptApiBase
*/
ScriptApiBase::ScriptApiBase(ScriptingType type):
m_type(type)
{
#ifdef SCRIPTAPI_LOCK_DEBUG
m_lock_recursion_count = 0;
#endif
m_luastack = luaL_newstate();
FATAL_ERROR_IF(!m_luastack, "luaL_newstate() failed");
lua_atpanic(m_luastack, &luaPanic);
/*if (m_type == ScriptingType::Client)
clientOpenLibs(m_luastack);
else*/
luaL_openlibs(m_luastack);
// Make the ScriptApiBase* accessible to ModApiBase
#if INDIRECT_SCRIPTAPI_RIDX
*(void **)(lua_newuserdata(m_luastack, sizeof(void *))) = this;
#else
lua_pushlightuserdata(m_luastack, this);
#endif
lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
// Add and save an error handler
lua_getglobal(m_luastack, "debug");
lua_getfield(m_luastack, -1, "traceback");
lua_rawseti(m_luastack, LUA_REGISTRYINDEX, CUSTOM_RIDX_BACKTRACE);
lua_pop(m_luastack, 1); // pop debug
// If we are using LuaJIT add a C++ wrapper function to catch
// exceptions thrown in Lua -> C++ calls
#if USE_LUAJIT
lua_pushlightuserdata(m_luastack, (void*) script_exception_wrapper);
luaJIT_setmode(m_luastack, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
lua_pop(m_luastack, 1);
#endif
// Add basic globals
lua_newtable(m_luastack);
lua_setglobal(m_luastack, "core");
if (m_type == ScriptingType::Client)
lua_pushstring(m_luastack, "/");
else
lua_pushstring(m_luastack, DIR_DELIM);
lua_setglobal(m_luastack, "DIR_DELIM");
lua_pushstring(m_luastack, porting::getPlatformName());
lua_setglobal(m_luastack, "PLATFORM");
// Make sure Lua uses the right locale
setlocale(LC_NUMERIC, "C");
}
ScriptApiBase::~ScriptApiBase()
{
lua_close(m_luastack);
}
int ScriptApiBase::luaPanic(lua_State *L)
{
std::ostringstream oss;
oss << "LUA PANIC: unprotected error in call to Lua API ("
<< readParam<std::string>(L, -1) << ")";
FATAL_ERROR(oss.str().c_str());
// NOTREACHED
return 0;
}
void ScriptApiBase::clientOpenLibs(lua_State *L)
{
static const std::vector<std::pair<std::string, lua_CFunction>> m_libs = {
{ "", luaopen_base },
{ LUA_TABLIBNAME, luaopen_table },
{ LUA_OSLIBNAME, luaopen_os },
{ LUA_STRLIBNAME, luaopen_string },
{ LUA_MATHLIBNAME, luaopen_math },
{ LUA_DBLIBNAME, luaopen_debug },
#if USE_LUAJIT
{ LUA_JITLIBNAME, luaopen_jit },
#endif
};
for (const std::pair<std::string, lua_CFunction> &lib : m_libs) {
lua_pushcfunction(L, lib.second);
lua_pushstring(L, lib.first.c_str());
lua_call(L, 1, 0);
}
}
void ScriptApiBase::loadMod(const std::string &script_path,
const std::string &mod_name)
{
ModNameStorer mod_name_storer(getStack(), mod_name);
loadScript(script_path);
}
void ScriptApiBase::loadScript(const std::string &script_path)
{
verbosestream << "Loading and running script from " << script_path << std::endl;
lua_State *L = getStack();
int error_handler = PUSH_ERROR_HANDLER(L);
bool ok;
if (m_secure) {
ok = ScriptApiSecurity::safeLoadFile(L, script_path.c_str());
} else {
ok = !luaL_loadfile(L, script_path.c_str());
}
ok = ok && !lua_pcall(L, 0, 0, error_handler);
if (!ok) {
const char *error_msg = lua_tostring(L, -1);
if (!error_msg)
error_msg = "(error object is not a string)";
lua_pop(L, 2); // Pop error message and error handler
throw ModError("Failed to load and run script from " +
script_path + ":\n" + error_msg);
}
lua_pop(L, 1); // Pop error handler
}
#ifndef SERVER
void ScriptApiBase::loadModFromMemory(const std::string &mod_name)
{
ModNameStorer mod_name_storer(getStack(), mod_name);
sanity_check(m_type == ScriptingType::Client);
const std::string init_filename = mod_name + ":init.lua";
const std::string chunk_name = "@" + init_filename;
const std::string *contents = getClient()->getModFile(init_filename);
if (!contents)
throw ModError("Mod \"" + mod_name + "\" lacks init.lua");
verbosestream << "Loading and running script " << chunk_name << std::endl;
lua_State *L = getStack();
int error_handler = PUSH_ERROR_HANDLER(L);
bool ok = ScriptApiSecurity::safeLoadString(L, *contents, chunk_name.c_str());
if (ok)
ok = !lua_pcall(L, 0, 0, error_handler);
if (!ok) {
const char *error_msg = lua_tostring(L, -1);
if (!error_msg)
error_msg = "(error object is not a string)";
lua_pop(L, 2); // Pop error message and error handler
throw ModError("Failed to load and run mod \"" +
mod_name + "\":\n" + error_msg);
}
lua_pop(L, 1); // Pop error handler
}
#endif
// Push the list of callbacks (a lua table).
// Then push nargs arguments.
// Then call this function, which
// - runs the callbacks
// - replaces the table and arguments with the return value,
// computed depending on mode
// This function must only be called with scriptlock held (i.e. inside of a
// code block with SCRIPTAPI_PRECHECKHEADER declared)
void ScriptApiBase::runCallbacksRaw(int nargs,
RunCallbacksMode mode, const char *fxn)
{
#ifndef SERVER
// Hard fail for bad guarded callbacks
// Only run callbacks when the scripting enviroment is loaded
FATAL_ERROR_IF(m_type == ScriptingType::Client &&
!getClient()->modsLoaded(), fxn);
#endif
#ifdef SCRIPTAPI_LOCK_DEBUG
assert(m_lock_recursion_count > 0);
#endif
lua_State *L = getStack();
FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
// Insert error handler
PUSH_ERROR_HANDLER(L);
int error_handler = lua_gettop(L) - nargs - 1;
lua_insert(L, error_handler);
// Insert run_callbacks between error handler and table
lua_getglobal(L, "core");
lua_getfield(L, -1, "run_callbacks");
lua_remove(L, -2);
lua_insert(L, error_handler + 1);
// Insert mode after table
lua_pushnumber(L, (int)mode);
lua_insert(L, error_handler + 3);
// Stack now looks like this:
// ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
int result = lua_pcall(L, nargs + 2, 1, error_handler);
if (result != 0)
scriptError(result, fxn);
lua_remove(L, error_handler);
}
void ScriptApiBase::realityCheck()
{
int top = lua_gettop(m_luastack);
if (top >= 30) {
dstream << "Stack is over 30:" << std::endl;
stackDump(dstream);
std::string traceback = script_get_backtrace(m_luastack);
throw LuaError("Stack is over 30 (reality check)\n" + traceback);
}
}
void ScriptApiBase::scriptError(int result, const char *fxn)
{
script_error(getStack(), result, m_last_run_mod.c_str(), fxn);
}
void ScriptApiBase::stackDump(std::ostream &o)
{
int top = lua_gettop(m_luastack);
for (int i = 1; i <= top; i++) { /* repeat for each level */
int t = lua_type(m_luastack, i);
switch (t) {
case LUA_TSTRING: /* strings */
o << "\"" << readParam<std::string>(m_luastack, i) << "\"";
break;
case LUA_TBOOLEAN: /* booleans */
o << (readParam<bool>(m_luastack, i) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */ {
char buf[10];
porting::mt_snprintf(buf, sizeof(buf), "%lf", lua_tonumber(m_luastack, i));
o << buf;
break;
}
default: /* other values */
o << lua_typename(m_luastack, t);
break;
}
o << " ";
}
o << std::endl;
}
void ScriptApiBase::setOriginDirect(const char *origin)
{
m_last_run_mod = origin ? origin : "??";
}
void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn)
{
#ifdef SCRIPTAPI_DEBUG
lua_State *L = getStack();
m_last_run_mod = lua_istable(L, index) ?
getstringfield_default(L, index, "mod_origin", "") : "";
//printf(">>>> running %s for mod: %s\n", fxn, m_last_run_mod.c_str());
#endif
}
/*
* How ObjectRefs are handled in Lua:
* When an active object is created, an ObjectRef is created on the Lua side
* and stored in core.object_refs[id].
* Methods that require an ObjectRef to a certain object retrieve it from that
* table instead of creating their own.(*)
* When an active object is removed, the existing ObjectRef is invalidated
* using ::set_null() and removed from the core.object_refs table.
* (*) An exception to this are NULL ObjectRefs and anonymous ObjectRefs
* for objects without ID.
* It's unclear what the latter are needed for and their use is problematic
* since we lose control over the ref and the contained pointer.
*/
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 core.object_refs table
lua_getglobal(L, "core");
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 core.object_refs table
lua_getglobal(L, "core");
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(lua_State *L,
ServerActiveObject *cobj)
{
if (cobj == NULL || cobj->getId() == 0) {
ObjectRef::create(L, cobj);
} else {
push_objectRef(L, cobj->getId());
if (cobj->isGone())
warningstream << "ScriptApiBase::objectrefGetOrCreate(): "
<< "Pushing ObjectRef to removed/deactivated object"
<< ", this is probably a bug." << std::endl;
}
}
void ScriptApiBase::pushPlayerHPChangeReason(lua_State *L, const PlayerHPChangeReason &reason)
{
if (reason.hasLuaReference())
lua_rawgeti(L, LUA_REGISTRYINDEX, reason.lua_reference);
else
lua_newtable(L);
lua_getfield(L, -1, "type");
bool has_type = (bool)lua_isstring(L, -1);
lua_pop(L, 1);
if (!has_type) {
lua_pushstring(L, reason.getTypeAsString().c_str());
lua_setfield(L, -2, "type");
}
lua_pushstring(L, reason.from_mod ? "mod" : "engine");
lua_setfield(L, -2, "from");
if (reason.object) {
objectrefGetOrCreate(L, reason.object);
lua_setfield(L, -2, "object");
}
if (!reason.node.empty()) {
lua_pushstring(L, reason.node.c_str());
lua_setfield(L, -2, "node");
}
}
Server* ScriptApiBase::getServer()
{
return dynamic_cast<Server *>(m_gamedef);
}
#ifndef SERVER
Client* ScriptApiBase::getClient()
{
return dynamic_cast<Client *>(m_gamedef);
}
#endif

175
src/script/cpp_api/s_base.h Normal file
View File

@ -0,0 +1,175 @@
/*
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.
*/
#pragma once
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <unordered_map>
#include "common/helper.h"
#include "util/basic_macros.h"
extern "C" {
#include <lua.h>
#include <lualib.h>
}
#include "irrlichttypes.h"
#include "common/c_types.h"
#include "common/c_internal.h"
#include "debug.h"
#include "config.h"
#define SCRIPTAPI_LOCK_DEBUG
#define SCRIPTAPI_DEBUG
// MUST be an invalid mod name so that mods can't
// use that name to bypass security!
#define BUILTIN_MOD_NAME "*builtin*"
#define PCALL_RES(RES) { \
int result_ = (RES); \
if (result_ != 0) { \
scriptError(result_, __FUNCTION__); \
} \
}
#define runCallbacks(nargs, mode) \
runCallbacksRaw((nargs), (mode), __FUNCTION__)
#define setOriginFromTable(index) \
setOriginFromTableRaw(index, __FUNCTION__)
enum class ScriptingType: u8 {
Async,
Client,
MainMenu,
Server
};
class Server;
#ifndef SERVER
class Client;
class Game;
#endif
class IGameDef;
class Environment;
class GUIEngine;
class ServerActiveObject;
struct PlayerHPChangeReason;
class ScriptApiBase : protected LuaHelper {
public:
ScriptApiBase(ScriptingType type);
// fake constructor to allow script API classes (e.g ScriptApiEnv) to virtually inherit from this one.
ScriptApiBase()
{
FATAL_ERROR("ScriptApiBase created without ScriptingType!");
}
virtual ~ScriptApiBase();
DISABLE_CLASS_COPY(ScriptApiBase);
// These throw a ModError on failure
void loadMod(const std::string &script_path, const std::string &mod_name);
void loadScript(const std::string &script_path);
#ifndef SERVER
void loadModFromMemory(const std::string &mod_name);
#endif
void runCallbacksRaw(int nargs,
RunCallbacksMode mode, const char *fxn);
/* object */
void addObjectReference(ServerActiveObject *cobj);
void removeObjectReference(ServerActiveObject *cobj);
IGameDef *getGameDef() { return m_gamedef; }
Server* getServer();
ScriptingType getType() { return m_type; }
#ifndef SERVER
Client* getClient();
Game *getGame() { return m_game; }
#endif
std::string getOrigin() { return m_last_run_mod; }
void setOriginDirect(const char *origin);
void setOriginFromTableRaw(int index, const char *fxn);
void clientOpenLibs(lua_State *L);
protected:
friend class LuaABM;
friend class LuaLBM;
friend class InvRef;
friend class ObjectRef;
friend class NodeMetaRef;
friend class ModApiBase;
friend class ModApiEnvMod;
friend class LuaVoxelManip;
lua_State* getStack()
{ return m_luastack; }
void realityCheck();
void scriptError(int result, const char *fxn);
void stackDump(std::ostream &o);
void setGameDef(IGameDef* gamedef) { m_gamedef = gamedef; }
#ifndef SERVER
void setGame(Game *game) { m_game = game; }
#endif
Environment* getEnv() { return m_environment; }
void setEnv(Environment* env) { m_environment = env; }
#ifndef SERVER
GUIEngine* getGuiEngine() { return m_guiengine; }
void setGuiEngine(GUIEngine* guiengine) { m_guiengine = guiengine; }
#endif
void objectrefGetOrCreate(lua_State *L, ServerActiveObject *cobj);
void pushPlayerHPChangeReason(lua_State *L, const PlayerHPChangeReason& reason);
std::recursive_mutex m_luastackmutex;
std::string m_last_run_mod;
bool m_secure = false;
#ifdef SCRIPTAPI_LOCK_DEBUG
int m_lock_recursion_count{};
std::thread::id m_owning_thread;
#endif
private:
static int luaPanic(lua_State *L);
lua_State *m_luastack = nullptr;
IGameDef *m_gamedef = nullptr;
#ifndef SERVER
Game *m_game = nullptr;
#endif
Environment *m_environment = nullptr;
#ifndef SERVER
GUIEngine *m_guiengine = nullptr;
#endif
ScriptingType m_type;
};

View File

@ -0,0 +1,124 @@
/*
Dragonfire
Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de>
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_cheats.h"
#include "cpp_api/s_base.h"
#include "cpp_api/s_internal.h"
#include "settings.h"
ScriptApiCheatsCheat::ScriptApiCheatsCheat(
const std::string &name, const std::string &setting) :
m_name(name),
m_setting(setting), m_function_ref(0)
{
}
ScriptApiCheatsCheat::ScriptApiCheatsCheat(const std::string &name, const int &function) :
m_name(name), m_setting(""), m_function_ref(function)
{
}
bool ScriptApiCheatsCheat::is_enabled()
{
try {
return !m_function_ref && g_settings->getBool(m_setting);
} catch (SettingNotFoundException &) {
return false;
}
}
void ScriptApiCheatsCheat::toggle(lua_State *L, int error_handler)
{
if (m_function_ref) {
lua_rawgeti(L, LUA_REGISTRYINDEX, m_function_ref);
lua_pcall(L, 0, 0, error_handler);
} else
g_settings->setBool(m_setting, !is_enabled());
}
ScriptApiCheatsCategory::ScriptApiCheatsCategory(const std::string &name) : m_name(name)
{
}
ScriptApiCheatsCategory::~ScriptApiCheatsCategory()
{
for (auto i = m_cheats.begin(); i != m_cheats.end(); i++)
delete *i;
}
void ScriptApiCheatsCategory::read_cheats(lua_State *L)
{
lua_pushnil(L);
while (lua_next(L, -2)) {
ScriptApiCheatsCheat *cheat = nullptr;
std::string name = lua_tostring(L, -2);
if (lua_isstring(L, -1))
cheat = new ScriptApiCheatsCheat(name, lua_tostring(L, -1));
else if (lua_isfunction(L, -1)) {
cheat = new ScriptApiCheatsCheat(
name, luaL_ref(L, LUA_REGISTRYINDEX));
lua_pushnil(L);
}
if (cheat)
m_cheats.push_back(cheat);
lua_pop(L, 1);
}
}
ScriptApiCheats::~ScriptApiCheats()
{
for (auto i = m_cheat_categories.begin(); i != m_cheat_categories.end(); i++)
delete *i;
}
void ScriptApiCheats::init_cheats()
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "cheats");
if (!lua_istable(L, -1)) {
lua_pop(L, 2);
return;
}
lua_pushnil(L);
while (lua_next(L, -2)) {
if (lua_istable(L, -1)) {
ScriptApiCheatsCategory *category =
new ScriptApiCheatsCategory(lua_tostring(L, -2));
category->read_cheats(L);
m_cheat_categories.push_back(category);
}
lua_pop(L, 1);
}
lua_pop(L, 2);
m_cheats_loaded = true;
}
void ScriptApiCheats::toggle_cheat(ScriptApiCheatsCheat *cheat)
{
SCRIPTAPI_PRECHECKHEADER
PUSH_ERROR_HANDLER(L);
int error_handler = lua_gettop(L) - 1;
lua_insert(L, error_handler);
cheat->toggle(L, error_handler);
}

View File

@ -0,0 +1,58 @@
/*
Dragonfire
Copyright (C) 2020 Elias Fleckenstein <eliasfleckenstein@web.de>
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include <vector>
#include <string>
class ScriptApiCheatsCheat
{
public:
ScriptApiCheatsCheat(const std::string &name, const std::string &setting);
ScriptApiCheatsCheat(const std::string &name, const int &function);
std::string m_name;
bool is_enabled();
void toggle(lua_State *L, int error_handler);
private:
std::string m_setting;
int m_function_ref;
};
class ScriptApiCheatsCategory
{
public:
ScriptApiCheatsCategory(const std::string &name);
~ScriptApiCheatsCategory();
std::string m_name;
void read_cheats(lua_State *L);
std::vector<ScriptApiCheatsCheat *> m_cheats;
};
class ScriptApiCheats : virtual public ScriptApiBase
{
public:
virtual ~ScriptApiCheats();
void init_cheats();
void toggle_cheat(ScriptApiCheatsCheat *cheat);
bool m_cheats_loaded = false;
std::vector<ScriptApiCheatsCategory *> m_cheat_categories;
};

View File

@ -0,0 +1,437 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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 "s_client.h"
#include "s_internal.h"
#include "client/client.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "s_item.h"
void ScriptApiClient::on_mods_loaded()
{
SCRIPTAPI_PRECHECKHEADER
// Get registered shutdown hooks
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_mods_loaded");
// Call callbacks
runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiClient::on_shutdown()
{
SCRIPTAPI_PRECHECKHEADER
// Get registered shutdown hooks
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_shutdown");
// Call callbacks
runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
bool ScriptApiClient::on_sending_message(const std::string &message)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_sending_chat_message");
// Call callbacks
lua_pushstring(L, message.c_str());
runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_receiving_message(const std::string &message)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_receiving_chat_message");
// Call callbacks
lua_pushstring(L, message.c_str());
runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
return readParam<bool>(L, -1);
}
void ScriptApiClient::on_damage_taken(int32_t damage_amount)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_damage_taken");
// Call callbacks
lua_pushinteger(L, damage_amount);
runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
}
void ScriptApiClient::on_hp_modification(int32_t newhp)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_hp_modification");
// Call callbacks
lua_pushinteger(L, newhp);
runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC);
}
void ScriptApiClient::on_death()
{
SCRIPTAPI_PRECHECKHEADER
// Get registered shutdown hooks
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_death");
// Call callbacks
runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiClient::environment_step(float dtime)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_globalsteps
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_globalsteps");
// Call callbacks
lua_pushnumber(L, dtime);
try {
runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getClient()->setFatalError(std::string("Client environment_step: ") + e.what() + "\n"
+ script_get_backtrace(L));
}
}
void ScriptApiClient::on_formspec_input(const std::string &formname,
const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_formspec_input");
// Call callbacks
// param 1
lua_pushstring(L, formname.c_str());
// param 2
lua_newtable(L);
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
}
bool ScriptApiClient::on_dignode(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
const NodeDefManager *ndef = getClient()->ndef();
// Get core.registered_on_dignode
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_dignode");
// Push data
push_v3s16(L, p);
pushnode(L, node, ndef);
// Call functions
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return lua_toboolean(L, -1);
}
bool ScriptApiClient::on_punchnode(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
const NodeDefManager *ndef = getClient()->ndef();
// Get core.registered_on_punchgnode
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_punchnode");
// Push data
push_v3s16(L, p);
pushnode(L, node, ndef);
// Call functions
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_placenode(const PointedThing &pointed, const ItemDefinition &item)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_placenode
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_placenode");
// Push data
push_pointed_thing(L, pointed, true);
push_item_definition(L, item);
// Call functions
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_item_use(const ItemStack &item, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_item_use
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_item_use");
// Push data
LuaItemStack::create(L, item);
push_pointed_thing(L, pointed, true);
// Call functions
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_item_activate(const ItemStack &item, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_item_use
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_item_activate");
// Push data
LuaItemStack::create(L, item);
push_pointed_thing(L, pointed, true);
// Call functions
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_recieve_physics_override(float speed, float jump, float gravity, bool sneak, bool sneak_glitch, bool new_move)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_recieve_physics_override
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_recieve_physics_override");
// Push data
push_physics_override(L, speed, jump, gravity, sneak, sneak_glitch, new_move);
// Call functions
runCallbacks(1, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_play_sound(SimpleSoundSpec spec)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_play_sound
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_play_sound");
// Push data
push_soundspec(L, spec);
// Call functions
runCallbacks(1, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_spawn_particle(struct ParticleParameters param)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_spawn_particle");
// Push data
lua_newtable(L);
push_v3f(L, param.pos);
lua_setfield(L, -2, "pos");
push_v3f(L, param.vel);
lua_setfield(L, -2, "velocity");
push_v3f(L, param.acc);
lua_setfield(L, -2, "acceleration");
setfloatfield(L, -1, "expirationtime", param.expirationtime);
setboolfield(L, -1, "collisiondetection", param.collisiondetection);
setboolfield(L, -1, "collision_removal", param.collision_removal);
setboolfield(L, -1, "object_collision", param.object_collision);
setboolfield(L, -1, "vertical", param.vertical);
push_animation_definition(L, param.animation);
lua_setfield(L, -2, "animation");
setstringfield(L, -1, "texture", param.texture);
setintfield(L, -1, "glow", param.glow);
if (param.node.getContent() != CONTENT_IGNORE) {
pushnode(L, param.node, getGameDef()->ndef());
lua_setfield(L, -2, "node");
}
setintfield(L, -1, "node_tile", param.node_tile);
// Call functions
runCallbacks(1, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_particle_spawner(struct ParticleSpawnerParameters param)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_particle_spawner");
// Push data
lua_newtable(L);
push_v3f(L, param.minpos);
lua_setfield(L, -2, "minpos");
push_v3f(L, param.maxpos);
lua_setfield(L, -2, "maxpos");
// Call functions
runCallbacks(1, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_inventory_open(Inventory *inventory)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_inventory_open");
push_inventory(L, inventory);
runCallbacks(1, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
void ScriptApiClient::open_enderchest()
{
SCRIPTAPI_PRECHECKHEADER
PUSH_ERROR_HANDLER(L);
int error_handler = lua_gettop(L) - 1;
lua_insert(L, error_handler);
lua_getglobal(L, "core");
lua_getfield(L, -1, "open_enderchest");
if (lua_isfunction(L, -1))
lua_pcall(L, 0, 0, error_handler);
}
bool ScriptApiClient::on_receiving_inventory_form(std::string formname, std::string formspec)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_receiving_inventory_form");
lua_pushstring(L, formname.c_str());
lua_pushstring(L, formspec.c_str());
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_nodemeta_form_open(v3s16 position, std::string formname, std::string formspec)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_nodemeta_form_open");
push_v3s16(L, position);
lua_pushstring(L, formname.c_str());
lua_pushstring(L, formspec.c_str());
runCallbacks(3, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_sending_inventory_fields(const std::string &formname,
const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_sending_inventory_fields");
// Call callbacks
// param 1
lua_pushstring(L, formname.c_str());
// param 2
lua_newtable(L);
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiClient::on_sending_nodemeta_fields(v3s16 position,
const std::string &formname,
const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_sending_nodemeta_fields");
// Call callbacks
// param 1
push_v3s16(L, position);
// param 2
lua_pushstring(L, formname.c_str());
// param 3
lua_newtable(L);
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->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);
return readParam<bool>(L, -1);
}
void ScriptApiClient::setEnv(ClientEnvironment *env)
{
ScriptApiBase::setEnv(env);
}

View File

@ -0,0 +1,79 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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.
*/
#pragma once
#include "util/pointedthing.h"
#include "cpp_api/s_base.h"
#include "mapnode.h"
#include "itemdef.h"
#include "util/string.h"
#include "util/pointedthing.h"
#include "lua_api/l_item.h"
#include "particles.h"
#ifdef _CRT_MSVCP_CURRENT
#include <cstdint>
#endif
class ClientEnvironment;
class ScriptApiClient : virtual public ScriptApiBase
{
public:
// Calls when mods are loaded
void on_mods_loaded();
// Calls on_shutdown handlers
void on_shutdown();
// Chat message handlers
bool on_sending_message(const std::string &message);
bool on_receiving_message(const std::string &message);
void on_damage_taken(int32_t damage_amount);
void on_hp_modification(int32_t newhp);
void on_death();
void environment_step(float dtime);
void on_formspec_input(const std::string &formname, const StringMap &fields);
bool on_dignode(v3s16 p, MapNode node);
bool on_punchnode(v3s16 p, MapNode node);
bool on_placenode(const PointedThing &pointed, const ItemDefinition &item);
bool on_item_use(const ItemStack &item, const PointedThing &pointed);
bool on_item_activate(const ItemStack &item, const PointedThing &pointed);
bool on_recieve_physics_override(float override_speed, float override_jump, float override_gravity, bool sneak, bool sneak_glitch, bool new_move);
bool on_play_sound(SimpleSoundSpec spec);
bool on_spawn_particle(struct ParticleParameters param);
bool on_particle_spawner(struct ParticleSpawnerParameters param);
bool on_inventory_open(Inventory *inventory);
void open_enderchest();
bool on_receiving_inventory_form(std::string formname, std::string formspec);
bool on_nodemeta_form_open(v3s16 position, std::string formname, std::string formspec);
bool on_sending_inventory_fields(const std::string &formname,
const StringMap &fields);
bool on_sending_nodemeta_fields(v3s16 position,
const std::string &formname, const StringMap &fields);
void setEnv(ClientEnvironment *env);
};

View File

@ -0,0 +1,305 @@
/*
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 "cpp_api/s_internal.h"
#include "log.h"
#include "object_properties.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "server.h"
bool ScriptApiEntity::luaentity_Add(u16 id, const char *name)
{
SCRIPTAPI_PRECHECKHEADER
verbosestream<<"scriptapi_luaentity_add: id="<<id<<" name=\""
<<name<<"\""<<std::endl;
// Get core.registered_entities[name]
lua_getglobal(L, "core");
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
push_objectRef(L, id);
luaL_checktype(L, -1, LUA_TUSERDATA);
if (!luaL_checkudata(L, -1, "ObjectRef"))
luaL_typerror(L, -1, "ObjectRef");
lua_setfield(L, -2, "object");
// core.luaentities[id] = object
lua_getglobal(L, "core");
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;
int error_handler = PUSH_ERROR_HANDLER(L);
// Get core.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
// Get on_activate function
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);
setOriginFromTable(object);
PCALL_RES(lua_pcall(L, 3, 0, error_handler));
} else {
lua_pop(L, 1);
}
lua_pop(L, 2); // Pop object and error handler
}
void ScriptApiEntity::luaentity_Remove(u16 id)
{
SCRIPTAPI_PRECHECKHEADER
verbosestream << "scriptapi_luaentity_rm: id=" << id << std::endl;
// Get core.luaentities table
lua_getglobal(L, "core");
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, core
}
std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id)
{
SCRIPTAPI_PRECHECKHEADER
//infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl;
int error_handler = PUSH_ERROR_HANDLER(L);
// Get core.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
// Get get_staticdata function
lua_getfield(L, -1, "get_staticdata");
if (lua_isnil(L, -1)) {
lua_pop(L, 2); // Pop entity and get_staticdata
return "";
}
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
setOriginFromTable(object);
PCALL_RES(lua_pcall(L, 1, 1, error_handler));
lua_remove(L, object);
lua_remove(L, error_handler);
size_t len = 0;
const char *s = lua_tolstring(L, -1, &len);
lua_pop(L, 1); // Pop static data
return std::string(s, len);
}
void ScriptApiEntity::luaentity_GetProperties(u16 id,
ServerActiveObject *self, ObjectProperties *prop)
{
SCRIPTAPI_PRECHECKHEADER
//infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl;
// Get core.luaentities[id]
luaentity_get(L, id);
// Set default values that differ from ObjectProperties defaults
prop->hp_max = 10;
// Deprecated: read object properties directly
read_object_properties(L, -1, self, prop, getServer()->idef());
// Read initial_properties
lua_getfield(L, -1, "initial_properties");
read_object_properties(L, -1, self, prop, getServer()->idef());
lua_pop(L, 1);
}
void ScriptApiEntity::luaentity_Step(u16 id, float dtime,
const collisionMoveResult *moveresult)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Get core.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)) {
lua_pop(L, 2); // Pop on_step and entity
return;
}
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
lua_pushnumber(L, dtime); // dtime
/* moveresult */
if (moveresult)
push_collision_move_result(L, *moveresult);
else
lua_pushnil(L);
setOriginFromTable(object);
PCALL_RES(lua_pcall(L, 3, 0, error_handler));
lua_pop(L, 2); // Pop object and error handler
}
// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch,
// tool_capabilities, direction, damage)
bool ScriptApiEntity::luaentity_Punch(u16 id,
ServerActiveObject *puncher, float time_from_last_punch,
const ToolCapabilities *toolcap, v3f dir, s16 damage)
{
SCRIPTAPI_PRECHECKHEADER
//infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
int error_handler = PUSH_ERROR_HANDLER(L);
// Get core.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)) {
lua_pop(L, 2); // Pop on_punch and entity
return false;
}
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
objectrefGetOrCreate(L, puncher); // Clicker reference
lua_pushnumber(L, time_from_last_punch);
push_tool_capabilities(L, *toolcap);
push_v3f(L, dir);
lua_pushnumber(L, damage);
setOriginFromTable(object);
PCALL_RES(lua_pcall(L, 6, 1, error_handler));
bool retval = readParam<bool>(L, -1);
lua_pop(L, 2); // Pop object and error handler
return retval;
}
// Calls entity[field](ObjectRef self, ObjectRef sao)
bool ScriptApiEntity::luaentity_run_simple_callback(u16 id,
ServerActiveObject *sao, const char *field)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Get core.luaentities[id]
luaentity_get(L, id);
int object = lua_gettop(L);
// State: object is at top of stack
// Get function
lua_getfield(L, -1, field);
if (lua_isnil(L, -1)) {
lua_pop(L, 2); // Pop callback field and entity
return false;
}
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
objectrefGetOrCreate(L, sao); // killer reference
setOriginFromTable(object);
PCALL_RES(lua_pcall(L, 2, 1, error_handler));
bool retval = readParam<bool>(L, -1);
lua_pop(L, 2); // Pop object and error handler
return retval;
}
bool ScriptApiEntity::luaentity_on_death(u16 id, ServerActiveObject *killer)
{
return luaentity_run_simple_callback(id, killer, "on_death");
}
// Calls entity:on_rightclick(ObjectRef clicker)
void ScriptApiEntity::luaentity_Rightclick(u16 id, ServerActiveObject *clicker)
{
luaentity_run_simple_callback(id, clicker, "on_rightclick");
}
void ScriptApiEntity::luaentity_on_attach_child(u16 id, ServerActiveObject *child)
{
luaentity_run_simple_callback(id, child, "on_attach_child");
}
void ScriptApiEntity::luaentity_on_detach_child(u16 id, ServerActiveObject *child)
{
luaentity_run_simple_callback(id, child, "on_detach_child");
}
void ScriptApiEntity::luaentity_on_detach(u16 id, ServerActiveObject *parent)
{
luaentity_run_simple_callback(id, parent, "on_detach");
}

View File

@ -0,0 +1,53 @@
/*
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "irr_v3d.h"
struct ObjectProperties;
struct ToolCapabilities;
struct collisionMoveResult;
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,
ServerActiveObject *self, ObjectProperties *prop);
void luaentity_Step(u16 id, float dtime,
const collisionMoveResult *moveresult);
bool luaentity_Punch(u16 id,
ServerActiveObject *puncher, float time_from_last_punch,
const ToolCapabilities *toolcap, v3f dir, s16 damage);
bool luaentity_on_death(u16 id, ServerActiveObject *killer);
void luaentity_Rightclick(u16 id, ServerActiveObject *clicker);
void luaentity_on_attach_child(u16 id, ServerActiveObject *child);
void luaentity_on_detach_child(u16 id, ServerActiveObject *child);
void luaentity_on_detach(u16 id, ServerActiveObject *parent);
private:
bool luaentity_run_simple_callback(u16 id, ServerActiveObject *sao,
const char *field);
};

View File

@ -0,0 +1,263 @@
/*
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 "cpp_api/s_internal.h"
#include "common/c_converter.h"
#include "log.h"
#include "environment.h"
#include "mapgen/mapgen.h"
#include "lua_api/l_env.h"
#include "server.h"
void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
u32 blockseed)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_generateds
lua_getglobal(L, "core");
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 core.registered_globalsteps
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_globalsteps");
// Call callbacks
lua_pushnumber(L, dtime);
try {
runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getServer()->setAsyncFatalError(
std::string("environment_Step: ") + e.what() + "\n"
+ script_get_backtrace(L));
}
}
void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &type)
{
SCRIPTAPI_PRECHECKHEADER
if (player == NULL)
return;
// Get minetest.registered_playerevents
lua_getglobal(L, "minetest");
lua_getfield(L, -1, "registered_playerevents");
// Call callbacks
objectrefGetOrCreate(L, player); // player
lua_pushstring(L,type.c_str()); // event type
try {
runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getServer()->setAsyncFatalError(
std::string("player_event: ") + e.what() + "\n"
+ script_get_backtrace(L) );
}
}
void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
{
SCRIPTAPI_PRECHECKHEADER
verbosestream << "ScriptApiEnv: Environment initialized" << std::endl;
setEnv(env);
/*
Add {Loading,Active}BlockModifiers to environment
*/
// Get core.registered_abms
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_abms");
int registered_abms = lua_gettop(L);
if (!lua_istable(L, registered_abms)) {
lua_pop(L, 1);
throw LuaError("core.registered_abms was not a lua table, as expected.");
}
lua_pushnil(L);
while (lua_next(L, registered_abms)) {
// key at index -2 and value at index -1
int id = lua_tonumber(L, -2);
int current_abm = lua_gettop(L);
std::vector<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)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
trigger_contents.emplace_back(readParam<std::string>(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if (lua_isstring(L, -1)) {
trigger_contents.emplace_back(readParam<std::string>(L, -1));
}
lua_pop(L, 1);
std::vector<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)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
required_neighbors.emplace_back(readParam<std::string>(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if (lua_isstring(L, -1)) {
required_neighbors.emplace_back(readParam<std::string>(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);
bool simple_catch_up = true;
getboolfield(L, current_abm, "catch_up", simple_catch_up);
lua_getfield(L, current_abm, "action");
luaL_checktype(L, current_abm + 1, LUA_TFUNCTION);
lua_pop(L, 1);
LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors,
trigger_interval, trigger_chance, simple_catch_up);
env->addActiveBlockModifier(abm);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
lua_pop(L, 1);
// Get core.registered_lbms
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_lbms");
int registered_lbms = lua_gettop(L);
if (!lua_istable(L, registered_lbms)) {
lua_pop(L, 1);
throw LuaError("core.registered_lbms was not a lua table, as expected.");
}
lua_pushnil(L);
while (lua_next(L, registered_lbms)) {
// key at index -2 and value at index -1
int id = lua_tonumber(L, -2);
int current_lbm = lua_gettop(L);
std::set<std::string> trigger_contents;
lua_getfield(L, current_lbm, "nodenames");
if (lua_istable(L, -1)) {
int table = lua_gettop(L);
lua_pushnil(L);
while (lua_next(L, table)) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
trigger_contents.insert(readParam<std::string>(L, -1));
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
} else if (lua_isstring(L, -1)) {
trigger_contents.insert(readParam<std::string>(L, -1));
}
lua_pop(L, 1);
std::string name;
getstringfield(L, current_lbm, "name", name);
bool run_at_every_load = getboolfield_default(L, current_lbm,
"run_at_every_load", false);
lua_getfield(L, current_lbm, "action");
luaL_checktype(L, current_lbm + 1, LUA_TFUNCTION);
lua_pop(L, 1);
LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name,
run_at_every_load);
env->addLoadingBlockModifierDef(lbm);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
lua_pop(L, 1);
}
void ScriptApiEnv::on_emerge_area_completion(
v3s16 blockpos, int action, ScriptCallbackState *state)
{
Server *server = getServer();
// This function should be executed with envlock held.
// The caller (LuaEmergeAreaCallback in src/script/lua_api/l_env.cpp)
// should have obtained the lock.
// Note that the order of these locks is important! Envlock must *ALWAYS*
// be acquired before attempting to acquire scriptlock, or else ServerThread
// will try to acquire scriptlock after it already owns envlock, thus
// deadlocking EmergeThread and ServerThread
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
lua_rawgeti(L, LUA_REGISTRYINDEX, state->callback_ref);
luaL_checktype(L, -1, LUA_TFUNCTION);
push_v3s16(L, blockpos);
lua_pushinteger(L, action);
lua_pushinteger(L, state->refcount);
lua_rawgeti(L, LUA_REGISTRYINDEX, state->args_ref);
setOriginDirect(state->origin.c_str());
try {
PCALL_RES(lua_pcall(L, 4, 0, error_handler));
} catch (LuaError &e) {
server->setAsyncFatalError(
std::string("on_emerge_area_completion: ") + e.what() + "\n"
+ script_get_backtrace(L));
}
lua_pop(L, 1); // Pop error handler
if (state->refcount == 0) {
luaL_unref(L, LUA_REGISTRYINDEX, state->callback_ref);
luaL_unref(L, LUA_REGISTRYINDEX, state->args_ref);
}
}

View File

@ -0,0 +1,45 @@
/*
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "irr_v3d.h"
class ServerEnvironment;
struct ScriptCallbackState;
class ScriptApiEnv : virtual public ScriptApiBase
{
public:
// Called on environment step
void environment_Step(float dtime);
// Called after generating a piece of map
void environment_OnGenerated(v3s16 minp, v3s16 maxp, u32 blockseed);
// Called on player event
void player_event(ServerActiveObject *player, const std::string &type);
// Called after emerge of a block queued from core.emerge_area()
void on_emerge_area_completion(v3s16 blockpos, int action,
ScriptCallbackState *state);
void initializeEnvironment(ServerEnvironment *env);
};

View File

@ -0,0 +1,85 @@
/*
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.
*/
/******************************************************************************/
/******************************************************************************/
/* WARNING!!!! do NOT add this header in any include file or any code file */
/* not being a modapi file!!!!!!!! */
/******************************************************************************/
/******************************************************************************/
#pragma once
#include <thread>
#include "common/c_internal.h"
#include "cpp_api/s_base.h"
#include "threading/mutex_auto_lock.h"
#ifdef SCRIPTAPI_LOCK_DEBUG
#include <cassert>
class LockChecker {
public:
LockChecker(int *recursion_counter, std::thread::id *owning_thread)
{
m_lock_recursion_counter = recursion_counter;
m_owning_thread = owning_thread;
m_original_level = *recursion_counter;
if (*m_lock_recursion_counter > 0) {
assert(*m_owning_thread == std::this_thread::get_id());
} else {
*m_owning_thread = std::this_thread::get_id();
}
(*m_lock_recursion_counter)++;
}
~LockChecker()
{
assert(*m_owning_thread == std::this_thread::get_id());
assert(*m_lock_recursion_counter > 0);
(*m_lock_recursion_counter)--;
assert(*m_lock_recursion_counter == m_original_level);
}
private:
int *m_lock_recursion_counter;
int m_original_level;
std::thread::id *m_owning_thread;
};
#define SCRIPTAPI_LOCK_CHECK \
LockChecker scriptlock_checker( \
&this->m_lock_recursion_count, \
&this->m_owning_thread)
#else
#define SCRIPTAPI_LOCK_CHECK while(0)
#endif
#define SCRIPTAPI_PRECHECKHEADER \
RecursiveMutexAutoLock scriptlock(this->m_luastackmutex); \
SCRIPTAPI_LOCK_CHECK; \
realityCheck(); \
lua_State *L = getStack(); \
assert(lua_checkstack(L, 20)); \
StackUnroller stack_unroller(L);

View File

@ -0,0 +1,225 @@
/*
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 "cpp_api/s_internal.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 MoveAction &ma, int count,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getDetachedInventoryCallback(ma.from_inv.name, "allow_move"))
return count;
// function(inv, from_list, from_index, to_list, to_index, count, player)
// inv
InvRef::create(L, ma.from_inv);
lua_pushstring(L, ma.from_list.c_str()); // from_list
lua_pushinteger(L, ma.from_i + 1); // from_index
lua_pushstring(L, ma.to_list.c_str()); // to_list
lua_pushinteger(L, ma.to_i + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 7, 1, error_handler));
if(!lua_isnumber(L, -1))
throw LuaError("allow_move should return a number. name=" + ma.from_inv.name);
int ret = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return ret;
}
// Return number of accepted items to be put
int ScriptApiDetached::detached_inventory_AllowPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getDetachedInventoryCallback(ma.to_inv.name, "allow_put"))
return stack.count; // All will be accepted
// Call function(inv, listname, index, stack, player)
InvRef::create(L, ma.to_inv); // inv
lua_pushstring(L, ma.to_list.c_str()); // listname
lua_pushinteger(L, ma.to_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_put should return a number. name=" + ma.to_inv.name);
int ret = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return ret;
}
// Return number of accepted items to be taken
int ScriptApiDetached::detached_inventory_AllowTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getDetachedInventoryCallback(ma.from_inv.name, "allow_take"))
return stack.count; // All will be accepted
// Call function(inv, listname, index, stack, player)
InvRef::create(L, ma.from_inv); // inv
lua_pushstring(L, ma.from_list.c_str()); // listname
lua_pushinteger(L, ma.from_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_take should return a number. name=" + ma.from_inv.name);
int ret = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return ret;
}
// Report moved items
void ScriptApiDetached::detached_inventory_OnMove(
const MoveAction &ma, int count,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getDetachedInventoryCallback(ma.from_inv.name, "on_move"))
return;
// function(inv, from_list, from_index, to_list, to_index, count, player)
// inv
InvRef::create(L, ma.from_inv);
lua_pushstring(L, ma.from_list.c_str()); // from_list
lua_pushinteger(L, ma.from_i + 1); // from_index
lua_pushstring(L, ma.to_list.c_str()); // to_list
lua_pushinteger(L, ma.to_i + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 7, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
// Report put items
void ScriptApiDetached::detached_inventory_OnPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getDetachedInventoryCallback(ma.to_inv.name, "on_put"))
return;
// Call function(inv, listname, index, stack, player)
// inv
InvRef::create(L, ma.to_inv);
lua_pushstring(L, ma.to_list.c_str()); // listname
lua_pushinteger(L, ma.to_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
// Report taken items
void ScriptApiDetached::detached_inventory_OnTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getDetachedInventoryCallback(ma.from_inv.name, "on_take"))
return;
// Call function(inv, listname, index, stack, player)
// inv
InvRef::create(L, ma.from_inv);
lua_pushstring(L, ma.from_list.c_str()); // listname
lua_pushinteger(L, ma.from_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
// Retrieves core.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, "core");
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<<"Detached inventory \""<<name<<"\" not defined"<<std::endl;
lua_pop(L, 1);
return false;
}
setOriginFromTable(-1);
lua_getfield(L, -1, callbackname);
lua_remove(L, -2);
// Should be a function or nil
if (lua_type(L, -1) == LUA_TFUNCTION) {
return true;
}
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
return false;
}
errorstream << "Detached inventory \"" << name << "\" callback \""
<< callbackname << "\" is not a function" << std::endl;
lua_pop(L, 1);
return false;
}

View File

@ -0,0 +1,59 @@
/*
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.
*/
#pragma once
#include "cpp_api/s_base.h"
struct MoveAction;
struct ItemStack;
class ScriptApiDetached
: virtual public ScriptApiBase
{
public:
/* Detached inventory callbacks */
// Return number of accepted items to be moved
int detached_inventory_AllowMove(
const MoveAction &ma, int count,
ServerActiveObject *player);
// Return number of accepted items to be put
int detached_inventory_AllowPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Return number of accepted items to be taken
int detached_inventory_AllowTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Report moved items
void detached_inventory_OnMove(
const MoveAction &ma, int count,
ServerActiveObject *player);
// Report put items
void detached_inventory_OnPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Report taken items
void detached_inventory_OnTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
private:
bool getDetachedInventoryCallback(
const std::string &name, const char *callbackname);
};

View File

@ -0,0 +1,263 @@
/*
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 "cpp_api/s_internal.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "lua_api/l_item.h"
#include "lua_api/l_inventory.h"
#include "server.h"
#include "log.h"
#include "util/pointedthing.h"
#include "inventory.h"
#include "inventorymanager.h"
bool ScriptApiItem::item_OnDrop(ItemStack &item,
ServerActiveObject *dropper, v3f pos)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_drop"))
return false;
// Call function
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, dropper);
pushFloatPos(L, pos);
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
bool ScriptApiItem::item_OnPlace(ItemStack &item,
ServerActiveObject *placer, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_place"))
return false;
// Call function
LuaItemStack::create(L, item);
if (!placer)
lua_pushnil(L);
else
objectrefGetOrCreate(L, placer);
pushPointedThing(pointed);
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
bool ScriptApiItem::item_OnUse(ItemStack &item,
ServerActiveObject *user, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Push callback function on stack
if (!getItemCallback(item.name.c_str(), "on_use"))
return false;
// Call function
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, user);
pushPointedThing(pointed);
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if(!lua_isnil(L, -1)) {
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item,
ServerActiveObject *user, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
if (!getItemCallback(item.name.c_str(), "on_secondary_use"))
return false;
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, user);
pushPointedThing(pointed);
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
lua_getglobal(L, "core");
lua_getfield(L, -1, "on_craft");
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, user);
// Push inventory list
std::vector<ItemStack> items;
for (u32 i = 0; i < old_craft_grid->getSize(); i++) {
items.push_back(old_craft_grid->getItem(i));
}
push_items(L, items);
InvRef::create(L, craft_inv);
PCALL_RES(lua_pcall(L, 4, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
{
SCRIPTAPI_PRECHECKHEADER
sanity_check(old_craft_grid);
int error_handler = PUSH_ERROR_HANDLER(L);
lua_getglobal(L, "core");
lua_getfield(L, -1, "craft_predict");
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, user);
//Push inventory list
std::vector<ItemStack> items;
for (u32 i = 0; i < old_craft_grid->getSize(); i++) {
items.push_back(old_craft_grid->getItem(i));
}
push_items(L, items);
InvRef::create(L, craft_inv);
PCALL_RES(lua_pcall(L, 4, 1, error_handler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L, -1, getServer()->idef());
} catch (LuaError &e) {
throw LuaError(std::string(e.what()) + ". item=" + item.name);
}
}
lua_pop(L, 2); // Pop item and error handler
return true;
}
// Retrieves core.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 core.registered_items[name] doesn't exist, core.nodedef_default
// is tried instead so unknown items can still be manipulated to some degree
bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname,
const v3s16 *p)
{
lua_State* L = getStack();
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_items");
lua_remove(L, -2); // Remove core
luaL_checktype(L, -1, LUA_TTABLE);
lua_getfield(L, -1, name);
lua_remove(L, -2); // Remove registered_items
// Should be a table
if (lua_type(L, -1) != LUA_TTABLE) {
// Report error and clean up
errorstream << "Item \"" << name << "\" not defined";
if (p)
errorstream << " at position " << PP(*p);
errorstream << std::endl;
lua_pop(L, 1);
// Try core.nodedef_default instead
lua_getglobal(L, "core");
lua_getfield(L, -1, "nodedef_default");
lua_remove(L, -2);
luaL_checktype(L, -1, LUA_TTABLE);
}
setOriginFromTable(-1);
lua_getfield(L, -1, callbackname);
lua_remove(L, -2); // Remove item def
// Should be a function or nil
if (lua_type(L, -1) == LUA_TFUNCTION) {
return true;
}
if (!lua_isnil(L, -1)) {
errorstream << "Item \"" << name << "\" callback \""
<< callbackname << "\" is not a function" << std::endl;
}
lua_pop(L, 1);
return false;
}
void ScriptApiItem::pushPointedThing(const PointedThing &pointed, bool hitpoint)
{
lua_State* L = getStack();
push_pointed_thing(L, pointed, false, hitpoint);
}

View File

@ -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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "irr_v3d.h"
struct PointedThing;
struct ItemStack;
class ServerActiveObject;
struct ItemDefinition;
class LuaItemStack;
class ModApiItemMod;
class InventoryList;
struct InventoryLocation;
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);
bool item_OnSecondaryUse(ItemStack &item,
ServerActiveObject *user, const PointedThing &pointed);
bool item_OnCraft(ItemStack &item, ServerActiveObject *user,
const InventoryList *old_craft_grid, const InventoryLocation &craft_inv);
bool item_CraftPredict(ItemStack &item, ServerActiveObject *user,
const InventoryList *old_craft_grid, const InventoryLocation &craft_inv);
protected:
friend class LuaItemStack;
friend class ModApiItemMod;
bool getItemCallback(const char *name, const char *callbackname, const v3s16 *p = nullptr);
/*!
* Pushes a `pointed_thing` tabe to the stack.
* \param hitpoint If true, the exact pointing location is also pushed
*/
void pushPointedThing(const PointedThing &pointed, bool hitpoint = false);
};

View File

@ -0,0 +1,93 @@
/*
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_mainmenu.h"
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"
void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "gamedata");
int gamedata_idx = lua_gettop(L);
lua_pushstring(L, "errormessage");
if (!data->errormessage.empty()) {
lua_pushstring(L, data->errormessage.c_str());
} else {
lua_pushnil(L);
}
lua_settable(L, gamedata_idx);
setboolfield(L, gamedata_idx, "reconnect_requested", data->reconnect_requested);
lua_pop(L, 1);
}
void ScriptApiMainMenu::handleMainMenuEvent(std::string text)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Get handler function
lua_getglobal(L, "core");
lua_getfield(L, -1, "event_handler");
lua_remove(L, -2); // Remove core
if (lua_isnil(L, -1)) {
lua_pop(L, 1); // Pop event_handler
return;
}
luaL_checktype(L, -1, LUA_TFUNCTION);
// Call it
lua_pushstring(L, text.c_str());
PCALL_RES(lua_pcall(L, 1, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Get handler function
lua_getglobal(L, "core");
lua_getfield(L, -1, "button_handler");
lua_remove(L, -2); // Remove core
if (lua_isnil(L, -1)) {
lua_pop(L, 1); // Pop button handler
return;
}
luaL_checktype(L, -1, LUA_TFUNCTION);
// Convert fields to a Lua table
lua_newtable(L);
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
// Call it
PCALL_RES(lua_pcall(L, 1, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}

View File

@ -0,0 +1,45 @@
/*
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "util/string.h"
#include "gui/guiMainMenu.h"
class ScriptApiMainMenu : virtual public ScriptApiBase {
public:
/**
* Hand over MainMenuDataForScript to lua to inform lua of the content
* @param data the data
*/
void setMainMenuData(MainMenuDataForScript *data);
/**
* process events received from formspec
* @param text events in textual form
*/
void handleMainMenuEvent(std::string text);
/**
* process field data recieved from formspec
* @param fields data in field format
*/
void handleMainMenuButtons(const StringMap &fields);
};

View File

@ -0,0 +1,50 @@
/*
Minetest
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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 "s_modchannels.h"
#include "s_internal.h"
void ScriptApiModChannels::on_modchannel_message(const std::string &channel,
const std::string &sender, const std::string &message)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_generateds
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_modchannel_message");
// Call callbacks
lua_pushstring(L, channel.c_str());
lua_pushstring(L, sender.c_str());
lua_pushstring(L, message.c_str());
runCallbacks(3, RUN_CALLBACKS_MODE_AND);
}
void ScriptApiModChannels::on_modchannel_signal(
const std::string &channel, ModChannelSignal signal)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_generateds
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_modchannel_signal");
// Call callbacks
lua_pushstring(L, channel.c_str());
lua_pushinteger(L, (int)signal);
runCallbacks(2, RUN_CALLBACKS_MODE_AND);
}

View File

@ -0,0 +1,31 @@
/*
Minetest
Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "modchannels.h"
class ScriptApiModChannels : virtual public ScriptApiBase
{
public:
void on_modchannel_message(const std::string &channel, const std::string &sender,
const std::string &message);
void on_modchannel_signal(const std::string &channel, ModChannelSignal signal);
};

View File

@ -0,0 +1,272 @@
/*
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 "cpp_api/s_internal.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "nodedef.h"
#include "server.h"
#include "environment.h"
#include "util/pointedthing.h"
// Should be ordered exactly like enum NodeDrawType in nodedef.h
struct EnumString ScriptApiNode::es_DrawType[] =
{
{NDT_NORMAL, "normal"},
{NDT_AIRLIKE, "airlike"},
{NDT_LIQUID, "liquid"},
{NDT_FLOWINGLIQUID, "flowingliquid"},
{NDT_GLASSLIKE, "glasslike"},
{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"},
{NDT_GLASSLIKE_FRAMED, "glasslike_framed"},
{NDT_FIRELIKE, "firelike"},
{NDT_GLASSLIKE_FRAMED_OPTIONAL, "glasslike_framed_optional"},
{NDT_MESH, "mesh"},
{NDT_PLANTLIKE_ROOTED, "plantlike_rooted"},
{0, NULL},
};
struct EnumString ScriptApiNode::es_ContentParamType2[] =
{
{CPT2_NONE, "none"},
{CPT2_FULL, "full"},
{CPT2_FLOWINGLIQUID, "flowingliquid"},
{CPT2_FACEDIR, "facedir"},
{CPT2_WALLMOUNTED, "wallmounted"},
{CPT2_LEVELED, "leveled"},
{CPT2_DEGROTATE, "degrotate"},
{CPT2_MESHOPTIONS, "meshoptions"},
{CPT2_COLOR, "color"},
{CPT2_COLORED_FACEDIR, "colorfacedir"},
{CPT2_COLORED_WALLMOUNTED, "colorwallmounted"},
{CPT2_GLASSLIKE_LIQUID_LEVEL, "glasslikeliquidlevel"},
{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"},
{NODEBOX_LEVELED, "leveled"},
{NODEBOX_CONNECTED, "connected"},
{0, NULL},
};
bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
ServerActiveObject *puncher, const PointedThing &pointed)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_punch", &p))
return false;
// Call function
push_v3s16(L, p);
pushnode(L, node, ndef);
objectrefGetOrCreate(L, puncher);
pushPointedThing(pointed);
PCALL_RES(lua_pcall(L, 4, 0, error_handler));
lua_pop(L, 1); // Pop error handler
return true;
}
bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node,
ServerActiveObject *digger)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_dig", &p))
return false;
// Call function
push_v3s16(L, p);
pushnode(L, node, ndef);
objectrefGetOrCreate(L, digger);
PCALL_RES(lua_pcall(L, 3, 0, error_handler));
lua_pop(L, 1); // Pop error handler
return true;
}
void ScriptApiNode::node_on_construct(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_construct", &p))
return;
// Call function
push_v3s16(L, p);
PCALL_RES(lua_pcall(L, 1, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_destruct", &p))
return;
// Call function
push_v3s16(L, p);
PCALL_RES(lua_pcall(L, 1, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
bool ScriptApiNode::node_on_flood(v3s16 p, MapNode node, MapNode newnode)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_flood", &p))
return false;
// Call function
push_v3s16(L, p);
pushnode(L, node, ndef);
pushnode(L, newnode, ndef);
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
lua_remove(L, error_handler);
return readParam<bool>(L, -1, false);
}
void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "after_destruct", &p))
return;
// Call function
push_v3s16(L, p);
pushnode(L, node, ndef);
PCALL_RES(lua_pcall(L, 2, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_timer", &p))
return false;
// Call function
push_v3s16(L, p);
lua_pushnumber(L,dtime);
PCALL_RES(lua_pcall(L, 2, 1, error_handler));
lua_remove(L, error_handler);
return readParam<bool>(L, -1, false);
}
void ScriptApiNode::node_on_receive_fields(v3s16 p,
const std::string &formname,
const StringMap &fields,
ServerActiveObject *sender)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = getEnv()->getMap().getNode(p);
if (node.getContent() == CONTENT_IGNORE)
return;
// Push callback function on stack
if (!getItemCallback(ndef->get(node).name.c_str(), "on_receive_fields", &p))
return;
// Call function
push_v3s16(L, p); // pos
lua_pushstring(L, formname.c_str()); // formname
lua_newtable(L); // fields
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
objectrefGetOrCreate(L, sender); // player
PCALL_RES(lua_pcall(L, 4, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}

View File

@ -0,0 +1,57 @@
/*
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.
*/
#pragma once
#include "irr_v3d.h"
#include "cpp_api/s_base.h"
#include "cpp_api/s_nodemeta.h"
#include "util/string.h"
struct MapNode;
class ServerActiveObject;
class ScriptApiNode
: virtual public ScriptApiBase,
public ScriptApiNodemeta
{
public:
ScriptApiNode() = default;
virtual ~ScriptApiNode() = default;
bool node_on_punch(v3s16 p, MapNode node,
ServerActiveObject *puncher, const PointedThing &pointed);
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);
bool node_on_flood(v3s16 p, MapNode node, MapNode newnode);
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 StringMap &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[];
};

View File

@ -0,0 +1,232 @@
/*
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 "cpp_api/s_internal.h"
#include "common/c_converter.h"
#include "nodedef.h"
#include "mapnode.h"
#include "server.h"
#include "environment.h"
#include "lua_api/l_item.h"
// Return number of accepted items to be moved
int ScriptApiNodemeta::nodemeta_inventory_AllowMove(
const MoveAction &ma, int count,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = getEnv()->getMap().getNode(ma.to_inv.p);
if (node.getContent() == CONTENT_IGNORE)
return 0;
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_move", &ma.to_inv.p))
return count;
// function(pos, from_list, from_index, to_list, to_index, count, player)
push_v3s16(L, ma.to_inv.p); // pos
lua_pushstring(L, ma.from_list.c_str()); // from_list
lua_pushinteger(L, ma.from_i + 1); // from_index
lua_pushstring(L, ma.to_list.c_str()); // to_list
lua_pushinteger(L, ma.to_i + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 7, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_move should"
" return a number, guilty node: " + nodename);
int num = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return num;
}
// Return number of accepted items to be put
int ScriptApiNodemeta::nodemeta_inventory_AllowPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = getEnv()->getMap().getNode(ma.to_inv.p);
if (node.getContent() == CONTENT_IGNORE)
return 0;
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_put", &ma.to_inv.p))
return stack.count;
// Call function(pos, listname, index, stack, player)
push_v3s16(L, ma.to_inv.p); // pos
lua_pushstring(L, ma.to_list.c_str()); // listname
lua_pushinteger(L, ma.to_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if(!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_put should"
" return a number, guilty node: " + nodename);
int num = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return num;
}
// Return number of accepted items to be taken
int ScriptApiNodemeta::nodemeta_inventory_AllowTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = getEnv()->getMap().getNode(ma.from_inv.p);
if (node.getContent() == CONTENT_IGNORE)
return 0;
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "allow_metadata_inventory_take", &ma.from_inv.p))
return stack.count;
// Call function(pos, listname, index, count, player)
push_v3s16(L, ma.from_inv.p); // pos
lua_pushstring(L, ma.from_list.c_str()); // listname
lua_pushinteger(L, ma.from_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 1, error_handler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_take should"
" return a number, guilty node: " + nodename);
int num = luaL_checkinteger(L, -1);
lua_pop(L, 2); // Pop integer and error handler
return num;
}
// Report moved items
void ScriptApiNodemeta::nodemeta_inventory_OnMove(
const MoveAction &ma, int count,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = getEnv()->getMap().getNode(ma.from_inv.p);
if (node.getContent() == CONTENT_IGNORE)
return;
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_move", &ma.from_inv.p))
return;
// function(pos, from_list, from_index, to_list, to_index, count, player)
push_v3s16(L, ma.from_inv.p); // pos
lua_pushstring(L, ma.from_list.c_str()); // from_list
lua_pushinteger(L, ma.from_i + 1); // from_index
lua_pushstring(L, ma.to_list.c_str()); // to_list
lua_pushinteger(L, ma.to_i + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 7, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
// Report put items
void ScriptApiNodemeta::nodemeta_inventory_OnPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = getEnv()->getMap().getNode(ma.to_inv.p);
if (node.getContent() == CONTENT_IGNORE)
return;
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_put", &ma.to_inv.p))
return;
// Call function(pos, listname, index, stack, player)
push_v3s16(L, ma.to_inv.p); // pos
lua_pushstring(L, ma.to_list.c_str()); // listname
lua_pushinteger(L, ma.to_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
// Report taken items
void ScriptApiNodemeta::nodemeta_inventory_OnTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
const NodeDefManager *ndef = getServer()->ndef();
// If node doesn't exist, we don't know what callback to call
MapNode node = getEnv()->getMap().getNode(ma.from_inv.p);
if (node.getContent() == CONTENT_IGNORE)
return;
// Push callback function on stack
std::string nodename = ndef->get(node).name;
if (!getItemCallback(nodename.c_str(), "on_metadata_inventory_take", &ma.from_inv.p))
return;
// Call function(pos, listname, index, stack, player)
push_v3s16(L, ma.from_inv.p); // pos
lua_pushstring(L, ma.from_list.c_str()); // listname
lua_pushinteger(L, ma.from_i + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
PCALL_RES(lua_pcall(L, 5, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}

View File

@ -0,0 +1,63 @@
/*
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "cpp_api/s_item.h"
#include "irr_v3d.h"
struct MoveAction;
struct ItemStack;
class ScriptApiNodemeta
: virtual public ScriptApiBase,
public ScriptApiItem
{
public:
ScriptApiNodemeta() = default;
virtual ~ScriptApiNodemeta() = default;
// Return number of accepted items to be moved
int nodemeta_inventory_AllowMove(
const MoveAction &ma, int count,
ServerActiveObject *player);
// Return number of accepted items to be put
int nodemeta_inventory_AllowPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Return number of accepted items to be taken
int nodemeta_inventory_AllowTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Report moved items
void nodemeta_inventory_OnMove(
const MoveAction &ma, int count,
ServerActiveObject *player);
// Report put items
void nodemeta_inventory_OnPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Report taken items
void nodemeta_inventory_OnTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
private:
};

View File

@ -0,0 +1,369 @@
/*
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"
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "debug.h"
#include "inventorymanager.h"
#include "lua_api/l_inventory.h"
#include "lua_api/l_item.h"
#include "util/string.h"
void ScriptApiPlayer::on_newplayer(ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_newplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_newplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player, const PlayerHPChangeReason &reason)
{
SCRIPTAPI_PRECHECKHEADER
// Get callback table
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_dieplayers");
// Push arguments
objectrefGetOrCreate(L, player);
pushPlayerHPChangeReason(L, reason);
// Run callbacks
runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}
bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player,
ServerActiveObject *hitter,
float time_from_last_punch,
const ToolCapabilities *toolcap,
v3f dir,
s16 damage)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_punchplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_punchplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
objectrefGetOrCreate(L, hitter);
lua_pushnumber(L, time_from_last_punch);
push_tool_capabilities(L, *toolcap);
push_v3f(L, dir);
lua_pushnumber(L, damage);
runCallbacks(6, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
s32 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player,
s32 hp_change, const PlayerHPChangeReason &reason)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
// Get core.registered_on_player_hpchange
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_player_hpchange");
lua_remove(L, -2);
// Push arguments
objectrefGetOrCreate(L, player);
lua_pushnumber(L, hp_change);
pushPlayerHPChangeReason(L, reason);
// Call callbacks
PCALL_RES(lua_pcall(L, 3, 1, error_handler));
hp_change = lua_tointeger(L, -1);
lua_pop(L, 2); // Pop result and error handler
return hp_change;
}
bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_respawnplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_respawnplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
runCallbacks(1, RUN_CALLBACKS_MODE_OR);
return readParam<bool>(L, -1);
}
bool ScriptApiPlayer::on_prejoinplayer(
const std::string &name,
const std::string &ip,
std::string *reason)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_prejoinplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_prejoinplayers");
lua_pushstring(L, name.c_str());
lua_pushstring(L, ip.c_str());
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
if (lua_isstring(L, -1)) {
reason->assign(readParam<std::string>(L, -1));
return true;
}
return false;
}
bool ScriptApiPlayer::can_bypass_userlimit(const std::string &name, const std::string &ip)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_prejoinplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_can_bypass_userlimit");
lua_pushstring(L, name.c_str());
lua_pushstring(L, ip.c_str());
runCallbacks(2, RUN_CALLBACKS_MODE_OR);
return lua_toboolean(L, -1);
}
void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player, s64 last_login)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_joinplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_joinplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
if (last_login != -1)
lua_pushinteger(L, last_login);
else
lua_pushnil(L);
runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player,
bool timeout)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_leaveplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_leaveplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
lua_pushboolean(L, timeout);
runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_cheat(ServerActiveObject *player,
const std::string &cheat_type)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_cheats
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_cheats");
// Call callbacks
objectrefGetOrCreate(L, player);
lua_newtable(L);
lua_pushlstring(L, cheat_type.c_str(), cheat_type.size());
lua_setfield(L, -2, "type");
runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player,
const std::string &formname,
const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_player_receive_fields
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_player_receive_fields");
// Call callbacks
// param 1
objectrefGetOrCreate(L, player);
// param 2
lua_pushstring(L, formname.c_str());
// param 3
lua_newtable(L);
StringMap::const_iterator it;
for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->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);
}
void ScriptApiPlayer::on_authplayer(const std::string &name, const std::string &ip, bool is_success)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_authplayers
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_authplayers");
// Call callbacks
lua_pushstring(L, name.c_str());
lua_pushstring(L, ip.c_str());
lua_pushboolean(L, is_success);
runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::pushMoveArguments(
const MoveAction &ma, int count,
ServerActiveObject *player)
{
lua_State *L = getStack();
objectrefGetOrCreate(L, player); // player
lua_pushstring(L, "move"); // action
InvRef::create(L, ma.from_inv); // inventory
lua_newtable(L);
{
// Table containing the action information
lua_pushstring(L, ma.from_list.c_str());
lua_setfield(L, -2, "from_list");
lua_pushstring(L, ma.to_list.c_str());
lua_setfield(L, -2, "to_list");
lua_pushinteger(L, ma.from_i + 1);
lua_setfield(L, -2, "from_index");
lua_pushinteger(L, ma.to_i + 1);
lua_setfield(L, -2, "to_index");
lua_pushinteger(L, count);
lua_setfield(L, -2, "count");
}
}
void ScriptApiPlayer::pushPutTakeArguments(
const char *method, const InventoryLocation &loc,
const std::string &listname, int index, const ItemStack &stack,
ServerActiveObject *player)
{
lua_State *L = getStack();
objectrefGetOrCreate(L, player); // player
lua_pushstring(L, method); // action
InvRef::create(L, loc); // inventory
lua_newtable(L);
{
// Table containing the action information
lua_pushstring(L, listname.c_str());
lua_setfield(L, -2, "listname");
lua_pushinteger(L, index + 1);
lua_setfield(L, -2, "index");
LuaItemStack::create(L, stack);
lua_setfield(L, -2, "stack");
}
}
// Return number of accepted items to be moved
int ScriptApiPlayer::player_inventory_AllowMove(
const MoveAction &ma, int count,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_allow_player_inventory_actions");
pushMoveArguments(ma, count, player);
runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC);
return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : count;
}
// Return number of accepted items to be put
int ScriptApiPlayer::player_inventory_AllowPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_allow_player_inventory_actions");
pushPutTakeArguments("put", ma.to_inv, ma.to_list, ma.to_i, stack, player);
runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC);
return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : stack.count;
}
// Return number of accepted items to be taken
int ScriptApiPlayer::player_inventory_AllowTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_allow_player_inventory_actions");
pushPutTakeArguments("take", ma.from_inv, ma.from_list, ma.from_i, stack, player);
runCallbacks(4, RUN_CALLBACKS_MODE_OR_SC);
return lua_type(L, -1) == LUA_TNUMBER ? lua_tonumber(L, -1) : stack.count;
}
// Report moved items
void ScriptApiPlayer::player_inventory_OnMove(
const MoveAction &ma, int count,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_player_inventory_actions");
pushMoveArguments(ma, count, player);
runCallbacks(4, RUN_CALLBACKS_MODE_FIRST);
}
// Report put items
void ScriptApiPlayer::player_inventory_OnPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_player_inventory_actions");
pushPutTakeArguments("put", ma.to_inv, ma.to_list, ma.to_i, stack, player);
runCallbacks(4, RUN_CALLBACKS_MODE_FIRST);
}
// Report taken items
void ScriptApiPlayer::player_inventory_OnTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_player_inventory_actions");
pushPutTakeArguments("take", ma.from_inv, ma.from_list, ma.from_i, stack, player);
runCallbacks(4, RUN_CALLBACKS_MODE_FIRST);
}

View File

@ -0,0 +1,88 @@
/*
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include "irr_v3d.h"
#include "util/string.h"
struct MoveAction;
struct InventoryLocation;
struct ItemStack;
struct ToolCapabilities;
struct PlayerHPChangeReason;
class ServerActiveObject;
class ScriptApiPlayer : virtual public ScriptApiBase
{
public:
virtual ~ScriptApiPlayer() = default;
void on_newplayer(ServerActiveObject *player);
void on_dieplayer(ServerActiveObject *player, const PlayerHPChangeReason &reason);
bool on_respawnplayer(ServerActiveObject *player);
bool on_prejoinplayer(const std::string &name, const std::string &ip,
std::string *reason);
bool can_bypass_userlimit(const std::string &name, const std::string &ip);
void on_joinplayer(ServerActiveObject *player, s64 last_login);
void on_leaveplayer(ServerActiveObject *player, bool timeout);
void on_cheat(ServerActiveObject *player, const std::string &cheat_type);
bool on_punchplayer(ServerActiveObject *player, ServerActiveObject *hitter,
float time_from_last_punch, const ToolCapabilities *toolcap,
v3f dir, s16 damage);
s32 on_player_hpchange(ServerActiveObject *player, s32 hp_change,
const PlayerHPChangeReason &reason);
void on_playerReceiveFields(ServerActiveObject *player,
const std::string &formname, const StringMap &fields);
void on_authplayer(const std::string &name, const std::string &ip, bool is_success);
// Player inventory callbacks
// Return number of accepted items to be moved
int player_inventory_AllowMove(
const MoveAction &ma, int count,
ServerActiveObject *player);
// Return number of accepted items to be put
int player_inventory_AllowPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Return number of accepted items to be taken
int player_inventory_AllowTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Report moved items
void player_inventory_OnMove(
const MoveAction &ma, int count,
ServerActiveObject *player);
// Report put items
void player_inventory_OnPut(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
// Report taken items
void player_inventory_OnTake(
const MoveAction &ma, const ItemStack &stack,
ServerActiveObject *player);
private:
void pushPutTakeArguments(
const char *method, const InventoryLocation &loc,
const std::string &listname, int index, const ItemStack &stack,
ServerActiveObject *player);
void pushMoveArguments(const MoveAction &ma,
int count, ServerActiveObject *player);
};

View File

@ -0,0 +1,810 @@
/*
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_security.h"
#include "filesys.h"
#include "porting.h"
#include "server.h"
#include "client/client.h"
#include "settings.h"
#include <cerrno>
#include <string>
#include <iostream>
#define SECURE_API(lib, name) \
lua_pushcfunction(L, sl_##lib##_##name); \
lua_setfield(L, -2, #name);
static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1)
{
if (from < 0) from = lua_gettop(L) + from + 1;
if (to < 0) to = lua_gettop(L) + to + 1;
for (unsigned i = 0; i < (len / sizeof(list[0])); i++) {
lua_getfield(L, from, list[i]);
lua_setfield(L, to, list[i]);
}
}
// Pushes the original version of a library function on the stack, from the old version
static inline void push_original(lua_State *L, const char *lib, const char *func)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
lua_getfield(L, -1, lib);
lua_remove(L, -2); // Remove globals_backup
lua_getfield(L, -1, func);
lua_remove(L, -2); // Remove lib
}
void ScriptApiSecurity::initializeSecurity()
{
static const char *whitelist[] = {
"assert",
"core",
"collectgarbage",
"DIR_DELIM",
"error",
"getfenv",
"getmetatable",
"ipairs",
"next",
"pairs",
"pcall",
"print",
"rawequal",
"rawget",
"rawset",
"select",
"setfenv",
"setmetatable",
"tonumber",
"tostring",
"type",
"unpack",
"_VERSION",
"xpcall",
// Completely safe libraries
"coroutine",
"string",
"table",
"math",
};
static const char *io_whitelist[] = {
"open",
"close",
"flush",
"read",
"type",
"write",
};
static const char *os_whitelist[] = {
"clock",
"date",
"difftime",
"getenv",
"setlocale",
"time",
"tmpname",
};
static const char *debug_whitelist[] = {
"gethook",
"traceback",
"getinfo",
"getmetatable",
"setupvalue",
"setmetatable",
"upvalueid",
"sethook",
"debug",
"setlocal",
};
static const char *package_whitelist[] = {
"config",
"cpath",
"path",
"searchpath",
};
#if USE_LUAJIT
static const char *jit_whitelist[] = {
"arch",
"flush",
"off",
"on",
"opt",
"os",
"status",
"version",
"version_num",
};
#endif
m_secure = true;
lua_State *L = getStack();
// Backup globals to the registry
lua_getglobal(L, "_G");
lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
// Replace the global environment with an empty one
int thread = getThread(L);
createEmptyEnv(L);
setLuaEnv(L, thread);
// Get old globals
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
int old_globals = lua_gettop(L);
// Copy safe base functions
lua_getglobal(L, "_G");
copy_safe(L, whitelist, sizeof(whitelist));
// And replace unsafe ones
SECURE_API(g, dofile);
SECURE_API(g, load);
SECURE_API(g, loadfile);
SECURE_API(g, loadstring);
SECURE_API(g, require);
lua_pop(L, 1);
// Copy safe IO functions
lua_getfield(L, old_globals, "io");
lua_newtable(L);
copy_safe(L, io_whitelist, sizeof(io_whitelist));
// And replace unsafe ones
//SECURE_API(io, open);
SECURE_API(io, input);
SECURE_API(io, output);
SECURE_API(io, lines);
lua_setglobal(L, "io");
lua_pop(L, 1); // Pop old IO
// Copy safe OS functions
lua_getfield(L, old_globals, "os");
lua_newtable(L);
copy_safe(L, os_whitelist, sizeof(os_whitelist));
// And replace unsafe ones
SECURE_API(os, remove);
SECURE_API(os, rename);
lua_setglobal(L, "os");
lua_pop(L, 1); // Pop old OS
// Copy safe debug functions
lua_getfield(L, old_globals, "debug");
lua_newtable(L);
copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
lua_setglobal(L, "debug");
lua_pop(L, 1); // Pop old debug
// Copy safe package fields
lua_getfield(L, old_globals, "package");
lua_newtable(L);
copy_safe(L, package_whitelist, sizeof(package_whitelist));
lua_setglobal(L, "package");
lua_pop(L, 1); // Pop old package
#if USE_LUAJIT
// Copy safe jit functions, if they exist
lua_getfield(L, -1, "jit");
if (!lua_isnil(L, -1)) {
lua_newtable(L);
copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
lua_setglobal(L, "jit");
}
lua_pop(L, 1); // Pop old jit
#endif
lua_pop(L, 1); // Pop globals_backup
}
void ScriptApiSecurity::initializeSecurityClient()
{
static const char *whitelist[] = {
"assert",
"core",
"collectgarbage",
"DIR_DELIM",
"error",
"getfenv",
"ipairs",
"next",
"pairs",
"pcall",
"print",
"rawequal",
"rawget",
"rawset",
"select",
"setfenv",
// getmetatable can be used to escape the sandbox
"setmetatable",
"tonumber",
"tostring",
"type",
"unpack",
"_VERSION",
"xpcall",
// Completely safe libraries
"coroutine",
"string",
"table",
"math",
};
static const char *os_whitelist[] = {
"clock",
"date",
"difftime",
"time"
};
static const char *debug_whitelist[] = {
"getinfo",
"traceback"
};
#if USE_LUAJIT
static const char *jit_whitelist[] = {
"arch",
"flush",
"off",
"on",
"opt",
"os",
"status",
"version",
"version_num",
};
#endif
m_secure = true;
lua_State *L = getStack();
int thread = getThread(L);
// Backup globals to the registry
lua_getglobal(L, "_G");
lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
// create an empty environment
createEmptyEnv(L);
// Copy safe base functions
lua_getglobal(L, "_G");
lua_getfield(L, -2, "_G");
copy_safe(L, whitelist, sizeof(whitelist));
// And replace unsafe ones
SECURE_API(g, dofile);
SECURE_API(g, load);
SECURE_API(g, loadfile);
SECURE_API(g, loadstring);
SECURE_API(g, require);
lua_pop(L, 2);
// Copy safe OS functions
lua_getglobal(L, "os");
lua_newtable(L);
copy_safe(L, os_whitelist, sizeof(os_whitelist));
lua_setfield(L, -3, "os");
lua_pop(L, 1); // Pop old OS
// Copy safe debug functions
lua_getglobal(L, "debug");
lua_newtable(L);
copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
lua_setfield(L, -3, "debug");
lua_pop(L, 1); // Pop old debug
#if USE_LUAJIT
// Copy safe jit functions, if they exist
lua_getglobal(L, "jit");
lua_newtable(L);
copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
lua_setfield(L, -3, "jit");
lua_pop(L, 1); // Pop old jit
#endif
// Set the environment to the one we created earlier
setLuaEnv(L, thread);
}
int ScriptApiSecurity::getThread(lua_State *L)
{
#if LUA_VERSION_NUM <= 501
int is_main = lua_pushthread(L); // Push the main thread
FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
"isn't the main Lua thread!");
return lua_gettop(L);
#endif
return 0;
}
void ScriptApiSecurity::createEmptyEnv(lua_State *L)
{
lua_newtable(L); // Create new environment
lua_pushvalue(L, -1);
lua_setfield(L, -2, "_G"); // Create the _G loop
}
void ScriptApiSecurity::setLuaEnv(lua_State *L, int thread)
{
#if LUA_VERSION_NUM >= 502 // Lua >= 5.2
// Set the global environment
lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
#else // Lua <= 5.1
// Set the environment of the main thread
FATAL_ERROR_IF(!lua_setfenv(L, thread), "Security: Unable to set "
"environment of the main Lua thread!");
lua_pop(L, 1); // Pop thread
#endif
}
bool ScriptApiSecurity::isSecure(lua_State *L)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_GLOBALS_BACKUP);
bool secure = !lua_isnil(L, -1);
lua_pop(L, 1);
return secure;
}
bool ScriptApiSecurity::safeLoadString(lua_State *L, const std::string &code, const char *chunk_name)
{
if (code.size() > 0 && code[0] == LUA_SIGNATURE[0]) {
lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
return false;
}
if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name))
return false;
return true;
}
bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path, const char *display_name)
{
FILE *fp;
char *chunk_name;
if (!display_name)
display_name = path;
if (!path) {
fp = stdin;
chunk_name = const_cast<char *>("=stdin");
} else {
fp = fopen(path, "rb");
if (!fp) {
lua_pushfstring(L, "%s: %s", path, strerror(errno));
return false;
}
chunk_name = new char[strlen(display_name) + 2];
chunk_name[0] = '@';
chunk_name[1] = '\0';
strcat(chunk_name, display_name);
}
size_t start = 0;
int c = std::getc(fp);
if (c == '#') {
// Skip the first line
while ((c = std::getc(fp)) != EOF && c != '\n') {}
if (c == '\n')
std::getc(fp);
start = std::ftell(fp);
}
// Read the file
int ret = std::fseek(fp, 0, SEEK_END);
if (ret) {
lua_pushfstring(L, "%s: %s", path, strerror(errno));
if (path) {
std::fclose(fp);
delete [] chunk_name;
}
return false;
}
size_t size = std::ftell(fp) - start;
std::string code(size, '\0');
ret = std::fseek(fp, start, SEEK_SET);
if (ret) {
lua_pushfstring(L, "%s: %s", path, strerror(errno));
if (path) {
std::fclose(fp);
delete [] chunk_name;
}
return false;
}
size_t num_read = std::fread(&code[0], 1, size, fp);
if (path)
std::fclose(fp);
if (num_read != size) {
lua_pushliteral(L, "Error reading file to load.");
if (path)
delete [] chunk_name;
return false;
}
bool result = safeLoadString(L, code, chunk_name);
if (path)
delete [] chunk_name;
return result;
}
bool ScriptApiSecurity::checkPath(lua_State *L, const char *path,
bool write_required, bool *write_allowed)
{
if (write_allowed)
*write_allowed = false;
std::string str; // Transient
std::string abs_path = fs::AbsolutePath(path);
if (!abs_path.empty()) {
// Don't allow accessing the settings file
str = fs::AbsolutePath(g_settings_path);
if (str == abs_path) return false;
}
// If we couldn't find the absolute path (path doesn't exist) then
// try removing the last components until it works (to allow
// non-existent files/folders for mkdir).
std::string cur_path = path;
std::string removed;
while (abs_path.empty() && !cur_path.empty()) {
std::string component;
cur_path = fs::RemoveLastPathComponent(cur_path, &component);
if (component == "..") {
// Parent components can't be allowed or we could allow something like
// /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd.
// If we have previous non-relative elements in the path we might be
// able to remove them so that things like worlds/foo/noexist/../auth.txt
// could be allowed, but those paths will be interpreted as nonexistent
// by the operating system anyways.
return false;
}
removed.append(component).append(removed.empty() ? "" : DIR_DELIM + removed);
abs_path = fs::AbsolutePath(cur_path);
}
if (abs_path.empty())
return false;
// Add the removed parts back so that you can't, eg, create a
// directory in worldmods if worldmods doesn't exist.
if (!removed.empty())
abs_path += DIR_DELIM + removed;
// Get server from registry
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
ScriptApiBase *script;
#if INDIRECT_SCRIPTAPI_RIDX
script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1));
#else
script = (ScriptApiBase *) lua_touserdata(L, -1);
#endif
lua_pop(L, 1);
const IGameDef *gamedef = script->getGameDef();
if (!gamedef)
return false;
// Get mod name
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
if (lua_isstring(L, -1)) {
std::string mod_name = readParam<std::string>(L, -1);
// Builtin can access anything
if (mod_name == BUILTIN_MOD_NAME) {
if (write_allowed) *write_allowed = true;
return true;
}
// Allow paths in mod path
// Don't bother if write access isn't important, since it will be handled later
if (write_required || write_allowed != NULL) {
const ModSpec *mod = gamedef->getModSpec(mod_name);
if (mod) {
str = fs::AbsolutePath(mod->path);
if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
if (write_allowed) *write_allowed = true;
return true;
}
}
}
}
lua_pop(L, 1); // Pop mod name
// Allow read-only access to all mod directories
if (!write_required) {
const std::vector<ModSpec> &mods = gamedef->getMods();
for (const ModSpec &mod : mods) {
str = fs::AbsolutePath(mod.path);
if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
return true;
}
}
}
str = fs::AbsolutePath(gamedef->getWorldPath());
if (!str.empty()) {
// Don't allow access to other paths in the world mod/game path.
// These have to be blocked so you can't override a trusted mod
// by creating a mod with the same name in a world mod directory.
// We add to the absolute path of the world instead of getting
// the absolute paths directly because that won't work if they
// don't exist.
if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
return false;
}
// Allow all other paths in world path
if (fs::PathStartsWith(abs_path, str)) {
if (write_allowed) *write_allowed = true;
return true;
}
}
// Default to disallowing
return false;
}
int ScriptApiSecurity::sl_g_dofile(lua_State *L)
{
int nret = sl_g_loadfile(L);
if (nret != 1) {
lua_error(L);
// code after this function isn't executed
}
int top_precall = lua_gettop(L);
lua_call(L, 0, LUA_MULTRET);
// Return number of arguments returned by the function,
// adjusting for the function being poped.
return lua_gettop(L) - (top_precall - 1);
}
int ScriptApiSecurity::sl_g_load(lua_State *L)
{
size_t len;
const char *buf;
std::string code;
const char *chunk_name = "=(load)";
luaL_checktype(L, 1, LUA_TFUNCTION);
if (!lua_isnone(L, 2)) {
luaL_checktype(L, 2, LUA_TSTRING);
chunk_name = lua_tostring(L, 2);
}
while (true) {
lua_pushvalue(L, 1);
lua_call(L, 0, 1);
int t = lua_type(L, -1);
if (t == LUA_TNIL) {
break;
}
if (t != LUA_TSTRING) {
lua_pushnil(L);
lua_pushliteral(L, "Loader didn't return a string");
return 2;
}
buf = lua_tolstring(L, -1, &len);
code += std::string(buf, len);
lua_pop(L, 1); // Pop return value
}
if (!safeLoadString(L, code, chunk_name)) {
lua_pushnil(L);
lua_insert(L, -2);
return 2;
}
return 1;
}
int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
{
#ifndef SERVER
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI);
#if INDIRECT_SCRIPTAPI_RIDX
ScriptApiBase *script = (ScriptApiBase *) *(void**)(lua_touserdata(L, -1));
#else
ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
#endif
lua_pop(L, 1);
// Client implementation
if (script->getType() == ScriptingType::Client) {
std::string path = readParam<std::string>(L, 1);
const std::string *contents = script->getClient()->getModFile(path);
if (!contents) {
std::string error_msg = "Coudln't find script called: " + path;
lua_pushnil(L);
lua_pushstring(L, error_msg.c_str());
return 2;
}
std::string chunk_name = "@" + path;
if (!safeLoadString(L, *contents, chunk_name.c_str())) {
lua_pushnil(L);
lua_insert(L, -2);
return 2;
}
return 1;
}
#endif
// Server implementation
const char *path = NULL;
if (lua_isstring(L, 1)) {
path = lua_tostring(L, 1);
CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
}
if (!safeLoadFile(L, path)) {
lua_pushnil(L);
lua_insert(L, -2);
return 2;
}
return 1;
}
int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
{
const char *chunk_name = "=(load)";
luaL_checktype(L, 1, LUA_TSTRING);
if (!lua_isnone(L, 2)) {
luaL_checktype(L, 2, LUA_TSTRING);
chunk_name = lua_tostring(L, 2);
}
size_t size;
const char *code = lua_tolstring(L, 1, &size);
std::string code_s(code, size);
if (!safeLoadString(L, code_s, chunk_name)) {
lua_pushnil(L);
lua_insert(L, -2);
return 2;
}
return 1;
}
int ScriptApiSecurity::sl_g_require(lua_State *L)
{
lua_pushliteral(L, "require() is disabled when mod security is on.");
return lua_error(L);
}
int ScriptApiSecurity::sl_io_open(lua_State *L)
{
bool with_mode = lua_gettop(L) > 1;
luaL_checktype(L, 1, LUA_TSTRING);
const char *path = lua_tostring(L, 1);
bool write_requested = false;
if (with_mode) {
luaL_checktype(L, 2, LUA_TSTRING);
const char *mode = lua_tostring(L, 2);
write_requested = strchr(mode, 'w') != NULL ||
strchr(mode, '+') != NULL ||
strchr(mode, 'a') != NULL;
}
CHECK_SECURE_PATH_INTERNAL(L, path, write_requested, NULL);
push_original(L, "io", "open");
lua_pushvalue(L, 1);
if (with_mode) {
lua_pushvalue(L, 2);
}
lua_call(L, with_mode ? 2 : 1, 2);
return 2;
}
int ScriptApiSecurity::sl_io_input(lua_State *L)
{
if (lua_isstring(L, 1)) {
const char *path = lua_tostring(L, 1);
CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
}
push_original(L, "io", "input");
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
return 1;
}
int ScriptApiSecurity::sl_io_output(lua_State *L)
{
if (lua_isstring(L, 1)) {
const char *path = lua_tostring(L, 1);
CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
}
push_original(L, "io", "output");
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
return 1;
}
int ScriptApiSecurity::sl_io_lines(lua_State *L)
{
if (lua_isstring(L, 1)) {
const char *path = lua_tostring(L, 1);
CHECK_SECURE_PATH_INTERNAL(L, path, false, NULL);
}
int top_precall = lua_gettop(L);
push_original(L, "io", "lines");
lua_pushvalue(L, 1);
lua_call(L, 1, LUA_MULTRET);
// Return number of arguments returned by the function,
// adjusting for the function being poped.
return lua_gettop(L) - top_precall;
}
int ScriptApiSecurity::sl_os_rename(lua_State *L)
{
luaL_checktype(L, 1, LUA_TSTRING);
const char *path1 = lua_tostring(L, 1);
CHECK_SECURE_PATH_INTERNAL(L, path1, true, NULL);
luaL_checktype(L, 2, LUA_TSTRING);
const char *path2 = lua_tostring(L, 2);
CHECK_SECURE_PATH_INTERNAL(L, path2, true, NULL);
push_original(L, "os", "rename");
lua_pushvalue(L, 1);
lua_pushvalue(L, 2);
lua_call(L, 2, 2);
return 2;
}
int ScriptApiSecurity::sl_os_remove(lua_State *L)
{
luaL_checktype(L, 1, LUA_TSTRING);
const char *path = lua_tostring(L, 1);
CHECK_SECURE_PATH_INTERNAL(L, path, true, NULL);
push_original(L, "os", "remove");
lua_pushvalue(L, 1);
lua_call(L, 1, 2);
return 2;
}

View File

@ -0,0 +1,78 @@
/*
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.
*/
#pragma once
#include "cpp_api/s_base.h"
#define CHECK_SECURE_PATH_INTERNAL(L, path, write_required, ptr) \
if (!ScriptApiSecurity::checkPath(L, path, write_required, ptr)) { \
throw LuaError(std::string("Mod security: Blocked attempted ") + \
(write_required ? "write to " : "read from ") + path); \
}
#define CHECK_SECURE_PATH(L, path, write_required) \
if (ScriptApiSecurity::isSecure(L)) { \
CHECK_SECURE_PATH_INTERNAL(L, path, write_required, NULL); \
}
#define CHECK_SECURE_PATH_POSSIBLE_WRITE(L, path, ptr) \
if (ScriptApiSecurity::isSecure(L)) { \
CHECK_SECURE_PATH_INTERNAL(L, path, false, ptr); \
}
class ScriptApiSecurity : virtual public ScriptApiBase
{
public:
int getThread(lua_State *L);
// creates an empty Lua environment
void createEmptyEnv(lua_State *L);
// sets the enviroment to the table thats on top of the stack
void setLuaEnv(lua_State *L, int thread);
// Sets up security on the ScriptApi's Lua state
void initializeSecurity();
void initializeSecurityClient();
// Checks if the Lua state has been secured
static bool isSecure(lua_State *L);
// Loads a string as Lua code safely (doesn't allow bytecode).
static bool safeLoadString(lua_State *L, const std::string &code, const char *chunk_name);
// Loads a file as Lua code safely (doesn't allow bytecode).
static bool safeLoadFile(lua_State *L, const char *path, const char *display_name = NULL);
// Checks if mods are allowed to read (and optionally write) to the path
static bool checkPath(lua_State *L, const char *path, bool write_required,
bool *write_allowed=NULL);
private:
// Syntax: "sl_" <Library name or 'g' (global)> '_' <Function name>
// (sl stands for Secure Lua)
static int sl_g_dofile(lua_State *L);
static int sl_g_load(lua_State *L);
static int sl_g_loadfile(lua_State *L);
static int sl_g_loadstring(lua_State *L);
static int sl_g_require(lua_State *L);
static int sl_io_open(lua_State *L);
static int sl_io_input(lua_State *L);
static int sl_io_output(lua_State *L);
static int sl_io_lines(lua_State *L);
static int sl_os_rename(lua_State *L);
static int sl_os_remove(lua_State *L);
};

View File

@ -0,0 +1,198 @@
/*
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_server.h"
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"
bool ScriptApiServer::getAuth(const std::string &playername,
std::string *dst_password,
std::set<std::string> *dst_privs,
s64 *dst_last_login)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
getAuthHandler();
lua_getfield(L, -1, "get_auth");
if (lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError("Authentication handler missing get_auth");
lua_pushstring(L, playername.c_str());
PCALL_RES(lua_pcall(L, 1, 1, error_handler));
lua_remove(L, -2); // Remove auth handler
lua_remove(L, error_handler);
// nil = login not allowed
if (lua_isnil(L, -1))
return false;
luaL_checktype(L, -1, LUA_TTABLE);
std::string password;
if (!getstringfield(L, -1, "password", password))
throw LuaError("Authentication handler didn't return password");
if (dst_password)
*dst_password = password;
lua_getfield(L, -1, "privileges");
if (!lua_istable(L, -1))
throw LuaError("Authentication handler didn't return privilege table");
if (dst_privs)
readPrivileges(-1, *dst_privs);
lua_pop(L, 1); // Remove key from privs table
s64 last_login;
if(!getintfield(L, -1, "last_login", last_login))
throw LuaError("Authentication handler didn't return last_login");
if (dst_last_login)
*dst_last_login = (s64)last_login;
return true;
}
void ScriptApiServer::getAuthHandler()
{
lua_State *L = getStack();
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_auth_handler");
if (lua_isnil(L, -1)){
lua_pop(L, 1);
lua_getfield(L, -1, "builtin_auth_handler");
}
setOriginFromTable(-1);
lua_remove(L, -2); // Remove core
if (lua_type(L, -1) != LUA_TTABLE)
throw LuaError("Authentication handler table not valid");
}
void ScriptApiServer::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 = readParam<bool>(L, -1);
if (value)
result.insert(key);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
}
void ScriptApiServer::createAuth(const std::string &playername,
const std::string &password)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
getAuthHandler();
lua_getfield(L, -1, "create_auth");
lua_remove(L, -2); // Remove auth handler
if (lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError("Authentication handler missing create_auth");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
PCALL_RES(lua_pcall(L, 2, 0, error_handler));
lua_pop(L, 1); // Pop error handler
}
bool ScriptApiServer::setPassword(const std::string &playername,
const std::string &password)
{
SCRIPTAPI_PRECHECKHEADER
int error_handler = PUSH_ERROR_HANDLER(L);
getAuthHandler();
lua_getfield(L, -1, "set_password");
lua_remove(L, -2); // Remove auth handler
if (lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError("Authentication handler missing set_password");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
PCALL_RES(lua_pcall(L, 2, 1, error_handler));
lua_remove(L, error_handler);
return lua_toboolean(L, -1);
}
bool ScriptApiServer::on_chat_message(const std::string &name,
const std::string &message)
{
SCRIPTAPI_PRECHECKHEADER
// Get core.registered_on_chat_messages
lua_getglobal(L, "core");
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);
return readParam<bool>(L, -1);
}
void ScriptApiServer::on_mods_loaded()
{
SCRIPTAPI_PRECHECKHEADER
// Get registered shutdown hooks
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_mods_loaded");
// Call callbacks
runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiServer::on_shutdown()
{
SCRIPTAPI_PRECHECKHEADER
// Get registered shutdown hooks
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_shutdown");
// Call callbacks
runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
std::string ScriptApiServer::formatChatMessage(const std::string &name,
const std::string &message)
{
SCRIPTAPI_PRECHECKHEADER
// Push function onto stack
lua_getglobal(L, "core");
lua_getfield(L, -1, "format_chat_message");
// Push arguments onto stack
lua_pushstring(L, name.c_str());
lua_pushstring(L, message.c_str());
// Actually call the function
lua_call(L, 2, 1);
// Fetch return value
std::string ret = lua_tostring(L, -1);
lua_pop(L, 1);
return ret;
}

View File

@ -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.
*/
#pragma once
#include "cpp_api/s_base.h"
#include <set>
class ScriptApiServer
: virtual public ScriptApiBase
{
public:
// Calls on_chat_message handlers
// Returns true if script handled message
bool on_chat_message(const std::string &name, const std::string &message);
// Calls when mods are loaded
void on_mods_loaded();
// Calls on_shutdown handlers
void on_shutdown();
// Calls core.format_chat_message
std::string formatChatMessage(const std::string &name,
const std::string &message);
/* auth */
bool getAuth(const std::string &playername,
std::string *dst_password,
std::set<std::string> *dst_privs,
s64 *dst_last_login = nullptr);
void createAuth(const std::string &playername,
const std::string &password);
bool setPassword(const std::string &playername,
const std::string &password);
private:
void getAuthHandler();
void readPrivileges(int index, std::set<std::string> &result);
};