mainly work on object scripting api
[oweals/minetest.git] / src / clientobject.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-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 "clientobject.h"
21 #include "debug.h"
22 #include "porting.h"
23 #include "constants.h"
24 #include "utility.h"
25
26 ClientActiveObject::ClientActiveObject(u16 id):
27         ActiveObject(id)
28 {
29 }
30
31 ClientActiveObject::~ClientActiveObject()
32 {
33         removeFromScene();
34 }
35
36 ClientActiveObject* ClientActiveObject::create(u8 type)
37 {
38         if(type == ACTIVEOBJECT_TYPE_INVALID)
39         {
40                 dstream<<"ClientActiveObject::create(): passed "
41                                 <<"ACTIVEOBJECT_TYPE_INVALID"<<std::endl;
42                 return NULL;
43         }
44         else if(type == ACTIVEOBJECT_TYPE_TEST)
45         {
46                 dstream<<"ClientActiveObject::create(): passed "
47                                 <<"ACTIVEOBJECT_TYPE_TEST"<<std::endl;
48                 return new TestCAO(0);
49         }
50         else if(type == ACTIVEOBJECT_TYPE_LUA)
51         {
52                 dstream<<"ClientActiveObject::create(): passed "
53                                 <<"ACTIVEOBJECT_TYPE_LUA"<<std::endl;
54                 return new LuaCAO(0);
55         }
56         else
57         {
58                 dstream<<"ClientActiveObject::create(): passed "
59                                 <<"unknown type="<<type<<std::endl;
60                 return NULL;
61         }
62 }
63
64 /*
65         TestCAO
66 */
67
68 TestCAO::TestCAO(u16 id):
69         ClientActiveObject(id),
70         m_node(NULL),
71         m_position(v3f(0,10*BS,0))
72 {
73 }
74
75 TestCAO::~TestCAO()
76 {
77 }
78
79 void TestCAO::addToScene(scene::ISceneManager *smgr)
80 {
81         if(m_node != NULL)
82                 return;
83         
84         video::IVideoDriver* driver = smgr->getVideoDriver();
85         
86         scene::SMesh *mesh = new scene::SMesh();
87         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
88         video::SColor c(255,255,255,255);
89         video::S3DVertex vertices[4] =
90         {
91                 video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
92                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
93                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
94                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),
95         };
96         u16 indices[] = {0,1,2,2,3,0};
97         buf->append(vertices, 4, indices, 6);
98         // Set material
99         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
100         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
101         buf->getMaterial().setTexture
102                         (0, driver->getTexture(porting::getDataPath("rat.png").c_str()));
103         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
104         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
105         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
106         // Add to mesh
107         mesh->addMeshBuffer(buf);
108         buf->drop();
109         m_node = smgr->addMeshSceneNode(mesh, NULL);
110         mesh->drop();
111         updateNodePos();
112 }
113
114 void TestCAO::removeFromScene()
115 {
116         if(m_node == NULL)
117                 return;
118
119         m_node->remove();
120         m_node = NULL;
121 }
122
123 void TestCAO::updateLight(u8 light_at_pos)
124 {
125 }
126
127 v3s16 TestCAO::getLightPosition()
128 {
129         return floatToInt(m_position, BS);
130 }
131
132 void TestCAO::updateNodePos()
133 {
134         if(m_node == NULL)
135                 return;
136
137         m_node->setPosition(m_position);
138         //m_node->setRotation(v3f(0, 45, 0));
139 }
140
141 void TestCAO::step(float dtime)
142 {
143         if(m_node)
144         {
145                 v3f rot = m_node->getRotation();
146                 //dstream<<"dtime="<<dtime<<", rot.Y="<<rot.Y<<std::endl;
147                 rot.Y += dtime * 180;
148                 m_node->setRotation(rot);
149         }
150 }
151
152 void TestCAO::processMessage(const std::string &data)
153 {
154         dstream<<"TestCAO: Got data: "<<data<<std::endl;
155         std::istringstream is(data, std::ios::binary);
156         u16 cmd;
157         is>>cmd;
158         if(cmd == 0)
159         {
160                 v3f newpos;
161                 is>>newpos.X;
162                 is>>newpos.Y;
163                 is>>newpos.Z;
164                 m_position = newpos;
165                 updateNodePos();
166         }
167 }
168
169 /*
170         LuaCAO
171 */
172
173 extern "C"{
174 #include "lstring.h"
175 }
176
177 /*
178         Callbacks in script:
179
180         on_step(self, dtime)
181         on_process_message(self, data)
182         on_initialize(self, data)
183         TODO:
184         string on_get_info_text(self)
185         on_block_removed_near({X=,Y=,Z=}, node)
186         on_block_placed_near({X=,Y=,Z=}, node)
187 */
188
189 /*
190         object_set_position(self, p)
191 */
192 static int lf_object_set_position(lua_State *L)
193 {
194         // 2: position
195         assert(lua_istable(L, -1));
196         lua_pushstring(L, "X");
197         lua_gettable(L, -2);
198         lua_Number x = lua_tonumber(L, -1);
199         lua_pop(L, 1);
200         lua_pushstring(L, "Y");
201         lua_gettable(L, -2);
202         lua_Number y = lua_tonumber(L, -1);
203         lua_pop(L, 1);
204         lua_pushstring(L, "Z");
205         lua_gettable(L, -2);
206         lua_Number z = lua_tonumber(L, -1);
207         lua_pop(L, 1);
208         lua_pop(L, 1);
209         // 1: self
210         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
211         lua_pop(L, 1);
212         
213         assert(self);
214         
215         self->setPosition(v3f(x*BS,y*BS,z*BS));
216         
217         return 0; // Number of return values
218 }
219
220 /*
221         object_set_rotation(self, p)
222 */
223 static int lf_object_set_rotation(lua_State *L)
224 {
225         // 2: position
226         assert(lua_istable(L, -1));
227         lua_pushstring(L, "X");
228         lua_gettable(L, -2);
229         lua_Number x = lua_tonumber(L, -1);
230         lua_pop(L, 1);
231         lua_pushstring(L, "Y");
232         lua_gettable(L, -2);
233         lua_Number y = lua_tonumber(L, -1);
234         lua_pop(L, 1);
235         lua_pushstring(L, "Z");
236         lua_gettable(L, -2);
237         lua_Number z = lua_tonumber(L, -1);
238         lua_pop(L, 1);
239         lua_pop(L, 1);
240         // 1: self
241         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
242         lua_pop(L, 1);
243         
244         assert(self);
245         
246         self->setRotation(v3f(x,y,z));
247         
248         return 0; // Number of return values
249 }
250
251 /*
252         object_add_to_mesh(self, image, corners, backface_culling)
253         corners is an array like this:
254         {{x,y,z},{x,y,z},{x,y,z},{x,y,z}}
255 */
256 static int lf_object_add_to_mesh(lua_State *L)
257 {
258         // 4: backface_culling
259         bool backface_culling = lua_toboolean(L, -1);
260         lua_pop(L, 1);
261         // 3: corners
262         if(lua_istable(L, -1) == false)
263         {
264                 dstream<<"ERROR: object_add_to_mesh(): parameter 3 not a table"
265                                 <<std::endl;
266                 return 0;
267         }
268         v3f corners[4];
269         // Loop table
270         for(int i=0; i<4; i++)
271         {
272                 // Get child table
273                 lua_pushinteger(L, i+1);
274                 lua_gettable(L, -2);
275                 if(lua_istable(L, -1) == false)
276                 {
277                         dstream<<"ERROR: object_add_to_mesh(): parameter 3 not a"
278                                         " table of tables"<<std::endl;
279                         return 0;
280                 }
281
282                 // Get x, y and z from the child table
283                 
284                 lua_pushinteger(L, 1);
285                 lua_gettable(L, -2);
286                 corners[i].X = lua_tonumber(L, -1) * BS;
287                 lua_pop(L, 1);
288
289                 lua_pushinteger(L, 2);
290                 lua_gettable(L, -2);
291                 corners[i].Y = lua_tonumber(L, -1) * BS;
292                 lua_pop(L, 1);
293
294                 lua_pushinteger(L, 3);
295                 lua_gettable(L, -2);
296                 corners[i].Z = lua_tonumber(L, -1) * BS;
297                 lua_pop(L, 1);
298
299                 // Pop child table
300                 lua_pop(L, 1);
301         }
302         lua_pop(L, 1);
303         // 2: image
304         const char *image = lua_tostring(L, -1);
305         lua_pop(L, 1);
306         // 1: self
307         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
308         lua_pop(L, 1);
309
310         assert(self);
311         
312         self->addToMesh(image, corners, backface_culling);
313         
314         return 0; // Number of return values
315 }
316
317 /*
318         object_clear_mesh(self)
319 */
320 static int lf_object_clear_mesh(lua_State *L)
321 {
322         // 1: self
323         LuaCAO *self = (LuaCAO*)lua_touserdata(L, -1);
324         lua_pop(L, 1);
325         
326         assert(self);
327
328         self->clearMesh();
329
330         return 0;
331 }
332
333 LuaCAO::LuaCAO(u16 id):
334         ClientActiveObject(id),
335         L(NULL),
336         m_smgr(NULL),
337         m_node(NULL),
338         m_mesh(NULL),
339         m_position(v3f(0,10*BS,0))
340 {
341         dstream<<"LuaCAO::LuaCAO(): id="<<id<<std::endl;
342         L = lua_open();
343         assert(L);
344         
345         // Load libraries
346         luaopen_base(L);
347         luaopen_table(L);
348         luaopen_string(L);
349         luaopen_math(L);
350
351         // Disable some stuff
352         const char *to_disable[] = {
353                 "arg",
354                 "debug",
355                 "dofile",
356                 "io",
357                 "loadfile",
358                 "os",
359                 "package",
360                 "require",
361                 NULL
362         };
363         const char **td = to_disable;
364         do{
365                 lua_pushnil(L);
366                 lua_setglobal(L, *td);
367         }while(*(++td));
368         
369         // Add globals
370         //lua_pushlightuserdata(L, this);
371         //lua_setglobal(L, "self");
372         
373         // Register functions
374         lua_register(L, "object_set_position", lf_object_set_position);
375         lua_register(L, "object_set_rotation", lf_object_set_rotation);
376         lua_register(L, "object_add_to_mesh", lf_object_add_to_mesh);
377         lua_register(L, "object_clear_mesh", lf_object_clear_mesh);
378 }
379
380 LuaCAO::~LuaCAO()
381 {
382         lua_close(L);
383 }
384
385 void LuaCAO::step(float dtime)
386 {
387         /*
388                 Call step(self, dtime) from lua
389         */
390         
391         const char *funcname = "on_step";
392         lua_getglobal(L, funcname);
393         if(!lua_isfunction(L,-1))
394         {
395                 lua_pop(L,1);
396                 dstream<<"WARNING: LuaCAO: Function not found: "
397                                 <<funcname<<std::endl;
398                 return;
399         }
400         
401         // Parameters:
402         // 1: self
403         lua_pushlightuserdata(L, this);
404         // 2: dtime
405         lua_pushnumber(L, dtime);
406         
407         // Call (2 parameters, 0 result)
408         if(lua_pcall(L, 2, 0, 0))
409         {
410                 dstream<<"WARNING: LuaCAO: Error running function "
411                                 <<funcname<<": "
412                                 <<lua_tostring(L,-1)<<std::endl;
413                 return;
414         }
415 }
416
417 void LuaCAO::processMessage(const std::string &data)
418 {
419         /*
420                 Call process_message(self, data) from lua
421         */
422         
423         const char *funcname = "on_process_message";
424         lua_getglobal(L, funcname);
425         if(!lua_isfunction(L,-1))
426         {
427                 lua_pop(L,1);
428                 dstream<<"WARNING: LuaCAO: Function not found: "
429                                 <<funcname<<std::endl;
430                 return;
431         }
432         
433         // Parameters:
434         // 1: self
435         lua_pushlightuserdata(L, this);
436         // 2: data
437         lua_pushlstring(L, data.c_str(), data.size());
438         
439         // Call (2 parameters, 0 results)
440         if(lua_pcall(L, 2, 1, 0))
441         {
442                 dstream<<"WARNING: LuaCAO: Error running function "
443                                 <<funcname<<": "
444                                 <<lua_tostring(L,-1)<<std::endl;
445                 return;
446         }
447 }
448
449 void LuaCAO::initialize(const std::string &data)
450 {
451         dstream<<"LuaCAO::initialize(): id="<<getId()<<std::endl;
452
453         std::istringstream is(data, std::ios::binary);
454         std::string script = deSerializeLongString(is);
455         std::string other = deSerializeLongString(is);
456
457         /*dstream<<"=== script (size="<<script.size()<<")"<<std::endl
458                         <<script<<std::endl
459                         <<"==="<<std::endl;*/
460         dstream<<"LuaCAO::initialize(): script size="<<script.size()<<std::endl;
461         
462         /*dstream<<"other.size()="<<other.size()<<std::endl;
463         dstream<<"other=\""<<other<<"\""<<std::endl;*/
464         
465         // Load the script to lua
466         loadScript(script);
467
468         /*
469                 Call initialize(self, data) in the script
470         */
471         
472         const char *funcname = "on_initialize";
473         lua_getglobal(L, funcname);
474         if(!lua_isfunction(L,-1))
475         {
476                 lua_pop(L,1);
477                 dstream<<"WARNING: LuaCAO: Function not found: "
478                                 <<funcname<<std::endl;
479                 return;
480         }
481         
482         // Parameters:
483         // 1: self
484         lua_pushlightuserdata(L, this);
485         // 2: data (other)
486         lua_pushlstring(L, other.c_str(), other.size());
487         
488         // Call (2 parameters, 0 result)
489         if(lua_pcall(L, 2, 0, 0))
490         {
491                 dstream<<"WARNING: LuaCAO: Error running function "
492                                 <<funcname<<": "
493                                 <<lua_tostring(L,-1)<<std::endl;
494                 return;
495         }
496
497 }
498
499 void LuaCAO::loadScript(const std::string script)
500 {
501         int ret;
502         ret = luaL_loadstring(L, script.c_str());
503         if(ret)
504         {
505                 const char *message = lua_tostring(L, -1);
506                 lua_pop(L, 1);
507                 dstream<<"LuaCAO::loadScript(): lua_loadstring failed: "
508                                 <<message<<std::endl;
509                 assert(0);
510                 return;
511         }
512         ret = lua_pcall(L, 0, 0, 0);
513         if(ret)
514         {
515                 const char *message = lua_tostring(L, -1);
516                 lua_pop(L, 1);
517                 dstream<<"LuaCAO::loadScript(): lua_pcall failed: "
518                                 <<message<<std::endl;
519                 assert(0);
520                 return;
521         }
522 }
523
524 void LuaCAO::addToScene(scene::ISceneManager *smgr)
525 {
526         if(m_smgr != NULL)
527         {
528                 dstream<<"WARNING: LuaCAO::addToScene() called more than once"
529                                 <<std::endl;
530                 return;
531         }
532
533         if(m_node != NULL)
534         {
535                 dstream<<"WARNING: LuaCAO::addToScene(): m_node != NULL"
536                                 <<std::endl;
537                 return;
538         }
539         
540         m_smgr = smgr;
541         
542         if(m_mesh == NULL)
543         {
544                 m_mesh = new scene::SMesh();
545                 m_node = smgr->addMeshSceneNode(m_mesh, NULL);
546                 
547                 /*v3f corners[4] = {
548                         v3f(-BS/2,-BS/4,0),
549                         v3f(BS/2,-BS/4,0),
550                         v3f(BS/2,BS/4,0),
551                         v3f(-BS/2,BS/4,0),
552                 };
553                 addToMesh("rat.png", corners, false);*/
554         }
555         else
556         {
557                 dstream<<"WARNING: LuaCAO::addToScene(): m_mesh != NULL"
558                                 <<std::endl;
559                 return;
560         }
561         
562         updateNodePos();
563 }
564
565 void LuaCAO::addToMesh(const char *image, v3f *corners, bool backface_culling)
566 {
567         dstream<<"INFO: addToMesh called"<<std::endl;
568
569         video::IVideoDriver* driver = m_smgr->getVideoDriver();
570         
571         assert(m_mesh);
572                 
573         scene::IMeshBuffer *buf = new scene::SMeshBuffer();
574         video::SColor c(255,255,255,255);
575         video::S3DVertex vertices[4] =
576         {
577                 video::S3DVertex(corners[0], v3f(0,0,0), c, v2f(0,1)),
578                 video::S3DVertex(corners[1], v3f(0,0,0), c, v2f(1,1)),
579                 video::S3DVertex(corners[2], v3f(0,0,0), c, v2f(1,0)),
580                 video::S3DVertex(corners[3], v3f(0,0,0), c, v2f(0,0)),
581                 /*video::S3DVertex(-BS/2,-BS/4,0, 0,0,0, c, 0,1),
582                 video::S3DVertex(BS/2,-BS/4,0, 0,0,0, c, 1,1),
583                 video::S3DVertex(BS/2,BS/4,0, 0,0,0, c, 1,0),
584                 video::S3DVertex(-BS/2,BS/4,0, 0,0,0, c, 0,0),*/
585         };
586         u16 indices[] = {0,1,2,2,3,0};
587         buf->append(vertices, 4, indices, 6);
588         // Set material
589         buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
590         buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, backface_culling);
591         buf->getMaterial().setTexture
592                         (0, driver->getTexture(porting::getDataPath(image).c_str()));
593         buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
594         buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
595         buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
596         // Add to mesh
597         m_mesh->addMeshBuffer(buf);
598         buf->drop();
599         // Reset mesh
600         if(m_node)
601                 m_node->setMesh(m_mesh);
602 }
603
604 void LuaCAO::clearMesh()
605 {
606         if(m_node)
607         {
608                 m_node->setMesh(NULL);
609         }
610         
611         if(m_mesh)
612         {
613                 m_mesh->drop();
614                 m_mesh = NULL;
615         }
616 }
617
618 void LuaCAO::removeFromScene()
619 {
620         if(m_node == NULL)
621                 return;
622         
623         if(m_node)
624         {
625                 m_node->remove();
626                 m_node = NULL;
627         }
628         if(m_mesh)
629         {
630                 m_mesh->drop();
631                 m_mesh = NULL;
632         }
633
634         m_smgr = NULL;
635 }
636
637 void LuaCAO::updateLight(u8 light_at_pos)
638 {
639 }
640
641 v3s16 LuaCAO::getLightPosition()
642 {
643         return floatToInt(m_position, BS);
644 }
645
646 void LuaCAO::updateNodePos()
647 {
648         if(m_node == NULL)
649                 return;
650
651         m_node->setPosition(m_position);
652         m_node->setRotation(-m_rotation);
653 }
654
655 void LuaCAO::setPosition(v3f pos)
656 {
657         m_position = pos;
658         updateNodePos();
659 }
660
661 v3f LuaCAO::getPosition()
662 {
663         return m_position;
664 }
665
666 void LuaCAO::setRotation(v3f rot)
667 {
668         m_rotation = rot;
669         updateNodePos();
670 }
671
672 v3f LuaCAO::getRotation()
673 {
674         return m_rotation;
675 }
676
677