]> git.lizzy.rs Git - dragonfireclient.git/blob - src/scriptapi.cpp
f8875c0e36d53e41eb149477d6fa126499013c3b
[dragonfireclient.git] / src / scriptapi.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "scriptapi.h"
21
22 #include <iostream>
23 extern "C" {
24 #include <lua.h>
25 #include <lualib.h>
26 #include <lauxlib.h>
27 }
28
29 #include "log.h"
30 #include "server.h"
31 #include "porting.h"
32 #include "filesys.h"
33 #include "serverobject.h"
34 #include "script.h"
35 //#include "luna.h"
36 #include "luaentity_common.h"
37 #include "content_sao.h" // For LuaEntitySAO
38
39 /*
40 TODO:
41 - Global environment step function
42 - Random node triggers
43 - Object network and client-side stuff
44 - Named node types and dynamic id allocation
45 - LuaNodeMetadata
46         blockdef.has_metadata = true/false
47         - Stores an inventory and stuff in a Settings object
48         meta.inventory_add_list("main")
49         blockdef.on_inventory_modified
50         meta.set("owner", playername)
51         meta.get("owner")
52 */
53
54 static void stackDump(lua_State *L, std::ostream &o)
55 {
56   int i;
57   int top = lua_gettop(L);
58   for (i = 1; i <= top; i++) {  /* repeat for each level */
59         int t = lua_type(L, i);
60         switch (t) {
61
62           case LUA_TSTRING:  /* strings */
63                 o<<"\""<<lua_tostring(L, i)<<"\"";
64                 break;
65
66           case LUA_TBOOLEAN:  /* booleans */
67                 o<<(lua_toboolean(L, i) ? "true" : "false");
68                 break;
69
70           case LUA_TNUMBER:  /* numbers */ {
71                 char buf[10];
72                 snprintf(buf, 10, "%g", lua_tonumber(L, i));
73                 o<<buf;
74                 break; }
75
76           default:  /* other values */
77                 o<<lua_typename(L, t);
78                 break;
79
80         }
81         o<<" ";
82   }
83   o<<std::endl;
84 }
85
86 static void realitycheck(lua_State *L)
87 {
88         int top = lua_gettop(L);
89         if(top >= 30){
90                 dstream<<"Stack is over 30:"<<std::endl;
91                 stackDump(L, dstream);
92                 script_error(L, "Stack is over 30 (reality check)");
93         }
94 }
95
96 class StackUnroller
97 {
98 private:
99         lua_State *m_lua;
100         int m_original_top;
101 public:
102         StackUnroller(lua_State *L):
103                 m_lua(L),
104                 m_original_top(-1)
105         {
106                 m_original_top = lua_gettop(m_lua); // store stack height
107         }
108         ~StackUnroller()
109         {
110                 lua_settop(m_lua, m_original_top); // restore stack height
111         }
112 };
113
114 v3f readFloatPos(lua_State *L, int index)
115 {
116         v3f pos;
117         lua_pushvalue(L, index); // Push pos
118         luaL_checktype(L, -1, LUA_TTABLE);
119         lua_getfield(L, -1, "x");
120         pos.X = lua_tonumber(L, -1);
121         lua_pop(L, 1);
122         lua_getfield(L, -1, "y");
123         pos.Y = lua_tonumber(L, -1);
124         lua_pop(L, 1);
125         lua_getfield(L, -1, "z");
126         pos.Z = lua_tonumber(L, -1);
127         lua_pop(L, 1);
128         lua_pop(L, 1); // Pop pos
129         pos *= BS; // Scale to internal format
130         return pos;
131 }
132
133 /*
134         Global functions
135 */
136
137 // Register new object prototype
138 // register_entity(name, prototype)
139 static int l_register_entity(lua_State *L)
140 {
141         const char *name = luaL_checkstring(L, 1);
142         luaL_checktype(L, 2, LUA_TTABLE);
143         infostream<<"register_entity: "<<name<<std::endl;
144
145         // Get minetest.registered_entities
146         lua_getglobal(L, "minetest");
147         lua_getfield(L, -1, "registered_entities");
148         luaL_checktype(L, -1, LUA_TTABLE);
149         int registered_entities = lua_gettop(L);
150         lua_pushvalue(L, 2); // Object = param 2 -> stack top
151         // registered_entities[name] = object
152         lua_setfield(L, registered_entities, name);
153         
154         // Get registered object to top of stack
155         lua_pushvalue(L, 2);
156         
157         // Set __index to point to itself
158         lua_pushvalue(L, -1);
159         lua_setfield(L, -2, "__index");
160
161         // Set metatable.__index = metatable
162         luaL_getmetatable(L, "minetest.entity");
163         lua_pushvalue(L, -1); // duplicate metatable
164         lua_setfield(L, -2, "__index");
165         // Set object metatable
166         lua_setmetatable(L, -2);
167
168         return 0; /* number of results */
169 }
170
171 static const struct luaL_Reg minetest_f [] = {
172         {"register_entity", l_register_entity},
173         {NULL, NULL}
174 };
175
176 /*
177         LuaEntity functions
178 */
179
180 static const struct luaL_Reg minetest_entity_m [] = {
181         {NULL, NULL}
182 };
183
184 /*
185         Getters for stuff in main tables
186 */
187
188 static void objectref_get(lua_State *L, u16 id)
189 {
190         // Get minetest.object_refs[i]
191         lua_getglobal(L, "minetest");
192         lua_getfield(L, -1, "object_refs");
193         luaL_checktype(L, -1, LUA_TTABLE);
194         lua_pushnumber(L, id);
195         lua_gettable(L, -2);
196         lua_remove(L, -2); // object_refs
197         lua_remove(L, -2); // minetest
198 }
199
200 static void luaentity_get(lua_State *L, u16 id)
201 {
202         // Get minetest.luaentities[i]
203         lua_getglobal(L, "minetest");
204         lua_getfield(L, -1, "luaentities");
205         luaL_checktype(L, -1, LUA_TTABLE);
206         lua_pushnumber(L, id);
207         lua_gettable(L, -2);
208         lua_remove(L, -2); // luaentities
209         lua_remove(L, -2); // minetest
210 }
211
212 /*
213         Reference objects
214 */
215 #define method(class, name) {#name, class::l_##name}
216
217 class EnvRef
218 {
219 private:
220         ServerEnvironment *m_env;
221
222         static const char className[];
223         static const luaL_reg methods[];
224
225         static EnvRef *checkobject(lua_State *L, int narg)
226         {
227                 luaL_checktype(L, narg, LUA_TUSERDATA);
228                 void *ud = luaL_checkudata(L, narg, className);
229                 if(!ud) luaL_typerror(L, narg, className);
230                 return *(EnvRef**)ud;  // unbox pointer
231         }
232         
233         // Exported functions
234
235         // EnvRef:add_node(pos, content)
236         // pos = {x=num, y=num, z=num}
237         // content = number
238         static int l_add_node(lua_State *L)
239         {
240                 infostream<<"EnvRef::l_add_node()"<<std::endl;
241                 EnvRef *o = checkobject(L, 1);
242                 ServerEnvironment *env = o->m_env;
243                 if(env == NULL) return 0;
244                 // pos
245                 v3s16 pos;
246                 lua_pushvalue(L, 2); // Push pos
247                 luaL_checktype(L, -1, LUA_TTABLE);
248                 lua_getfield(L, -1, "x");
249                 pos.X = lua_tonumber(L, -1);
250                 lua_pop(L, 1);
251                 lua_getfield(L, -1, "y");
252                 pos.Y = lua_tonumber(L, -1);
253                 lua_pop(L, 1);
254                 lua_getfield(L, -1, "z");
255                 pos.Z = lua_tonumber(L, -1);
256                 lua_pop(L, 1);
257                 lua_pop(L, 1); // Pop pos
258                 // content
259                 u16 content = 0;
260                 lua_pushvalue(L, 3); // Push content
261                 content = lua_tonumber(L, -1);
262                 lua_pop(L, 1); // Pop content
263                 // Do it
264                 env->getMap().addNodeWithEvent(pos, MapNode(content));
265                 return 0;
266         }
267
268         static int gc_object(lua_State *L) {
269                 EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1));
270                 delete o;
271                 return 0;
272         }
273
274 public:
275         EnvRef(ServerEnvironment *env):
276                 m_env(env)
277         {
278                 infostream<<"EnvRef created"<<std::endl;
279         }
280
281         ~EnvRef()
282         {
283                 infostream<<"EnvRef destructing"<<std::endl;
284         }
285
286         // Creates an EnvRef and leaves it on top of stack
287         // Not callable from Lua; all references are created on the C side.
288         static void create(lua_State *L, ServerEnvironment *env)
289         {
290                 EnvRef *o = new EnvRef(env);
291                 //infostream<<"EnvRef::create: o="<<o<<std::endl;
292                 *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
293                 luaL_getmetatable(L, className);
294                 lua_setmetatable(L, -2);
295         }
296
297         static void set_null(lua_State *L)
298         {
299                 EnvRef *o = checkobject(L, -1);
300                 o->m_env = NULL;
301         }
302         
303         static void Register(lua_State *L)
304         {
305                 lua_newtable(L);
306                 int methodtable = lua_gettop(L);
307                 luaL_newmetatable(L, className);
308                 int metatable = lua_gettop(L);
309
310                 lua_pushliteral(L, "__metatable");
311                 lua_pushvalue(L, methodtable);
312                 lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
313
314                 lua_pushliteral(L, "__index");
315                 lua_pushvalue(L, methodtable);
316                 lua_settable(L, metatable);
317
318                 lua_pushliteral(L, "__gc");
319                 lua_pushcfunction(L, gc_object);
320                 lua_settable(L, metatable);
321
322                 lua_pop(L, 1);  // drop metatable
323
324                 luaL_openlib(L, 0, methods, 0);  // fill methodtable
325                 lua_pop(L, 1);  // drop methodtable
326
327                 // Cannot be created from Lua
328                 //lua_register(L, className, create_object);
329         }
330 };
331 const char EnvRef::className[] = "EnvRef";
332 const luaL_reg EnvRef::methods[] = {
333         method(EnvRef, add_node),
334         {0,0}
335 };
336
337 class ObjectRef
338 {
339 private:
340         ServerActiveObject *m_object;
341
342         static const char className[];
343         static const luaL_reg methods[];
344
345         static ObjectRef *checkobject(lua_State *L, int narg)
346         {
347                 luaL_checktype(L, narg, LUA_TUSERDATA);
348                 void *ud = luaL_checkudata(L, narg, className);
349                 if(!ud) luaL_typerror(L, narg, className);
350                 return *(ObjectRef**)ud;  // unbox pointer
351         }
352         
353         static ServerActiveObject* getobject(ObjectRef *ref)
354         {
355                 ServerActiveObject *co = ref->m_object;
356                 return co;
357         }
358         
359         static LuaEntitySAO* getluaobject(ObjectRef *ref)
360         {
361                 ServerActiveObject *obj = getobject(ref);
362                 if(obj == NULL)
363                         return NULL;
364                 if(obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY)
365                         return NULL;
366                 return (LuaEntitySAO*)obj;
367         }
368         
369         // Exported functions
370         
371         // remove(self)
372         static int l_remove(lua_State *L)
373         {
374                 ObjectRef *ref = checkobject(L, 1);
375                 ServerActiveObject *co = getobject(ref);
376                 if(co == NULL) return 0;
377                 infostream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl;
378                 co->m_removed = true;
379                 return 0;
380         }
381         
382         // getpos(self)
383         // returns: {x=num, y=num, z=num}
384         static int l_getpos(lua_State *L)
385         {
386                 ObjectRef *ref = checkobject(L, 1);
387                 ServerActiveObject *co = getobject(ref);
388                 if(co == NULL) return 0;
389                 v3f pos = co->getBasePosition() / BS;
390                 lua_newtable(L);
391                 lua_pushnumber(L, pos.X);
392                 lua_setfield(L, -2, "x");
393                 lua_pushnumber(L, pos.Y);
394                 lua_setfield(L, -2, "y");
395                 lua_pushnumber(L, pos.Z);
396                 lua_setfield(L, -2, "z");
397                 return 1;
398         }
399         
400         // setpos(self, pos)
401         static int l_setpos(lua_State *L)
402         {
403                 ObjectRef *ref = checkobject(L, 1);
404                 //LuaEntitySAO *co = getluaobject(ref);
405                 ServerActiveObject *co = getobject(ref);
406                 if(co == NULL) return 0;
407                 // pos
408                 v3f pos = readFloatPos(L, 2);
409                 // Do it
410                 co->setPos(pos);
411                 return 0;
412         }
413         
414         // moveto(self, pos, continuous=false)
415         static int l_moveto(lua_State *L)
416         {
417                 ObjectRef *ref = checkobject(L, 1);
418                 //LuaEntitySAO *co = getluaobject(ref);
419                 ServerActiveObject *co = getobject(ref);
420                 if(co == NULL) return 0;
421                 // pos
422                 v3f pos = readFloatPos(L, 2);
423                 // continuous
424                 bool continuous = lua_toboolean(L, 3);
425                 // Do it
426                 co->moveTo(pos, continuous);
427                 return 0;
428         }
429
430         static int gc_object(lua_State *L) {
431                 //ObjectRef *o = checkobject(L, 1);
432                 ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1));
433                 //infostream<<"ObjectRef::gc_object: o="<<o<<std::endl;
434                 delete o;
435                 return 0;
436         }
437
438 public:
439         ObjectRef(ServerActiveObject *object):
440                 m_object(object)
441         {
442                 //infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl;
443         }
444
445         ~ObjectRef()
446         {
447                 /*if(m_object)
448                         infostream<<"ObjectRef destructing for id="
449                                         <<m_object->getId()<<std::endl;
450                 else
451                         infostream<<"ObjectRef destructing for id=unknown"<<std::endl;*/
452         }
453
454         // Creates an ObjectRef and leaves it on top of stack
455         // Not callable from Lua; all references are created on the C side.
456         static void create(lua_State *L, ServerActiveObject *object)
457         {
458                 ObjectRef *o = new ObjectRef(object);
459                 //infostream<<"ObjectRef::create: o="<<o<<std::endl;
460                 *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
461                 luaL_getmetatable(L, className);
462                 lua_setmetatable(L, -2);
463         }
464
465         static void set_null(lua_State *L)
466         {
467                 ObjectRef *o = checkobject(L, -1);
468                 o->m_object = NULL;
469         }
470         
471         static void Register(lua_State *L)
472         {
473                 lua_newtable(L);
474                 int methodtable = lua_gettop(L);
475                 luaL_newmetatable(L, className);
476                 int metatable = lua_gettop(L);
477
478                 lua_pushliteral(L, "__metatable");
479                 lua_pushvalue(L, methodtable);
480                 lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
481
482                 lua_pushliteral(L, "__index");
483                 lua_pushvalue(L, methodtable);
484                 lua_settable(L, metatable);
485
486                 lua_pushliteral(L, "__gc");
487                 lua_pushcfunction(L, gc_object);
488                 lua_settable(L, metatable);
489
490                 lua_pop(L, 1);  // drop metatable
491
492                 luaL_openlib(L, 0, methods, 0);  // fill methodtable
493                 lua_pop(L, 1);  // drop methodtable
494
495                 // Cannot be created from Lua
496                 //lua_register(L, className, create_object);
497         }
498 };
499 const char ObjectRef::className[] = "ObjectRef";
500 const luaL_reg ObjectRef::methods[] = {
501         method(ObjectRef, remove),
502         method(ObjectRef, getpos),
503         method(ObjectRef, setpos),
504         method(ObjectRef, moveto),
505         {0,0}
506 };
507
508 /*
509         Main export function
510 */
511
512 void scriptapi_export(lua_State *L, Server *server)
513 {
514         realitycheck(L);
515         assert(lua_checkstack(L, 20));
516         infostream<<"scriptapi_export"<<std::endl;
517         StackUnroller stack_unroller(L);
518         
519         // Register global functions in table minetest
520         lua_newtable(L);
521         luaL_register(L, NULL, minetest_f);
522         lua_setglobal(L, "minetest");
523         
524         // Get the main minetest table
525         lua_getglobal(L, "minetest");
526
527         // Add registered_entities table in minetest
528         lua_newtable(L);
529         lua_setfield(L, -2, "registered_entities");
530
531         // Add object_refs table in minetest
532         lua_newtable(L);
533         lua_setfield(L, -2, "object_refs");
534
535         // Add luaentities table in minetest
536         lua_newtable(L);
537         lua_setfield(L, -2, "luaentities");
538
539         // Create entity prototype
540         luaL_newmetatable(L, "minetest.entity");
541         // metatable.__index = metatable
542         lua_pushvalue(L, -1); // Duplicate metatable
543         lua_setfield(L, -2, "__index");
544         // Put functions in metatable
545         luaL_register(L, NULL, minetest_entity_m);
546         // Put other stuff in metatable
547
548         // Environment C reference
549         EnvRef::Register(L);
550
551         // Object C reference
552         ObjectRef::Register(L);
553 }
554
555 void scriptapi_add_environment(lua_State *L, ServerEnvironment *env)
556 {
557         realitycheck(L);
558         assert(lua_checkstack(L, 20));
559         infostream<<"scriptapi_add_environment"<<std::endl;
560         StackUnroller stack_unroller(L);
561
562         // Create EnvRef on stack
563         EnvRef::create(L, env);
564         int envref = lua_gettop(L);
565
566         // minetest.env = envref
567         lua_getglobal(L, "minetest");
568         luaL_checktype(L, -1, LUA_TTABLE);
569         lua_pushvalue(L, envref);
570         lua_setfield(L, -2, "env");
571 }
572
573 // Dump stack top with the dump2 function
574 static void dump2(lua_State *L, const char *name)
575 {
576         // Dump object (debug)
577         lua_getglobal(L, "dump2");
578         luaL_checktype(L, -1, LUA_TFUNCTION);
579         lua_pushvalue(L, -2); // Get previous stack top as first parameter
580         lua_pushstring(L, name);
581         if(lua_pcall(L, 2, 0, 0))
582                 script_error(L, "error: %s\n", lua_tostring(L, -1));
583 }
584
585 /*
586         object_reference
587 */
588
589 void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj)
590 {
591         realitycheck(L);
592         assert(lua_checkstack(L, 20));
593         infostream<<"scriptapi_add_object_reference: id="<<cobj->getId()<<std::endl;
594         StackUnroller stack_unroller(L);
595
596         // Create object on stack
597         ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack
598         int object = lua_gettop(L);
599
600         // Get minetest.object_refs table
601         lua_getglobal(L, "minetest");
602         lua_getfield(L, -1, "object_refs");
603         luaL_checktype(L, -1, LUA_TTABLE);
604         int objectstable = lua_gettop(L);
605         
606         // object_refs[id] = object
607         lua_pushnumber(L, cobj->getId()); // Push id
608         lua_pushvalue(L, object); // Copy object to top of stack
609         lua_settable(L, objectstable);
610 }
611
612 void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj)
613 {
614         realitycheck(L);
615         assert(lua_checkstack(L, 20));
616         infostream<<"scriptapi_rm_object_reference: id="<<cobj->getId()<<std::endl;
617         StackUnroller stack_unroller(L);
618
619         // Get minetest.object_refs table
620         lua_getglobal(L, "minetest");
621         lua_getfield(L, -1, "object_refs");
622         luaL_checktype(L, -1, LUA_TTABLE);
623         int objectstable = lua_gettop(L);
624         
625         // Get object_refs[id]
626         lua_pushnumber(L, cobj->getId()); // Push id
627         lua_gettable(L, objectstable);
628         // Set object reference to NULL
629         ObjectRef::set_null(L);
630         lua_pop(L, 1); // pop object
631
632         // Set object_refs[id] = nil
633         lua_pushnumber(L, cobj->getId()); // Push id
634         lua_pushnil(L);
635         lua_settable(L, objectstable);
636 }
637
638 /*
639         luaentity
640 */
641
642 void scriptapi_luaentity_add(lua_State *L, u16 id, const char *name,
643                 const char *init_state)
644 {
645         realitycheck(L);
646         assert(lua_checkstack(L, 20));
647         infostream<<"scriptapi_luaentity_add: id="<<id<<" name=\""
648                         <<name<<"\""<<std::endl;
649         StackUnroller stack_unroller(L);
650         
651         // Create object as a dummy string (TODO: Create properly)
652
653         // Get minetest.registered_entities[name]
654         lua_getglobal(L, "minetest");
655         lua_getfield(L, -1, "registered_entities");
656         luaL_checktype(L, -1, LUA_TTABLE);
657         lua_pushstring(L, name);
658         lua_gettable(L, -2);
659         // Should be a table, which we will use as a prototype
660         luaL_checktype(L, -1, LUA_TTABLE);
661         int prototype_table = lua_gettop(L);
662         //dump2(L, "prototype_table");
663         
664         // Create entity object
665         lua_newtable(L);
666         int object = lua_gettop(L);
667
668         // Set object metatable
669         lua_pushvalue(L, prototype_table);
670         lua_setmetatable(L, -2);
671         
672         // Add object reference
673         // This should be userdata with metatable ObjectRef
674         objectref_get(L, id);
675         luaL_checktype(L, -1, LUA_TUSERDATA);
676         if(!luaL_checkudata(L, -1, "ObjectRef"))
677                 luaL_typerror(L, -1, "ObjectRef");
678         lua_setfield(L, -2, "object");
679
680         // minetest.luaentities[id] = object
681         lua_getglobal(L, "minetest");
682         lua_getfield(L, -1, "luaentities");
683         luaL_checktype(L, -1, LUA_TTABLE);
684         lua_pushnumber(L, id); // Push id
685         lua_pushvalue(L, object); // Copy object to top of stack
686         lua_settable(L, -3);
687         
688         // This callback doesn't really make sense
689         /*// Get on_activate function
690         lua_pushvalue(L, object);
691         lua_getfield(L, -1, "on_activate");
692         luaL_checktype(L, -1, LUA_TFUNCTION);
693         lua_pushvalue(L, object); // self
694         // Call with 1 arguments, 0 results
695         if(lua_pcall(L, 1, 0, 0))
696                 script_error(L, "error running function %s:on_activate: %s\n",
697                                 name, lua_tostring(L, -1));*/
698 }
699
700 void scriptapi_luaentity_rm(lua_State *L, u16 id)
701 {
702         realitycheck(L);
703         assert(lua_checkstack(L, 20));
704         infostream<<"scriptapi_luaentity_rm: id="<<id<<std::endl;
705
706         // Get minetest.luaentities table
707         lua_getglobal(L, "minetest");
708         lua_getfield(L, -1, "luaentities");
709         luaL_checktype(L, -1, LUA_TTABLE);
710         int objectstable = lua_gettop(L);
711         
712         // Set luaentities[id] = nil
713         lua_pushnumber(L, id); // Push id
714         lua_pushnil(L);
715         lua_settable(L, objectstable);
716         
717         lua_pop(L, 2); // pop luaentities, minetest
718 }
719
720 std::string scriptapi_luaentity_get_state(lua_State *L, u16 id)
721 {
722         realitycheck(L);
723         assert(lua_checkstack(L, 20));
724         infostream<<"scriptapi_luaentity_get_state: id="<<id<<std::endl;
725         
726         return "";
727 }
728
729 void scriptapi_luaentity_get_properties(lua_State *L, u16 id,
730                 LuaEntityProperties *prop)
731 {
732         realitycheck(L);
733         assert(lua_checkstack(L, 20));
734         infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl;
735         StackUnroller stack_unroller(L);
736
737         // Get minetest.luaentities[id]
738         luaentity_get(L, id);
739         //int object = lua_gettop(L);
740
741         lua_getfield(L, -1, "physical");
742         if(lua_isboolean(L, -1))
743                 prop->physical = lua_toboolean(L, -1);
744         lua_pop(L, 1);
745         
746         lua_getfield(L, -1, "weight");
747         prop->weight = lua_tonumber(L, -1);
748         lua_pop(L, 1);
749
750         lua_getfield(L, -1, "collisionbox");
751         if(lua_istable(L, -1)){
752                 lua_rawgeti(L, -1, 1);
753                 prop->collisionbox.MinEdge.X = lua_tonumber(L, -1);
754                 lua_pop(L, 1);
755                 lua_rawgeti(L, -1, 2);
756                 prop->collisionbox.MinEdge.Y = lua_tonumber(L, -1);
757                 lua_pop(L, 1);
758                 lua_rawgeti(L, -1, 3);
759                 prop->collisionbox.MinEdge.Z = lua_tonumber(L, -1);
760                 lua_pop(L, 1);
761                 lua_rawgeti(L, -1, 4);
762                 prop->collisionbox.MaxEdge.X = lua_tonumber(L, -1);
763                 lua_pop(L, 1);
764                 lua_rawgeti(L, -1, 5);
765                 prop->collisionbox.MaxEdge.Y = lua_tonumber(L, -1);
766                 lua_pop(L, 1);
767                 lua_rawgeti(L, -1, 6);
768                 prop->collisionbox.MaxEdge.Z = lua_tonumber(L, -1);
769                 lua_pop(L, 1);
770         }
771         lua_pop(L, 1);
772
773         lua_getfield(L, -1, "visual");
774         if(lua_isstring(L, -1))
775                 prop->visual = lua_tostring(L, -1);
776         lua_pop(L, 1);
777         
778         lua_getfield(L, -1, "textures");
779         if(lua_istable(L, -1)){
780                 prop->textures.clear();
781                 int table = lua_gettop(L);
782                 lua_pushnil(L);
783                 while(lua_next(L, table) != 0){
784                         // key at index -2 and value at index -1
785                         if(lua_isstring(L, -1))
786                                 prop->textures.push_back(lua_tostring(L, -1));
787                         else
788                                 prop->textures.push_back("");
789                         // removes value, keeps key for next iteration
790                         lua_pop(L, 1);
791                 }
792         }
793         lua_pop(L, 1);
794
795 }
796
797 void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime)
798 {
799         realitycheck(L);
800         assert(lua_checkstack(L, 20));
801         //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
802         StackUnroller stack_unroller(L);
803
804         // Get minetest.luaentities[id]
805         luaentity_get(L, id);
806         int object = lua_gettop(L);
807         // State: object is at top of stack
808         // Get step function
809         lua_getfield(L, -1, "on_step");
810         luaL_checktype(L, -1, LUA_TFUNCTION);
811         lua_pushvalue(L, object); // self
812         lua_pushnumber(L, dtime); // dtime
813         // Call with 2 arguments, 0 results
814         if(lua_pcall(L, 2, 0, 0))
815                 script_error(L, "error running function 'step': %s\n", lua_tostring(L, -1));
816 }
817
818 void scriptapi_luaentity_rightclick_player(lua_State *L, u16 id,
819                 const char *playername)
820 {
821         realitycheck(L);
822         assert(lua_checkstack(L, 20));
823         //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl;
824         StackUnroller stack_unroller(L);
825
826         // Get minetest.luaentities[id]
827         luaentity_get(L, id);
828         int object = lua_gettop(L);
829         // State: object is at top of stack
830         // Get step function
831         lua_getfield(L, -1, "on_rightclick");
832         luaL_checktype(L, -1, LUA_TFUNCTION);
833         lua_pushvalue(L, object); // self
834         // Call with 1 arguments, 0 results
835         if(lua_pcall(L, 1, 0, 0))
836                 script_error(L, "error running function 'step': %s\n", lua_tostring(L, -1));
837 }
838