Add files via upload
This commit is contained in:
parent
f37f0cf989
commit
2e17095765
11
src/script/common/CMakeLists.txt
Normal file
11
src/script/common/CMakeLists.txt
Normal 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)
|
||||
|
2150
src/script/common/c_content.cpp
Normal file
2150
src/script/common/c_content.cpp
Normal file
File diff suppressed because it is too large
Load Diff
208
src/script/common/c_content.h
Normal file
208
src/script/common/c_content.h
Normal 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 ¶ms);
|
||||
|
||||
void push_dig_params (lua_State *L,
|
||||
const DigParams ¶ms);
|
||||
void push_hit_params (lua_State *L,
|
||||
const HitParams ¶ms);
|
||||
|
||||
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);
|
681
src/script/common/c_converter.cpp
Normal file
681
src/script/common/c_converter.cpp
Normal 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;
|
||||
}
|
138
src/script/common/c_converter.h
Normal file
138
src/script/common/c_converter.h
Normal 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);
|
182
src/script/common/c_internal.cpp
Normal file
182
src/script/common/c_internal.cpp
Normal 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);
|
||||
}
|
138
src/script/common/c_internal.h
Normal file
138
src/script/common/c_internal.h
Normal 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);
|
34
src/script/common/c_types.cpp
Normal file
34
src/script/common/c_types.cpp
Normal 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},
|
||||
};
|
61
src/script/common/c_types.h
Normal file
61
src/script/common/c_types.h
Normal 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[];
|
132
src/script/common/helper.cpp
Normal file
132
src/script/common/helper.cpp
Normal 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;
|
||||
}
|
57
src/script/common/helper.h
Normal file
57
src/script/common/helper.h
Normal 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);
|
||||
}
|
||||
};
|
21
src/script/cpp_api/CMakeLists.txt
Normal file
21
src/script/cpp_api/CMakeLists.txt
Normal 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)
|
||||
|
289
src/script/cpp_api/s_async.cpp
Normal file
289
src/script/cpp_api/s_async.cpp
Normal 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 ¶ms)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
156
src/script/cpp_api/s_async.h
Normal file
156
src/script/cpp_api/s_async.h
Normal 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 ¶ms);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
454
src/script/cpp_api/s_base.cpp
Normal file
454
src/script/cpp_api/s_base.cpp
Normal 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
175
src/script/cpp_api/s_base.h
Normal 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;
|
||||
};
|
124
src/script/cpp_api/s_cheats.cpp
Normal file
124
src/script/cpp_api/s_cheats.cpp
Normal 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);
|
||||
}
|
58
src/script/cpp_api/s_cheats.h
Normal file
58
src/script/cpp_api/s_cheats.h
Normal 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;
|
||||
};
|
437
src/script/cpp_api/s_client.cpp
Normal file
437
src/script/cpp_api/s_client.cpp
Normal 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);
|
||||
}
|
79
src/script/cpp_api/s_client.h
Normal file
79
src/script/cpp_api/s_client.h
Normal 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);
|
||||
};
|
305
src/script/cpp_api/s_entity.cpp
Normal file
305
src/script/cpp_api/s_entity.cpp
Normal 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");
|
||||
}
|
53
src/script/cpp_api/s_entity.h
Normal file
53
src/script/cpp_api/s_entity.h
Normal 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);
|
||||
};
|
263
src/script/cpp_api/s_env.cpp
Normal file
263
src/script/cpp_api/s_env.cpp
Normal 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);
|
||||
}
|
||||
}
|
45
src/script/cpp_api/s_env.h
Normal file
45
src/script/cpp_api/s_env.h
Normal 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);
|
||||
};
|
85
src/script/cpp_api/s_internal.h
Normal file
85
src/script/cpp_api/s_internal.h
Normal 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);
|
225
src/script/cpp_api/s_inventory.cpp
Normal file
225
src/script/cpp_api/s_inventory.cpp
Normal 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;
|
||||
}
|
59
src/script/cpp_api/s_inventory.h
Normal file
59
src/script/cpp_api/s_inventory.h
Normal 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);
|
||||
};
|
263
src/script/cpp_api/s_item.cpp
Normal file
263
src/script/cpp_api/s_item.cpp
Normal 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);
|
||||
}
|
||||
|
62
src/script/cpp_api/s_item.h
Normal file
62
src/script/cpp_api/s_item.h
Normal 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);
|
||||
|
||||
};
|
93
src/script/cpp_api/s_mainmenu.cpp
Normal file
93
src/script/cpp_api/s_mainmenu.cpp
Normal 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
|
||||
}
|
45
src/script/cpp_api/s_mainmenu.h
Normal file
45
src/script/cpp_api/s_mainmenu.h
Normal 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);
|
||||
};
|
50
src/script/cpp_api/s_modchannels.cpp
Normal file
50
src/script/cpp_api/s_modchannels.cpp
Normal 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);
|
||||
}
|
31
src/script/cpp_api/s_modchannels.h
Normal file
31
src/script/cpp_api/s_modchannels.h
Normal 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);
|
||||
};
|
272
src/script/cpp_api/s_node.cpp
Normal file
272
src/script/cpp_api/s_node.cpp
Normal 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
|
||||
}
|
57
src/script/cpp_api/s_node.h
Normal file
57
src/script/cpp_api/s_node.h
Normal 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[];
|
||||
};
|
232
src/script/cpp_api/s_nodemeta.cpp
Normal file
232
src/script/cpp_api/s_nodemeta.cpp
Normal 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
|
||||
}
|
63
src/script/cpp_api/s_nodemeta.h
Normal file
63
src/script/cpp_api/s_nodemeta.h
Normal 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:
|
||||
|
||||
};
|
369
src/script/cpp_api/s_player.cpp
Normal file
369
src/script/cpp_api/s_player.cpp
Normal 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);
|
||||
}
|
88
src/script/cpp_api/s_player.h
Normal file
88
src/script/cpp_api/s_player.h
Normal 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);
|
||||
};
|
810
src/script/cpp_api/s_security.cpp
Normal file
810
src/script/cpp_api/s_security.cpp
Normal 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;
|
||||
}
|
78
src/script/cpp_api/s_security.h
Normal file
78
src/script/cpp_api/s_security.h
Normal 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);
|
||||
};
|
198
src/script/cpp_api/s_server.cpp
Normal file
198
src/script/cpp_api/s_server.cpp
Normal 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;
|
||||
}
|
55
src/script/cpp_api/s_server.h
Normal file
55
src/script/cpp_api/s_server.h
Normal 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);
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user