]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_server.cpp
DevTest: Fix broken PNG textures
[dragonfireclient.git] / src / script / lua_api / l_server.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "lua_api/l_server.h"
21 #include "lua_api/l_internal.h"
22 #include "common/c_converter.h"
23 #include "common/c_content.h"
24 #include "common/c_packer.h"
25 #include "cpp_api/s_base.h"
26 #include "cpp_api/s_security.h"
27 #include "scripting_server.h"
28 #include "server.h"
29 #include "environment.h"
30 #include "remoteplayer.h"
31 #include "log.h"
32 #include <algorithm>
33
34 // request_shutdown()
35 int ModApiServer::l_request_shutdown(lua_State *L)
36 {
37         NO_MAP_LOCK_REQUIRED;
38         const char *msg = lua_tolstring(L, 1, NULL);
39         bool reconnect = readParam<bool>(L, 2);
40         float seconds_before_shutdown = lua_tonumber(L, 3);
41         getServer(L)->requestShutdown(msg ? msg : "", reconnect, seconds_before_shutdown);
42         return 0;
43 }
44
45 // get_server_status()
46 int ModApiServer::l_get_server_status(lua_State *L)
47 {
48         NO_MAP_LOCK_REQUIRED;
49         lua_pushstring(L, getServer(L)->getStatusString().c_str());
50         return 1;
51 }
52
53 // get_server_uptime()
54 int ModApiServer::l_get_server_uptime(lua_State *L)
55 {
56         NO_MAP_LOCK_REQUIRED;
57         lua_pushnumber(L, getServer(L)->getUptime());
58         return 1;
59 }
60
61 // get_server_max_lag()
62 int ModApiServer::l_get_server_max_lag(lua_State *L)
63 {
64         NO_MAP_LOCK_REQUIRED;
65         GET_ENV_PTR;
66         lua_pushnumber(L, env->getMaxLagEstimate());
67         return 1;
68 }
69
70 // print(text)
71 int ModApiServer::l_print(lua_State *L)
72 {
73         NO_MAP_LOCK_REQUIRED;
74         std::string text;
75         text = luaL_checkstring(L, 1);
76         getServer(L)->printToConsoleOnly(text);
77         return 0;
78 }
79
80 // chat_send_all(text)
81 int ModApiServer::l_chat_send_all(lua_State *L)
82 {
83         NO_MAP_LOCK_REQUIRED;
84         const char *text = luaL_checkstring(L, 1);
85         // Get server from registry
86         Server *server = getServer(L);
87         // Send
88         server->notifyPlayers(utf8_to_wide(text));
89         return 0;
90 }
91
92 // chat_send_player(name, text)
93 int ModApiServer::l_chat_send_player(lua_State *L)
94 {
95         NO_MAP_LOCK_REQUIRED;
96         const char *name = luaL_checkstring(L, 1);
97         const char *text = luaL_checkstring(L, 2);
98
99         // Get server from registry
100         Server *server = getServer(L);
101         // Send
102         server->notifyPlayer(name, utf8_to_wide(text));
103         return 0;
104 }
105
106 // get_player_privs(name, text)
107 int ModApiServer::l_get_player_privs(lua_State *L)
108 {
109         NO_MAP_LOCK_REQUIRED;
110         const char *name = luaL_checkstring(L, 1);
111         // Get server from registry
112         Server *server = getServer(L);
113         // Do it
114         lua_newtable(L);
115         int table = lua_gettop(L);
116         std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
117         for (const std::string &privs_ : privs_s) {
118                 lua_pushboolean(L, true);
119                 lua_setfield(L, table, privs_.c_str());
120         }
121         lua_pushvalue(L, table);
122         return 1;
123 }
124
125 // get_player_ip()
126 int ModApiServer::l_get_player_ip(lua_State *L)
127 {
128         NO_MAP_LOCK_REQUIRED;
129
130         Server *server = getServer(L);
131
132         const char *name = luaL_checkstring(L, 1);
133         RemotePlayer *player = server->getEnv().getPlayer(name);
134         if (!player) {
135                 lua_pushnil(L); // no such player
136                 return 1;
137         }
138
139         lua_pushstring(L, server->getPeerAddress(player->getPeerId()).serializeString().c_str());
140         return 1;
141 }
142
143 // get_player_information(name)
144 int ModApiServer::l_get_player_information(lua_State *L)
145 {
146         NO_MAP_LOCK_REQUIRED;
147
148         Server *server = getServer(L);
149
150         const char *name = luaL_checkstring(L, 1);
151         RemotePlayer *player = server->getEnv().getPlayer(name);
152         if (!player) {
153                 lua_pushnil(L); // no such player
154                 return 1;
155         }
156
157         /*
158                 Be careful not to introduce a depdendency on the connection to
159                 the peer here. This function is >>REQUIRED<< to still be able to return
160                 values even when the peer unexpectedly disappears.
161                 Hence all the ConInfo values here are optional.
162         */
163
164         auto getConInfo = [&] (con::rtt_stat_type type, float *value) -> bool {
165                 return server->getClientConInfo(player->getPeerId(), type, value);
166         };
167
168         float min_rtt, max_rtt, avg_rtt, min_jitter, max_jitter, avg_jitter;
169         bool have_con_info =
170                 getConInfo(con::MIN_RTT, &min_rtt) &&
171                 getConInfo(con::MAX_RTT, &max_rtt) &&
172                 getConInfo(con::AVG_RTT, &avg_rtt) &&
173                 getConInfo(con::MIN_JITTER, &min_jitter) &&
174                 getConInfo(con::MAX_JITTER, &max_jitter) &&
175                 getConInfo(con::AVG_JITTER, &avg_jitter);
176
177         ClientInfo info;
178         if (!server->getClientInfo(player->getPeerId(), info)) {
179                 warningstream << FUNCTION_NAME << ": no client info?!" << std::endl;
180                 lua_pushnil(L); // error
181                 return 1;
182         }
183
184         lua_newtable(L);
185         int table = lua_gettop(L);
186
187         lua_pushstring(L,"address");
188         lua_pushstring(L, info.addr.serializeString().c_str());
189         lua_settable(L, table);
190
191         lua_pushstring(L,"ip_version");
192         if (info.addr.getFamily() == AF_INET) {
193                 lua_pushnumber(L, 4);
194         } else if (info.addr.getFamily() == AF_INET6) {
195                 lua_pushnumber(L, 6);
196         } else {
197                 lua_pushnumber(L, 0);
198         }
199         lua_settable(L, table);
200
201         if (have_con_info) { // may be missing
202                 lua_pushstring(L, "min_rtt");
203                 lua_pushnumber(L, min_rtt);
204                 lua_settable(L, table);
205
206                 lua_pushstring(L, "max_rtt");
207                 lua_pushnumber(L, max_rtt);
208                 lua_settable(L, table);
209
210                 lua_pushstring(L, "avg_rtt");
211                 lua_pushnumber(L, avg_rtt);
212                 lua_settable(L, table);
213
214                 lua_pushstring(L, "min_jitter");
215                 lua_pushnumber(L, min_jitter);
216                 lua_settable(L, table);
217
218                 lua_pushstring(L, "max_jitter");
219                 lua_pushnumber(L, max_jitter);
220                 lua_settable(L, table);
221
222                 lua_pushstring(L, "avg_jitter");
223                 lua_pushnumber(L, avg_jitter);
224                 lua_settable(L, table);
225         }
226
227         lua_pushstring(L,"connection_uptime");
228         lua_pushnumber(L, info.uptime);
229         lua_settable(L, table);
230
231         lua_pushstring(L,"protocol_version");
232         lua_pushnumber(L, info.prot_vers);
233         lua_settable(L, table);
234
235         lua_pushstring(L, "formspec_version");
236         lua_pushnumber(L, player->formspec_version);
237         lua_settable(L, table);
238
239         lua_pushstring(L, "lang_code");
240         lua_pushstring(L, info.lang_code.c_str());
241         lua_settable(L, table);
242
243 #ifndef NDEBUG
244         lua_pushstring(L,"serialization_version");
245         lua_pushnumber(L, info.ser_vers);
246         lua_settable(L, table);
247
248         lua_pushstring(L,"major");
249         lua_pushnumber(L, info.major);
250         lua_settable(L, table);
251
252         lua_pushstring(L,"minor");
253         lua_pushnumber(L, info.minor);
254         lua_settable(L, table);
255
256         lua_pushstring(L,"patch");
257         lua_pushnumber(L, info.patch);
258         lua_settable(L, table);
259
260         lua_pushstring(L,"version_string");
261         lua_pushstring(L, info.vers_string.c_str());
262         lua_settable(L, table);
263
264         lua_pushstring(L,"state");
265         lua_pushstring(L, ClientInterface::state2Name(info.state).c_str());
266         lua_settable(L, table);
267 #endif
268
269         return 1;
270 }
271
272 // get_ban_list()
273 int ModApiServer::l_get_ban_list(lua_State *L)
274 {
275         NO_MAP_LOCK_REQUIRED;
276         lua_pushstring(L, getServer(L)->getBanDescription("").c_str());
277         return 1;
278 }
279
280 // get_ban_description()
281 int ModApiServer::l_get_ban_description(lua_State *L)
282 {
283         NO_MAP_LOCK_REQUIRED;
284         const char * ip_or_name = luaL_checkstring(L, 1);
285         lua_pushstring(L, getServer(L)->getBanDescription(std::string(ip_or_name)).c_str());
286         return 1;
287 }
288
289 // ban_player()
290 int ModApiServer::l_ban_player(lua_State *L)
291 {
292         NO_MAP_LOCK_REQUIRED;
293
294         if (!getEnv(L))
295                 throw LuaError("Can't ban player before server has started up");
296
297         Server *server = getServer(L);
298         const char *name = luaL_checkstring(L, 1);
299         RemotePlayer *player = server->getEnv().getPlayer(name);
300         if (!player) {
301                 lua_pushboolean(L, false); // no such player
302                 return 1;
303         }
304
305         std::string ip_str = server->getPeerAddress(player->getPeerId()).serializeString();
306         server->setIpBanned(ip_str, name);
307         lua_pushboolean(L, true);
308         return 1;
309 }
310
311 // disconnect_player(name, [reason]) -> success
312 int ModApiServer::l_disconnect_player(lua_State *L)
313 {
314         NO_MAP_LOCK_REQUIRED;
315
316         if (!getEnv(L))
317                 throw LuaError("Can't kick player before server has started up");
318
319         const char *name = luaL_checkstring(L, 1);
320         std::string message;
321         if (lua_isstring(L, 2))
322                 message.append(readParam<std::string>(L, 2));
323         else
324                 message.append("Disconnected.");
325
326         Server *server = getServer(L);
327
328         RemotePlayer *player = server->getEnv().getPlayer(name);
329         if (!player) {
330                 lua_pushboolean(L, false); // No such player
331                 return 1;
332         }
333
334         server->DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING, message);
335         lua_pushboolean(L, true);
336         return 1;
337 }
338
339 int ModApiServer::l_remove_player(lua_State *L)
340 {
341         NO_MAP_LOCK_REQUIRED;
342         std::string name = luaL_checkstring(L, 1);
343         ServerEnvironment *s_env = dynamic_cast<ServerEnvironment *>(getEnv(L));
344         if (!s_env)
345                 throw LuaError("Can't remove player before server has started up");
346
347         RemotePlayer *player = s_env->getPlayer(name.c_str());
348         if (!player)
349                 lua_pushinteger(L, s_env->removePlayerFromDatabase(name) ? 0 : 1);
350         else
351                 lua_pushinteger(L, 2);
352
353         return 1;
354 }
355
356 // unban_player_or_ip()
357 int ModApiServer::l_unban_player_or_ip(lua_State *L)
358 {
359         NO_MAP_LOCK_REQUIRED;
360         const char * ip_or_name = luaL_checkstring(L, 1);
361         getServer(L)->unsetIpBanned(ip_or_name);
362         lua_pushboolean(L, true);
363         return 1;
364 }
365
366 // show_formspec(playername,formname,formspec)
367 int ModApiServer::l_show_formspec(lua_State *L)
368 {
369         NO_MAP_LOCK_REQUIRED;
370         const char *playername = luaL_checkstring(L, 1);
371         const char *formname = luaL_checkstring(L, 2);
372         const char *formspec = luaL_checkstring(L, 3);
373
374         if(getServer(L)->showFormspec(playername,formspec,formname))
375         {
376                 lua_pushboolean(L, true);
377         }else{
378                 lua_pushboolean(L, false);
379         }
380         return 1;
381 }
382
383 // get_current_modname()
384 int ModApiServer::l_get_current_modname(lua_State *L)
385 {
386         NO_MAP_LOCK_REQUIRED;
387         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
388         return 1;
389 }
390
391 // get_modpath(modname)
392 int ModApiServer::l_get_modpath(lua_State *L)
393 {
394         NO_MAP_LOCK_REQUIRED;
395         std::string modname = luaL_checkstring(L, 1);
396         const ModSpec *mod = getGameDef(L)->getModSpec(modname);
397         if (!mod)
398                 lua_pushnil(L);
399         else
400                 lua_pushstring(L, mod->path.c_str());
401         return 1;
402 }
403
404 // get_modnames()
405 // the returned list is sorted alphabetically for you
406 int ModApiServer::l_get_modnames(lua_State *L)
407 {
408         NO_MAP_LOCK_REQUIRED;
409
410         // Get a list of mods
411         std::vector<std::string> modlist;
412         for (auto &it : getGameDef(L)->getMods())
413                 modlist.emplace_back(it.name);
414
415         std::sort(modlist.begin(), modlist.end());
416
417         // Package them up for Lua
418         lua_createtable(L, modlist.size(), 0);
419         auto iter = modlist.begin();
420         for (u16 i = 0; iter != modlist.end(); ++iter) {
421                 lua_pushstring(L, iter->c_str());
422                 lua_rawseti(L, -2, ++i);
423         }
424         return 1;
425 }
426
427 // get_worldpath()
428 int ModApiServer::l_get_worldpath(lua_State *L)
429 {
430         NO_MAP_LOCK_REQUIRED;
431         const Server *srv = getServer(L);
432         lua_pushstring(L, srv->getWorldPath().c_str());
433         return 1;
434 }
435
436 // sound_play(spec, parameters, [ephemeral])
437 int ModApiServer::l_sound_play(lua_State *L)
438 {
439         NO_MAP_LOCK_REQUIRED;
440         SimpleSoundSpec spec;
441         read_soundspec(L, 1, spec);
442         ServerSoundParams params;
443         read_server_sound_params(L, 2, params);
444         bool ephemeral = lua_gettop(L) > 2 && readParam<bool>(L, 3);
445         if (ephemeral) {
446                 getServer(L)->playSound(spec, params, true);
447                 lua_pushnil(L);
448         } else {
449                 s32 handle = getServer(L)->playSound(spec, params);
450                 lua_pushinteger(L, handle);
451         }
452         return 1;
453 }
454
455 // sound_stop(handle)
456 int ModApiServer::l_sound_stop(lua_State *L)
457 {
458         NO_MAP_LOCK_REQUIRED;
459         s32 handle = luaL_checkinteger(L, 1);
460         getServer(L)->stopSound(handle);
461         return 0;
462 }
463
464 int ModApiServer::l_sound_fade(lua_State *L)
465 {
466         NO_MAP_LOCK_REQUIRED;
467         s32 handle = luaL_checkinteger(L, 1);
468         float step = readParam<float>(L, 2);
469         float gain = readParam<float>(L, 3);
470         getServer(L)->fadeSound(handle, step, gain);
471         return 0;
472 }
473
474 // dynamic_add_media(filepath)
475 int ModApiServer::l_dynamic_add_media(lua_State *L)
476 {
477         NO_MAP_LOCK_REQUIRED;
478
479         if (!getEnv(L))
480                 throw LuaError("Dynamic media cannot be added before server has started up");
481         Server *server = getServer(L);
482
483         std::string filepath;
484         std::string to_player;
485         bool ephemeral = false;
486
487         if (lua_istable(L, 1)) {
488                 getstringfield(L, 1, "filepath", filepath);
489                 getstringfield(L, 1, "to_player", to_player);
490                 getboolfield(L, 1, "ephemeral", ephemeral);
491         } else {
492                 filepath = readParam<std::string>(L, 1);
493         }
494         if (filepath.empty())
495                 luaL_typerror(L, 1, "non-empty string");
496         luaL_checktype(L, 2, LUA_TFUNCTION);
497
498         CHECK_SECURE_PATH(L, filepath.c_str(), false);
499
500         u32 token = server->getScriptIface()->allocateDynamicMediaCallback(L, 2);
501
502         bool ok = server->dynamicAddMedia(filepath, token, to_player, ephemeral);
503         if (!ok)
504                 server->getScriptIface()->freeDynamicMediaCallback(token);
505         lua_pushboolean(L, ok);
506
507         return 1;
508 }
509
510 // is_singleplayer()
511 int ModApiServer::l_is_singleplayer(lua_State *L)
512 {
513         NO_MAP_LOCK_REQUIRED;
514         const Server *srv = getServer(L);
515         lua_pushboolean(L, srv->isSingleplayer());
516         return 1;
517 }
518
519 // notify_authentication_modified(name)
520 int ModApiServer::l_notify_authentication_modified(lua_State *L)
521 {
522         NO_MAP_LOCK_REQUIRED;
523         std::string name;
524         if(lua_isstring(L, 1))
525                 name = readParam<std::string>(L, 1);
526         getServer(L)->reportPrivsModified(name);
527         return 0;
528 }
529
530 // do_async_callback(func, params, mod_origin)
531 int ModApiServer::l_do_async_callback(lua_State *L)
532 {
533         NO_MAP_LOCK_REQUIRED;
534         ServerScripting *script = getScriptApi<ServerScripting>(L);
535
536         luaL_checktype(L, 1, LUA_TFUNCTION);
537         luaL_checktype(L, 2, LUA_TTABLE);
538         luaL_checktype(L, 3, LUA_TSTRING);
539
540         call_string_dump(L, 1);
541         size_t func_length;
542         const char *serialized_func_raw = lua_tolstring(L, -1, &func_length);
543
544         PackedValue *param = script_pack(L, 2);
545
546         std::string mod_origin = readParam<std::string>(L, 3);
547
548         u32 jobId = script->queueAsync(
549                 std::string(serialized_func_raw, func_length),
550                 param, mod_origin);
551
552         lua_settop(L, 0);
553         lua_pushinteger(L, jobId);
554         return 1;
555 }
556
557 // register_async_dofile(path)
558 int ModApiServer::l_register_async_dofile(lua_State *L)
559 {
560         NO_MAP_LOCK_REQUIRED;
561
562         std::string path = readParam<std::string>(L, 1);
563         CHECK_SECURE_PATH(L, path.c_str(), false);
564
565         // Find currently running mod name (only at init time)
566         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
567         if (!lua_isstring(L, -1))
568                 return 0;
569         std::string modname = readParam<std::string>(L, -1);
570
571         getServer(L)->m_async_init_files.emplace_back(modname, path);
572         lua_pushboolean(L, true);
573         return 1;
574 }
575
576 // serialize_roundtrip(value)
577 // Meant for unit testing the packer from Lua
578 int ModApiServer::l_serialize_roundtrip(lua_State *L)
579 {
580         NO_MAP_LOCK_REQUIRED;
581
582         int top = lua_gettop(L);
583         auto *pv = script_pack(L, 1);
584         if (top != lua_gettop(L))
585                 throw LuaError("stack values leaked");
586
587 #ifndef NDEBUG
588         script_dump_packed(pv);
589 #endif
590
591         top = lua_gettop(L);
592         script_unpack(L, pv);
593         delete pv;
594         if (top + 1 != lua_gettop(L))
595                 throw LuaError("stack values leaked");
596
597         return 1;
598 }
599
600 void ModApiServer::Initialize(lua_State *L, int top)
601 {
602         API_FCT(request_shutdown);
603         API_FCT(get_server_status);
604         API_FCT(get_server_uptime);
605         API_FCT(get_server_max_lag);
606         API_FCT(get_worldpath);
607         API_FCT(is_singleplayer);
608
609         API_FCT(get_current_modname);
610         API_FCT(get_modpath);
611         API_FCT(get_modnames);
612
613         API_FCT(print);
614
615         API_FCT(chat_send_all);
616         API_FCT(chat_send_player);
617         API_FCT(show_formspec);
618         API_FCT(sound_play);
619         API_FCT(sound_stop);
620         API_FCT(sound_fade);
621         API_FCT(dynamic_add_media);
622
623         API_FCT(get_player_information);
624         API_FCT(get_player_privs);
625         API_FCT(get_player_ip);
626         API_FCT(get_ban_list);
627         API_FCT(get_ban_description);
628         API_FCT(ban_player);
629         API_FCT(disconnect_player);
630         API_FCT(remove_player);
631         API_FCT(unban_player_or_ip);
632         API_FCT(notify_authentication_modified);
633
634         API_FCT(do_async_callback);
635         API_FCT(register_async_dofile);
636         API_FCT(serialize_roundtrip);
637 }
638
639 void ModApiServer::InitializeAsync(lua_State *L, int top)
640 {
641         API_FCT(get_worldpath);
642         API_FCT(is_singleplayer);
643
644         API_FCT(get_current_modname);
645         API_FCT(get_modpath);
646         API_FCT(get_modnames);
647 }