e62f1efd0386708f20041fd23c3414d6ad14124c
[oweals/minetest.git] / src / serverobject.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 "serverobject.h"
21 #include <fstream>
22 #include "environment.h"
23
24 ServerActiveObject::ServerActiveObject(ServerEnvironment *env, u16 id, v3f pos):
25         ActiveObject(id),
26         m_known_by_count(0),
27         m_removed(false),
28         m_env(env),
29         m_base_position(pos)
30 {
31 }
32
33 ServerActiveObject::~ServerActiveObject()
34 {
35 }
36
37 /*
38         TestSAO
39 */
40
41 TestSAO::TestSAO(ServerEnvironment *env, u16 id, v3f pos):
42         ServerActiveObject(env, id, pos),
43         m_timer1(0),
44         m_age(0)
45 {
46 }
47
48 void TestSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
49 {
50         m_age += dtime;
51         if(m_age > 10)
52         {
53                 m_removed = true;
54                 return;
55         }
56
57         m_base_position.Y += dtime * BS * 2;
58         if(m_base_position.Y > 8*BS)
59                 m_base_position.Y = 2*BS;
60
61         m_timer1 -= dtime;
62         if(m_timer1 < 0.0)
63         {
64                 m_timer1 += 0.125;
65                 //dstream<<"TestSAO: id="<<getId()<<" sending data"<<std::endl;
66
67                 std::string data;
68
69                 data += itos(0); // 0 = position
70                 data += " ";
71                 data += itos(m_base_position.X);
72                 data += " ";
73                 data += itos(m_base_position.Y);
74                 data += " ";
75                 data += itos(m_base_position.Z);
76
77                 ActiveObjectMessage aom(getId(), false, data);
78                 messages.push_back(aom);
79         }
80 }
81
82 /*
83         LuaSAO
84 */
85
86 extern "C"{
87 #include "lstring.h"
88 }
89
90 /*
91         Callbacks in script:
92         
93         on_step(self, dtime)
94         on_get_client_init_data(self)
95         on_get_server_init_data(self)
96         on_initialize(self, data)
97 */
98
99 /*
100         object_remove(x,y,z)
101 */
102 static int lf_object_remove(lua_State *L)
103 {
104         // 1: self
105         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
106         lua_pop(L, 1);
107         
108         assert(self);
109
110         self->m_removed = true;
111
112         return 0;
113 }
114
115 /*
116         ServerEnvironment object_get_environment(self)
117 */
118 static int lf_object_get_environment(lua_State *L)
119 {
120         // 1: self
121         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
122         lua_pop(L, 1);
123         
124         assert(self);
125         
126         lua_pushlightuserdata(L, self->getEnv());
127         return 1;
128 }
129
130 /*
131         object_set_base_position(self, {X=,Y=,Z=})
132 */
133 static int lf_object_set_base_position(lua_State *L)
134 {
135         // 2: position
136         assert(lua_istable(L, -1));
137         lua_pushstring(L, "X");
138         lua_gettable(L, -2);
139         lua_Number x = lua_tonumber(L, -1);
140         lua_pop(L, 1);
141         lua_pushstring(L, "Y");
142         lua_gettable(L, -2);
143         lua_Number y = lua_tonumber(L, -1);
144         lua_pop(L, 1);
145         lua_pushstring(L, "Z");
146         lua_gettable(L, -2);
147         lua_Number z = lua_tonumber(L, -1);
148         lua_pop(L, 1);
149         lua_pop(L, 1);
150         // 1: self
151         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
152         lua_pop(L, 1);
153         
154         assert(self);
155         
156         self->setBasePosition(v3f(x*BS,y*BS,z*BS));
157         
158         return 0; // Number of return values
159 }
160
161 /*
162         {X=,Y=,Z=} object_get_base_position(self)
163 */
164 static int lf_object_get_base_position(lua_State *L)
165 {
166         // 1: self
167         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
168         lua_pop(L, 1);
169         
170         assert(self);
171         
172         v3f pos = self->getBasePosition();
173
174         lua_newtable(L);
175
176         lua_pushstring(L, "X");
177         lua_pushnumber(L, pos.X/BS);
178         lua_settable(L, -3);
179
180         lua_pushstring(L, "Y");
181         lua_pushnumber(L, pos.Y/BS);
182         lua_settable(L, -3);
183
184         lua_pushstring(L, "Z");
185         lua_pushnumber(L, pos.Z/BS);
186         lua_settable(L, -3);
187
188         return 1; // Number of return values
189 }
190
191 /*
192         object_add_message(self, string data)
193         lf = luafunc
194 */
195 static int lf_object_add_message(lua_State *L)
196 {
197         // 2: data
198         size_t datalen = 0;
199         const char *data_c = lua_tolstring(L, -1, &datalen);
200         lua_pop(L, 1);
201         // 1: self
202         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
203         lua_pop(L, 1);
204         
205         assert(self);
206         assert(data_c);
207         
208         std::string data(data_c, datalen);
209         //dstream<<"object_add_message: data="<<data<<std::endl;
210         
211         // Create message and add to queue
212         ActiveObjectMessage aom(self->getId());
213         aom.reliable = true;
214         aom.datastring = data;
215         self->m_message_queue.push_back(aom);
216
217         return 0; // Number of return values
218 }
219
220 /*
221         env_get_node(env, {X=,Y=,Z=})
222 */
223 static int lf_env_get_node(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: env
241         ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
242         lua_pop(L, 1);
243         
244         assert(env);
245
246         v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
247
248         /*dstream<<"Checking node from pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z
249                         <<")"<<std::endl;*/
250         
251         // Get the node
252         MapNode n(CONTENT_IGNORE);
253         n = env->getMap().getNodeNoEx(pos);
254
255         // Create a table with some data about the node
256         lua_newtable(L);
257         lua_pushstring(L, "content");
258         lua_pushinteger(L, n.d);
259         lua_settable(L, -3);
260         lua_pushstring(L, "param1");
261         lua_pushinteger(L, n.param);
262         lua_settable(L, -3);
263         lua_pushstring(L, "param2");
264         lua_pushinteger(L, n.param2);
265         lua_settable(L, -3);
266         
267         // Return the table
268         return 1;
269 }
270
271 /*
272         get_content_features(content)
273 */
274 static int lf_get_content_features(lua_State *L)
275 {
276         MapNode n;
277         
278         // 1: content
279         n.d = lua_tointeger(L, -1);
280         lua_pop(L, 1);
281         
282         // Get and return information
283         ContentFeatures &f = content_features(n.d);
284         
285         lua_newtable(L);
286         lua_pushstring(L, "walkable");
287         lua_pushboolean(L, f.walkable);
288         lua_settable(L, -3);
289         lua_pushstring(L, "diggable");
290         lua_pushboolean(L, f.diggable);
291         lua_settable(L, -3);
292         lua_pushstring(L, "buildable_to");
293         lua_pushboolean(L, f.buildable_to);
294         lua_settable(L, -3);
295         
296         return 1;
297 }
298
299 /*
300         bool env_dig_node(env, {X=,Y=,Z=})
301         Return true on success
302 */
303 static int lf_env_dig_node(lua_State *L)
304 {
305         // 2: position
306         assert(lua_istable(L, -1));
307         lua_pushstring(L, "X");
308         lua_gettable(L, -2);
309         lua_Number x = lua_tonumber(L, -1);
310         lua_pop(L, 1);
311         lua_pushstring(L, "Y");
312         lua_gettable(L, -2);
313         lua_Number y = lua_tonumber(L, -1);
314         lua_pop(L, 1);
315         lua_pushstring(L, "Z");
316         lua_gettable(L, -2);
317         lua_Number z = lua_tonumber(L, -1);
318         lua_pop(L, 1);
319         lua_pop(L, 1);
320         // 1: env
321         ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
322         lua_pop(L, 1);
323         assert(env);
324
325         v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
326         
327         /*
328                 Do stuff.
329                 This gets sent to the server by the map through the edit
330                 event system.
331         */
332         bool succeeded = env->getMap().removeNodeWithEvent(pos);
333         
334         lua_pushboolean(L, succeeded);
335         return 1;
336 }
337
338 /*
339         bool env_place_node(env, {X=,Y=,Z=}, node)
340         node={content=,param1=,param2=}
341         param1 and param2 are optional
342         Return true on success
343 */
344 static int lf_env_place_node(lua_State *L)
345 {
346         // 3: node
347         MapNode n(CONTENT_STONE);
348         assert(lua_istable(L, -1));
349         {
350                 lua_pushstring(L, "content");
351                 lua_gettable(L, -2);
352                 if(lua_isnumber(L, -1))
353                         n.d = lua_tonumber(L, -1);
354                 lua_pop(L, 1);
355                 lua_pushstring(L, "param1");
356                 lua_gettable(L, -2);
357                 if(lua_isnumber(L, -1))
358                         n.param = lua_tonumber(L, -1);
359                 lua_pop(L, 1);
360                 lua_pushstring(L, "param2");
361                 lua_gettable(L, -2);
362                 if(lua_isnumber(L, -1))
363                         n.param2 = lua_tonumber(L, -1);
364                 lua_pop(L, 1);
365         }
366         lua_pop(L, 1);
367         // 2: position
368         assert(lua_istable(L, -1));
369         lua_pushstring(L, "X");
370         lua_gettable(L, -2);
371         lua_Number x = lua_tonumber(L, -1);
372         lua_pop(L, 1);
373         lua_pushstring(L, "Y");
374         lua_gettable(L, -2);
375         lua_Number y = lua_tonumber(L, -1);
376         lua_pop(L, 1);
377         lua_pushstring(L, "Z");
378         lua_gettable(L, -2);
379         lua_Number z = lua_tonumber(L, -1);
380         lua_pop(L, 1);
381         lua_pop(L, 1);
382         // 1: env
383         ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
384         lua_pop(L, 1);
385         assert(env);
386
387         v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
388         
389         /*
390                 Do stuff.
391                 This gets sent to the server by the map through the edit
392                 event system.
393         */
394         bool succeeded = env->getMap().addNodeWithEvent(pos, n);
395
396         lua_pushboolean(L, succeeded);
397         return 1;
398 }
399
400 /*
401         string env_get_nearest_player_name(env, {X=,Y=,Z=})
402 */
403 static int lf_env_get_nearest_player_name(lua_State *L)
404 {
405         // 2: position
406         assert(lua_istable(L, -1));
407         lua_pushstring(L, "X");
408         lua_gettable(L, -2);
409         lua_Number x = lua_tonumber(L, -1);
410         lua_pop(L, 1);
411         lua_pushstring(L, "Y");
412         lua_gettable(L, -2);
413         lua_Number y = lua_tonumber(L, -1);
414         lua_pop(L, 1);
415         lua_pushstring(L, "Z");
416         lua_gettable(L, -2);
417         lua_Number z = lua_tonumber(L, -1);
418         lua_pop(L, 1);
419         lua_pop(L, 1);
420         // 1: env
421         ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
422         lua_pop(L, 1);
423         assert(env);
424         
425         v3f pos_f = v3f(x,y,z)*BS;
426         
427         Player *player = env->getNearestConnectedPlayer(pos_f);
428         
429         if(player)
430                 lua_pushstring(L, player->getName());
431         else
432                 lua_pushstring(L, "");
433         
434         return 1; // Number of return values
435 }
436
437 /*
438         {exists=, pos={X=,Y=,Z=}, connected=} env_get_player_info(env, name)
439 */
440 static int lf_env_get_player_info(lua_State *L)
441 {
442         // 2: name
443         const char *name = lua_tostring(L, -1);
444         lua_pop(L, 1);
445         // 1: env
446         ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
447         lua_pop(L, 1);
448         assert(env);
449         
450         Player *player = env->getPlayer(name);
451         v3f pos(0,0,0);
452         if(player)
453                 pos = player->getPosition();
454
455         lua_newtable(L);
456
457         lua_pushstring(L, "exists");
458         lua_pushboolean(L, (player != NULL));
459         lua_settable(L, -3);
460         
461         if(player != NULL)
462         {
463                 lua_pushstring(L, "pos");
464                 {
465                         lua_newtable(L);
466                         
467                         lua_pushstring(L, "X");
468                         lua_pushnumber(L, pos.X/BS);
469                         lua_settable(L, -3);
470
471                         lua_pushstring(L, "Y");
472                         lua_pushnumber(L, pos.Y/BS);
473                         lua_settable(L, -3);
474
475                         lua_pushstring(L, "Z");
476                         lua_pushnumber(L, pos.Z/BS);
477                         lua_settable(L, -3);
478                 }
479                 lua_settable(L, -3);
480
481                 lua_pushstring(L, "connected");
482                 lua_pushboolean(L, (player->peer_id != 0));
483                 lua_settable(L, -3);
484         }
485
486         return 1; // Number of return values
487 }
488
489 LuaSAO::LuaSAO(ServerEnvironment *env, u16 id, v3f pos):
490         ServerActiveObject(env, id, pos),
491         L(NULL)
492 {
493         dstream<<"LuaSAO::LuaSAO(): id="<<id<<std::endl;
494         L = lua_open();
495         assert(L);
496         
497         // Load libraries
498         luaopen_base(L);
499         luaopen_table(L);
500         luaopen_string(L);
501         luaopen_math(L);
502         
503         // Add globals
504         //lua_pushlightuserdata(L, this);
505         //lua_setglobal(L, "self");
506         
507         // Register functions
508 #define LUA_REGISTER_FUNC(L, x) lua_register(L, #x, lf_ ## x)
509         LUA_REGISTER_FUNC(L, object_remove);
510         LUA_REGISTER_FUNC(L, object_get_environment);
511         LUA_REGISTER_FUNC(L, object_set_base_position);
512         LUA_REGISTER_FUNC(L, object_get_base_position);
513         LUA_REGISTER_FUNC(L, object_add_message);
514         LUA_REGISTER_FUNC(L, env_get_node);
515         LUA_REGISTER_FUNC(L, get_content_features);
516         LUA_REGISTER_FUNC(L, env_dig_node);
517         LUA_REGISTER_FUNC(L, env_place_node);
518         LUA_REGISTER_FUNC(L, env_get_nearest_player_name);
519         LUA_REGISTER_FUNC(L, env_get_player_info);
520 }
521
522 LuaSAO::~LuaSAO()
523 {
524         lua_close(L);
525 }
526
527 std::string LuaSAO::getClientInitializationData()
528 {
529         /*
530                 Read client-side script from file
531         */
532         
533         std::string relative_path;
534         relative_path += "scripts/objects/";
535         relative_path += m_script_name;
536         relative_path += "/client.lua";
537         std::string full_path = porting::getDataPath(relative_path.c_str());
538         std::string script;
539         std::ifstream file(full_path.c_str(), std::ios::binary | std::ios::ate);
540         int size = file.tellg();
541         SharedBuffer<char> buf(size);
542         file.seekg(0, std::ios::beg);
543         file.read(&buf[0], size);
544         file.close();
545         
546         /*
547                 Create data string
548         */
549         std::string data;
550
551         // Append script
552         std::string script_string(&buf[0], buf.getSize());
553         data += serializeLongString(script_string);
554
555         /*
556                 Get data from server-side script for inclusion
557         */
558         std::string other_data;
559         
560         do{
561
562                 const char *funcname = "on_get_client_init_data";
563                 lua_getglobal(L, funcname);
564                 if(!lua_isfunction(L,-1))
565                 {
566                         lua_pop(L,1);
567                         dstream<<"WARNING: LuaSAO: Function not found: "
568                                         <<funcname<<std::endl;
569                         break;
570                 }
571                 
572                 // Parameters:
573                 // 1: self
574                 lua_pushlightuserdata(L, this);
575                 
576                 // Call (1 parameters, 1 result)
577                 if(lua_pcall(L, 1, 1, 0))
578                 {
579                         dstream<<"WARNING: LuaSAO: Error running function "
580                                         <<funcname<<": "
581                                         <<lua_tostring(L,-1)<<std::endl;
582                         break;
583                 }
584
585                 // Retrieve result
586                 if(!lua_isstring(L,-1))
587                 {
588                         dstream<<"WARNING: LuaSAO: Function "<<funcname
589                                         <<" didn't return a string"<<std::endl;
590                         break;
591                 }
592                 
593                 size_t cs_len = 0;
594                 const char *cs = lua_tolstring(L, -1, &cs_len);
595                 lua_pop(L,1);
596
597                 other_data = std::string(cs, cs_len);
598
599         }while(0);
600         
601         data += serializeLongString(other_data);
602
603         return data;
604 }
605
606 std::string LuaSAO::getServerInitializationData()
607 {
608         std::string data;
609         
610         // Script name
611         data.append(serializeString(m_script_name));
612
613         /*
614                 Get data from server-side script for inclusion
615         */
616         std::string other_data;
617         
618         do{
619
620                 const char *funcname = "on_get_server_init_data";
621                 lua_getglobal(L, funcname);
622                 if(!lua_isfunction(L,-1))
623                 {
624                         lua_pop(L,1);
625                         dstream<<"WARNING: LuaSAO: Function not found: "
626                                         <<funcname<<std::endl;
627                         break;
628                 }
629                 
630                 // Parameters:
631                 // 1: self
632                 lua_pushlightuserdata(L, this);
633                 
634                 // Call (1 parameters, 1 result)
635                 if(lua_pcall(L, 1, 1, 0))
636                 {
637                         dstream<<"WARNING: LuaSAO: Error running function "
638                                         <<funcname<<": "
639                                         <<lua_tostring(L,-1)<<std::endl;
640                         break;
641                 }
642
643                 // Retrieve result
644                 if(!lua_isstring(L,-1))
645                 {
646                         dstream<<"WARNING: LuaSAO: Function "<<funcname
647                                         <<" didn't return a string"<<std::endl;
648                         break;
649                 }
650                 
651                 size_t cs_len = 0;
652                 const char *cs = lua_tolstring(L, -1, &cs_len);
653                 lua_pop(L,1);
654
655                 other_data = std::string(cs, cs_len);
656
657         }while(0);
658         
659         data += serializeLongString(other_data);
660
661         return data;
662 }
663
664 void LuaSAO::initializeFromNothing(const std::string &script_name)
665 {
666         loadScripts(script_name);
667
668         /*
669                 Call on_initialize(self, data) in the script
670         */
671         
672         const char *funcname = "on_initialize";
673         lua_getglobal(L, funcname);
674         if(!lua_isfunction(L,-1))
675         {
676                 lua_pop(L,1);
677                 dstream<<"WARNING: LuaSAO: Function not found: "
678                                 <<funcname<<std::endl;
679                 return;
680         }
681         
682         // Parameters:
683         // 1: self
684         lua_pushlightuserdata(L, this);
685         // 2: data (other)
686         lua_pushstring(L, "");
687         
688         // Call (2 parameters, 0 result)
689         if(lua_pcall(L, 2, 0, 0))
690         {
691                 dstream<<"WARNING: LuaSAO: Error running function "
692                                 <<funcname<<": "
693                                 <<lua_tostring(L,-1)<<std::endl;
694                 return;
695         }
696 }
697
698 void LuaSAO::initializeFromSave(const std::string &data)
699 {
700         std::istringstream is(data, std::ios::binary);
701         std::string script_name = deSerializeString(is);
702         std::string other = deSerializeLongString(is);
703
704         loadScripts(script_name);
705
706         /*
707                 Call on_initialize(self, data) in the script
708         */
709         
710         const char *funcname = "on_initialize";
711         lua_getglobal(L, funcname);
712         if(!lua_isfunction(L,-1))
713         {
714                 lua_pop(L,1);
715                 dstream<<"WARNING: LuaSAO: Function not found: "
716                                 <<funcname<<std::endl;
717                 return;
718         }
719         
720         // Parameters:
721         // 1: self
722         lua_pushlightuserdata(L, this);
723         // 2: data (other)
724         lua_pushlstring(L, other.c_str(), other.size());
725         
726         // Call (2 parameters, 0 result)
727         if(lua_pcall(L, 2, 0, 0))
728         {
729                 dstream<<"WARNING: LuaSAO: Error running function "
730                                 <<funcname<<": "
731                                 <<lua_tostring(L,-1)<<std::endl;
732                 return;
733         }
734 }
735
736 void LuaSAO::loadScripts(const std::string &script_name)
737 {
738         m_script_name = script_name;
739         
740         std::string relative_path;
741         relative_path += "scripts/objects/";
742         relative_path += script_name;
743         std::string server_file = relative_path + "/server.lua";
744         std::string server_path = porting::getDataPath(server_file.c_str());
745
746         // Load the file
747         int ret;
748         ret = luaL_loadfile(L, server_path.c_str());
749         if(ret)
750         {
751                 const char *message = lua_tostring(L, -1);
752                 lua_pop(L, 1);
753                 dstream<<"LuaSAO::loadScript(): lua_loadfile failed: "
754                                 <<message<<std::endl;
755                 assert(0);
756                 return;
757         }
758         ret = lua_pcall(L, 0, 0, 0);
759         if(ret)
760         {
761                 const char *message = lua_tostring(L, -1);
762                 lua_pop(L, 1);
763                 dstream<<"LuaSAO::loadScript(): lua_pcall failed: "
764                                 <<message<<std::endl;
765                 assert(0);
766                 return;
767         }
768 }
769
770 void LuaSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
771 {
772         const char *funcname = "on_step";
773         lua_getglobal(L, funcname);
774         if(!lua_isfunction(L,-1))
775         {
776                 lua_pop(L,1);
777                 dstream<<"WARNING: LuaSAO::step(): Function not found: "
778                                 <<funcname<<std::endl;
779                 return;
780         }
781         
782         // Parameters:
783         // 1: self
784         lua_pushlightuserdata(L, this);
785         // 2: dtime
786         lua_pushnumber(L, dtime);
787         
788         // Call (2 parameters, 0 result)
789         if(lua_pcall(L, 2, 0, 0))
790         {
791                 dstream<<"WARNING: LuaSAO::step(): Error running function "
792                                 <<funcname<<": "
793                                 <<lua_tostring(L,-1)<<std::endl;
794                 return;
795         }
796
797         // Move messages
798         while(m_message_queue.size() != 0)
799         {
800                 messages.push_back(m_message_queue.pop_front());
801         }
802 }
803
804
805