]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_env.cpp
e49113405e4ae0b6bd60b2dc8334e7ed17d1a81c
[minetest.git] / src / script / cpp_api / s_env.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "cpp_api/s_env.h"
21 #include "cpp_api/s_internal.h"
22 #include "common/c_converter.h"
23 #include "log.h"
24 #include "environment.h"
25 #include "mapgen/mapgen.h"
26 #include "lua_api/l_env.h"
27 #include "server.h"
28 #include "script/common/c_content.h"
29
30
31 void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
32         u32 blockseed)
33 {
34         SCRIPTAPI_PRECHECKHEADER
35
36         // Get core.registered_on_generateds
37         lua_getglobal(L, "core");
38         lua_getfield(L, -1, "registered_on_generateds");
39         // Call callbacks
40         push_v3s16(L, minp);
41         push_v3s16(L, maxp);
42         lua_pushnumber(L, blockseed);
43         runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
44 }
45
46 void ScriptApiEnv::environment_Step(float dtime)
47 {
48         SCRIPTAPI_PRECHECKHEADER
49         //infostream << "scriptapi_environment_step" << std::endl;
50
51         // Get core.registered_globalsteps
52         lua_getglobal(L, "core");
53         lua_getfield(L, -1, "registered_globalsteps");
54         // Call callbacks
55         lua_pushnumber(L, dtime);
56         runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
57 }
58
59 void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &type)
60 {
61         SCRIPTAPI_PRECHECKHEADER
62
63         if (player == NULL)
64                 return;
65
66         // Get minetest.registered_playerevents
67         lua_getglobal(L, "minetest");
68         lua_getfield(L, -1, "registered_playerevents");
69
70         // Call callbacks
71         objectrefGetOrCreate(L, player);   // player
72         lua_pushstring(L,type.c_str()); // event type
73         runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
74 }
75
76 void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env)
77 {
78         SCRIPTAPI_PRECHECKHEADER
79         verbosestream << "ScriptApiEnv: Environment initialized" << std::endl;
80         setEnv(env);
81
82         /*
83                 Add {Loading,Active}BlockModifiers to environment
84         */
85
86         // Get core.registered_abms
87         lua_getglobal(L, "core");
88         lua_getfield(L, -1, "registered_abms");
89         int registered_abms = lua_gettop(L);
90
91         if (!lua_istable(L, registered_abms)) {
92                 lua_pop(L, 1);
93                 throw LuaError("core.registered_abms was not a lua table, as expected.");
94         }
95         lua_pushnil(L);
96         while (lua_next(L, registered_abms)) {
97                 // key at index -2 and value at index -1
98                 int id = lua_tonumber(L, -2);
99                 int current_abm = lua_gettop(L);
100
101                 std::vector<std::string> trigger_contents;
102                 lua_getfield(L, current_abm, "nodenames");
103                 if (lua_istable(L, -1)) {
104                         int table = lua_gettop(L);
105                         lua_pushnil(L);
106                         while (lua_next(L, table)) {
107                                 // key at index -2 and value at index -1
108                                 luaL_checktype(L, -1, LUA_TSTRING);
109                                 trigger_contents.emplace_back(readParam<std::string>(L, -1));
110                                 // removes value, keeps key for next iteration
111                                 lua_pop(L, 1);
112                         }
113                 } else if (lua_isstring(L, -1)) {
114                         trigger_contents.emplace_back(readParam<std::string>(L, -1));
115                 }
116                 lua_pop(L, 1);
117
118                 std::vector<std::string> required_neighbors;
119                 lua_getfield(L, current_abm, "neighbors");
120                 if (lua_istable(L, -1)) {
121                         int table = lua_gettop(L);
122                         lua_pushnil(L);
123                         while (lua_next(L, table)) {
124                                 // key at index -2 and value at index -1
125                                 luaL_checktype(L, -1, LUA_TSTRING);
126                                 required_neighbors.emplace_back(readParam<std::string>(L, -1));
127                                 // removes value, keeps key for next iteration
128                                 lua_pop(L, 1);
129                         }
130                 } else if (lua_isstring(L, -1)) {
131                         required_neighbors.emplace_back(readParam<std::string>(L, -1));
132                 }
133                 lua_pop(L, 1);
134
135                 float trigger_interval = 10.0;
136                 getfloatfield(L, current_abm, "interval", trigger_interval);
137
138                 int trigger_chance = 50;
139                 getintfield(L, current_abm, "chance", trigger_chance);
140
141                 bool simple_catch_up = true;
142                 getboolfield(L, current_abm, "catch_up", simple_catch_up);
143
144                 s16 min_y = INT16_MIN;
145                 getintfield(L, current_abm, "min_y", min_y);
146
147                 s16 max_y = INT16_MAX;
148                 getintfield(L, current_abm, "max_y", max_y);
149
150                 lua_getfield(L, current_abm, "action");
151                 luaL_checktype(L, current_abm + 1, LUA_TFUNCTION);
152                 lua_pop(L, 1);
153
154                 LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors,
155                         trigger_interval, trigger_chance, simple_catch_up, min_y, max_y);
156
157                 env->addActiveBlockModifier(abm);
158
159                 // removes value, keeps key for next iteration
160                 lua_pop(L, 1);
161         }
162         lua_pop(L, 1);
163
164         // Get core.registered_lbms
165         lua_getglobal(L, "core");
166         lua_getfield(L, -1, "registered_lbms");
167         int registered_lbms = lua_gettop(L);
168
169         if (!lua_istable(L, registered_lbms)) {
170                 lua_pop(L, 1);
171                 throw LuaError("core.registered_lbms was not a lua table, as expected.");
172         }
173
174         lua_pushnil(L);
175         while (lua_next(L, registered_lbms)) {
176                 // key at index -2 and value at index -1
177                 int id = lua_tonumber(L, -2);
178                 int current_lbm = lua_gettop(L);
179
180                 std::set<std::string> trigger_contents;
181                 lua_getfield(L, current_lbm, "nodenames");
182                 if (lua_istable(L, -1)) {
183                         int table = lua_gettop(L);
184                         lua_pushnil(L);
185                         while (lua_next(L, table)) {
186                                 // key at index -2 and value at index -1
187                                 luaL_checktype(L, -1, LUA_TSTRING);
188                                 trigger_contents.insert(readParam<std::string>(L, -1));
189                                 // removes value, keeps key for next iteration
190                                 lua_pop(L, 1);
191                         }
192                 } else if (lua_isstring(L, -1)) {
193                         trigger_contents.insert(readParam<std::string>(L, -1));
194                 }
195                 lua_pop(L, 1);
196
197                 std::string name;
198                 getstringfield(L, current_lbm, "name", name);
199
200                 bool run_at_every_load = getboolfield_default(L, current_lbm,
201                         "run_at_every_load", false);
202
203                 lua_getfield(L, current_lbm, "action");
204                 luaL_checktype(L, current_lbm + 1, LUA_TFUNCTION);
205                 lua_pop(L, 1);
206
207                 LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name,
208                         run_at_every_load);
209
210                 env->addLoadingBlockModifierDef(lbm);
211
212                 // removes value, keeps key for next iteration
213                 lua_pop(L, 1);
214         }
215         lua_pop(L, 1);
216 }
217
218 void ScriptApiEnv::on_emerge_area_completion(
219         v3s16 blockpos, int action, ScriptCallbackState *state)
220 {
221         Server *server = getServer();
222
223         // This function should be executed with envlock held.
224         // The caller (LuaEmergeAreaCallback in src/script/lua_api/l_env.cpp)
225         // should have obtained the lock.
226         // Note that the order of these locks is important!  Envlock must *ALWAYS*
227         // be acquired before attempting to acquire scriptlock, or else ServerThread
228         // will try to acquire scriptlock after it already owns envlock, thus
229         // deadlocking EmergeThread and ServerThread
230
231         SCRIPTAPI_PRECHECKHEADER
232
233         int error_handler = PUSH_ERROR_HANDLER(L);
234
235         lua_rawgeti(L, LUA_REGISTRYINDEX, state->callback_ref);
236         luaL_checktype(L, -1, LUA_TFUNCTION);
237
238         push_v3s16(L, blockpos);
239         lua_pushinteger(L, action);
240         lua_pushinteger(L, state->refcount);
241         lua_rawgeti(L, LUA_REGISTRYINDEX, state->args_ref);
242
243         setOriginDirect(state->origin.c_str());
244
245         try {
246                 PCALL_RES(lua_pcall(L, 4, 0, error_handler));
247         } catch (LuaError &e) {
248                 // Note: don't throw here, we still need to run the cleanup code below
249                 server->setAsyncFatalError(e);
250         }
251
252         lua_pop(L, 1); // Pop error handler
253
254         if (state->refcount == 0) {
255                 luaL_unref(L, LUA_REGISTRYINDEX, state->callback_ref);
256                 luaL_unref(L, LUA_REGISTRYINDEX, state->args_ref);
257         }
258 }
259
260 void ScriptApiEnv::check_for_falling(v3s16 p)
261 {
262         SCRIPTAPI_PRECHECKHEADER
263
264         int error_handler = PUSH_ERROR_HANDLER(L);
265         lua_getglobal(L, "core");
266         lua_getfield(L, -1, "check_for_falling");
267         luaL_checktype(L, -1, LUA_TFUNCTION);
268         push_v3s16(L, p);
269         PCALL_RES(lua_pcall(L, 1, 0, error_handler));
270 }
271
272 void ScriptApiEnv::on_liquid_transformed(
273         const std::vector<std::pair<v3s16, MapNode>> &list)
274 {
275         SCRIPTAPI_PRECHECKHEADER
276
277         // Get core.registered_on_liquid_transformed
278         lua_getglobal(L, "core");
279         lua_getfield(L, -1, "registered_on_liquid_transformed");
280         luaL_checktype(L, -1, LUA_TTABLE);
281         lua_remove(L, -2);
282
283         // Skip converting list and calling hook if there are
284         // no registered callbacks.
285         if(lua_objlen(L, -1) < 1) return;
286
287         // Convert the list to a pos array and a node array for lua
288         int index = 1;
289         lua_createtable(L, list.size(), 0);
290         lua_createtable(L, list.size(), 0);
291         for(std::pair<v3s16, MapNode> p : list) {
292                 lua_pushnumber(L, index);
293                 push_v3s16(L, p.first);
294                 lua_rawset(L, -4);
295                 lua_pushnumber(L, index++);
296                 pushnode(L, p.second);
297                 lua_rawset(L, -3);
298         }
299
300         runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
301 }