8b41cef1684456d1861691439e87423e13c699cf
[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         Functions for calling from script:
92
93         object_set_base_position(self, x,y,z)
94         x,y,z = object_get_base_position(self)
95         object_add_message(self, data)
96         n = object_get_node(self, x,y,z)
97         object_remove(self)
98
99         Callbacks in script:
100         
101         step(self, dtime)
102         get_client_init_data(self)
103         get_server_init_data(self)
104         initialize(self, data)
105 */
106
107 /*
108         object_set_base_position(self, x, y, z)
109 */
110 static int lf_object_set_base_position(lua_State *L)
111 {
112         // 4: z
113         lua_Number z = lua_tonumber(L, -1);
114         lua_pop(L, 1);
115         // 3: y
116         lua_Number y = lua_tonumber(L, -1);
117         lua_pop(L, 1);
118         // 2: x
119         lua_Number x = lua_tonumber(L, -1);
120         lua_pop(L, 1);
121         // 1: self
122         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
123         lua_pop(L, 1);
124         
125         assert(self);
126         
127         self->setBasePosition(v3f(x*BS,y*BS,z*BS));
128         
129         return 0; // Number of return values
130 }
131
132 /*
133         object_get_base_position(self)
134 */
135 static int lf_object_get_base_position(lua_State *L)
136 {
137         // 1: self
138         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
139         lua_pop(L, 1);
140         
141         assert(self);
142         
143         v3f pos = self->getBasePosition();
144
145         lua_pushnumber(L, pos.X/BS);
146         lua_pushnumber(L, pos.Y/BS);
147         lua_pushnumber(L, pos.Z/BS);
148         return 3; // Number of return values
149 }
150
151 /*
152         object_add_message(self, string data)
153         lf = luafunc
154 */
155 static int lf_object_add_message(lua_State *L)
156 {
157         // 2: data
158         size_t datalen = 0;
159         const char *data_c = lua_tolstring(L, -1, &datalen);
160         lua_pop(L, 1);
161         // 1: self
162         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
163         lua_pop(L, 1);
164         
165         assert(self);
166         assert(data_c);
167         
168         std::string data(data_c, datalen);
169         //dstream<<"object_add_message: data="<<data<<std::endl;
170         
171         // Create message and add to queue
172         ActiveObjectMessage aom(self->getId());
173         aom.reliable = true;
174         aom.datastring = data;
175         self->m_message_queue.push_back(aom);
176
177         return 0; // Number of return values
178 }
179
180 /*
181         object_get_node(self, x,y,z)
182 */
183 static int lf_object_get_node(lua_State *L)
184 {
185         // 4: z
186         lua_Number z = lua_tonumber(L, -1);
187         lua_pop(L, 1);
188         // 3: y
189         lua_Number y = lua_tonumber(L, -1);
190         lua_pop(L, 1);
191         // 2: x
192         lua_Number x = lua_tonumber(L, -1);
193         lua_pop(L, 1);
194         // 1: self
195         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
196         lua_pop(L, 1);
197         
198         assert(self);
199
200         v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
201
202         /*dstream<<"Checking node from pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z
203                         <<")"<<std::endl;*/
204         
205         // Get the node
206         MapNode n(CONTENT_IGNORE);
207         n = self->getEnv()->getMap().getNodeNoEx(pos);
208
209         // Create a table with some data about the node
210         lua_newtable(L);
211         lua_pushstring(L, "content");
212         lua_pushinteger(L, n.d);
213         lua_settable(L, -3);
214         lua_pushstring(L, "param1");
215         lua_pushinteger(L, n.param);
216         lua_settable(L, -3);
217         lua_pushstring(L, "param2");
218         lua_pushinteger(L, n.param2);
219         lua_settable(L, -3);
220         lua_pushstring(L, "walkable");
221         lua_pushboolean(L, content_features(n.d).walkable);
222         lua_settable(L, -3);
223         
224         // Return the table
225         return 1;
226 }
227
228 #if 0
229 /*
230         object_set_node(self, x,y,z, n)
231 */
232 static int lf_object_set_node(lua_State *L)
233 {
234         MapNode n;
235
236         // 5: n
237         // Get fields of table
238
239         lua_pushinteger(L, "content");
240         lua_gettable(L, -2);
241         n.d = lua_tonumber(L, -1);
242         lua_pop(L, 1);
243
244         lua_pushinteger(L, "param1");
245         lua_gettable(L, -2);
246         n.param = lua_tonumber(L, -1);
247         lua_pop(L, 1);
248
249         lua_pushinteger(L, "param2");
250         lua_gettable(L, -2);
251         n.param2 = lua_tonumber(L, -1);
252         lua_pop(L, 1);
253
254         lua_pop(L, 1);
255         // 4: z
256         lua_Number z = lua_tonumber(L, -1);
257         lua_pop(L, 1);
258         // 3: y
259         lua_Number y = lua_tonumber(L, -1);
260         lua_pop(L, 1);
261         // 2: x
262         lua_Number x = lua_tonumber(L, -1);
263         lua_pop(L, 1);
264         // 1: self
265         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
266         lua_pop(L, 1);
267         
268         assert(self);
269
270         v3s16 pos = floatToInt(v3f(x,y,z), 1.0);
271
272         /*dstream<<"Checking node from pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z
273                         <<")"<<std::endl;*/
274         
275         // Get the node
276         MapNode n(CONTENT_IGNORE);
277         n = self->getEnv()->getMap().getNodeNoEx(pos);
278
279         // Create a table with some data about the node
280         lua_newtable(L);
281         lua_pushstring(L, "content");
282         lua_pushinteger(L, n.d);
283         lua_settable(L, -3);
284         lua_pushstring(L, "walkable");
285         lua_pushboolean(L, content_features(n.d).walkable);
286         lua_settable(L, -3);
287         
288         // Return the table
289         return 1;
290 }
291 #endif
292
293 /*
294         object_remove(x,y,z)
295 */
296 static int lf_object_remove(lua_State *L)
297 {
298         // 1: self
299         LuaSAO *self = (LuaSAO*)lua_touserdata(L, -1);
300         lua_pop(L, 1);
301         
302         assert(self);
303
304         self->m_removed = true;
305
306         return 0;
307 }
308
309 LuaSAO::LuaSAO(ServerEnvironment *env, u16 id, v3f pos):
310         ServerActiveObject(env, id, pos),
311         L(NULL)
312 {
313         dstream<<"LuaSAO::LuaSAO(): id="<<id<<std::endl;
314         L = lua_open();
315         assert(L);
316         
317         // Load libraries
318         luaopen_base(L);
319         luaopen_table(L);
320         luaopen_string(L);
321         luaopen_math(L);
322         
323         // Add globals
324         //lua_pushlightuserdata(L, this);
325         //lua_setglobal(L, "self");
326         
327         // Register functions
328         lua_register(L, "object_set_base_position", lf_object_set_base_position);
329         lua_register(L, "object_get_base_position", lf_object_get_base_position);
330         lua_register(L, "object_add_message", lf_object_add_message);
331         lua_register(L, "object_get_node", lf_object_get_node);
332         lua_register(L, "object_remove", lf_object_remove);
333 }
334
335 LuaSAO::~LuaSAO()
336 {
337         lua_close(L);
338 }
339
340 std::string LuaSAO::getClientInitializationData()
341 {
342         /*
343                 Read client-side script from file
344         */
345         
346         std::string relative_path;
347         relative_path += "luaobjects/";
348         relative_path += m_script_name;
349         relative_path += "/client.lua";
350         std::string full_path = porting::getDataPath(relative_path.c_str());
351         std::string script;
352         std::ifstream file(full_path.c_str(), std::ios::binary | std::ios::ate);
353         int size = file.tellg();
354         SharedBuffer<char> buf(size);
355         file.seekg(0, std::ios::beg);
356         file.read(&buf[0], size);
357         file.close();
358         
359         /*
360                 Create data string
361         */
362         std::string data;
363
364         // Append script
365         std::string script_string(&buf[0], buf.getSize());
366         data += serializeLongString(script_string);
367
368         /*
369                 Get data from server-side script for inclusion
370         */
371         std::string other_data;
372         
373         do{
374
375                 const char *funcname = "get_client_init_data";
376                 lua_getglobal(L, funcname);
377                 if(!lua_isfunction(L,-1))
378                 {
379                         lua_pop(L,1);
380                         dstream<<"WARNING: LuaSAO: Function not found: "
381                                         <<funcname<<std::endl;
382                         break;
383                 }
384                 
385                 // Parameters:
386                 // 1: self
387                 lua_pushlightuserdata(L, this);
388                 
389                 // Call (1 parameters, 1 result)
390                 if(lua_pcall(L, 1, 1, 0))
391                 {
392                         dstream<<"WARNING: LuaSAO: Error running function "
393                                         <<funcname<<": "
394                                         <<lua_tostring(L,-1)<<std::endl;
395                         break;
396                 }
397
398                 // Retrieve result
399                 if(!lua_isstring(L,-1))
400                 {
401                         dstream<<"WARNING: LuaSAO: Function "<<funcname
402                                         <<" didn't return a string"<<std::endl;
403                         break;
404                 }
405                 
406                 size_t cs_len = 0;
407                 const char *cs = lua_tolstring(L, -1, &cs_len);
408                 lua_pop(L,1);
409
410                 other_data = std::string(cs, cs_len);
411
412         }while(0);
413         
414         data += serializeLongString(other_data);
415
416         return data;
417 }
418
419 std::string LuaSAO::getServerInitializationData()
420 {
421         std::string data;
422         
423         // Script name
424         data.append(serializeString(m_script_name));
425
426         /*
427                 Get data from server-side script for inclusion
428         */
429         std::string other_data;
430         
431         do{
432
433                 const char *funcname = "get_server_init_data";
434                 lua_getglobal(L, funcname);
435                 if(!lua_isfunction(L,-1))
436                 {
437                         lua_pop(L,1);
438                         dstream<<"WARNING: LuaSAO: Function not found: "
439                                         <<funcname<<std::endl;
440                         break;
441                 }
442                 
443                 // Parameters:
444                 // 1: self
445                 lua_pushlightuserdata(L, this);
446                 
447                 // Call (1 parameters, 1 result)
448                 if(lua_pcall(L, 1, 1, 0))
449                 {
450                         dstream<<"WARNING: LuaSAO: Error running function "
451                                         <<funcname<<": "
452                                         <<lua_tostring(L,-1)<<std::endl;
453                         break;
454                 }
455
456                 // Retrieve result
457                 if(!lua_isstring(L,-1))
458                 {
459                         dstream<<"WARNING: LuaSAO: Function "<<funcname
460                                         <<" didn't return a string"<<std::endl;
461                         break;
462                 }
463                 
464                 size_t cs_len = 0;
465                 const char *cs = lua_tolstring(L, -1, &cs_len);
466                 lua_pop(L,1);
467
468                 other_data = std::string(cs, cs_len);
469
470         }while(0);
471         
472         data += serializeLongString(other_data);
473
474         return data;
475 }
476
477 void LuaSAO::initialize(const std::string &data)
478 {
479         std::istringstream is(data, std::ios::binary);
480         std::string script_name = deSerializeString(is);
481         std::string other = deSerializeLongString(is);
482
483         loadScripts(script_name);
484
485         /*
486                 Call initialize(self, data) in the script
487         */
488         
489         const char *funcname = "initialize";
490         lua_getglobal(L, funcname);
491         if(!lua_isfunction(L,-1))
492         {
493                 lua_pop(L,1);
494                 dstream<<"WARNING: LuaSAO: Function not found: "
495                                 <<funcname<<std::endl;
496                 return;
497         }
498         
499         // Parameters:
500         // 1: self
501         lua_pushlightuserdata(L, this);
502         // 2: data (other)
503         lua_pushlstring(L, other.c_str(), other.size());
504         
505         // Call (2 parameters, 0 result)
506         if(lua_pcall(L, 2, 0, 0))
507         {
508                 dstream<<"WARNING: LuaSAO: Error running function "
509                                 <<funcname<<": "
510                                 <<lua_tostring(L,-1)<<std::endl;
511                 return;
512         }
513 }
514
515 void LuaSAO::loadScripts(const std::string &script_name)
516 {
517         m_script_name = script_name;
518         
519         std::string relative_path;
520         relative_path += "luaobjects/";
521         relative_path += script_name;
522         std::string server_file = relative_path + "/server.lua";
523         std::string server_path = porting::getDataPath(server_file.c_str());
524
525         // Load the file
526         int ret;
527         ret = luaL_loadfile(L, server_path.c_str());
528         if(ret)
529         {
530                 const char *message = lua_tostring(L, -1);
531                 lua_pop(L, 1);
532                 dstream<<"LuaSAO::loadScript(): lua_loadfile failed: "
533                                 <<message<<std::endl;
534                 assert(0);
535                 return;
536         }
537         ret = lua_pcall(L, 0, 0, 0);
538         if(ret)
539         {
540                 const char *message = lua_tostring(L, -1);
541                 lua_pop(L, 1);
542                 dstream<<"LuaSAO::loadScript(): lua_pcall failed: "
543                                 <<message<<std::endl;
544                 assert(0);
545                 return;
546         }
547 }
548
549 void LuaSAO::step(float dtime, Queue<ActiveObjectMessage> &messages)
550 {
551         lua_getglobal(L, "step");
552         if(!lua_isfunction(L,-1))
553         {
554                 lua_pop(L,1);
555                 dstream<<"WARNING: LuaSAO::step(): step function not found"<<std::endl;
556                 return;
557         }
558         
559         // Parameters:
560         // 1: self
561         lua_pushlightuserdata(L, this);
562         // 2: dtime
563         lua_pushnumber(L, dtime);
564         
565         // Call (2 parameters, 0 result)
566         if(lua_pcall(L, 2, 0, 0))
567         {
568                 dstream<<"WARNING: LuaSAO::step(): Error running function step(): "
569                                 <<lua_tostring(L,-1)<<std::endl;
570                 return;
571         }
572
573         // Move messages
574         while(m_message_queue.size() != 0)
575         {
576                 messages.push_back(m_message_queue.pop_front());
577         }
578 }
579
580
581