]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_client.cpp
Add spider
[dragonfireclient.git] / src / script / lua_api / l_client.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "l_client.h"
22 #include "chatmessage.h"
23 #include "client/client.h"
24 #include "client/clientevent.h"
25 #include "client/sound.h"
26 #include "client/clientenvironment.h"
27 #include "client/game.h"
28 #include "common/c_content.h"
29 #include "common/c_converter.h"
30 #include "cpp_api/s_base.h"
31 #include "gettext.h"
32 #include "l_internal.h"
33 #include "l_clientobject.h"
34 #include "lua_api/l_nodemeta.h"
35 #include "gui/mainmenumanager.h"
36 #include "map.h"
37 #include "util/string.h"
38 #include "nodedef.h"
39 #include "client/keycode.h"
40
41 #define checkCSMRestrictionFlag(flag) \
42         ( getClient(L)->checkCSMRestrictionFlag(CSMRestrictionFlags::flag) )
43
44 // Not the same as FlagDesc, which contains an `u32 flag`
45 struct CSMFlagDesc {
46         const char *name;
47         u64 flag;
48 };
49
50 /*
51         FIXME: This should eventually be moved somewhere else
52         It also needs to be kept in sync with the definition of CSMRestrictionFlags
53         in network/networkprotocol.h
54 */
55 const static CSMFlagDesc flagdesc_csm_restriction[] = {
56         {"load_client_mods",  CSM_RF_LOAD_CLIENT_MODS},
57         {"chat_messages",     CSM_RF_CHAT_MESSAGES},
58         {"read_itemdefs",     CSM_RF_READ_ITEMDEFS},
59         {"read_nodedefs",     CSM_RF_READ_NODEDEFS},
60         {"lookup_nodes",      CSM_RF_LOOKUP_NODES},
61         {"read_playerinfo",   CSM_RF_READ_PLAYERINFO},
62         {NULL,      0}
63 };
64
65 // get_current_modname()
66 int ModApiClient::l_get_current_modname(lua_State *L)
67 {
68         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
69         return 1;
70 }
71
72 // get_modpath(modname)
73 int ModApiClient::l_get_modpath(lua_State *L)
74 {
75         std::string modname = readParam<std::string>(L, 1);
76         // Client mods use a virtual filesystem, see Client::scanModSubfolder()
77         std::string path = modname + ":";
78         lua_pushstring(L, path.c_str());
79         return 1;
80 }
81
82 // get_last_run_mod()
83 int ModApiClient::l_get_last_run_mod(lua_State *L)
84 {
85         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
86         std::string current_mod = readParam<std::string>(L, -1, "");
87         if (current_mod.empty()) {
88                 lua_pop(L, 1);
89                 lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str());
90         }
91         return 1;
92 }
93
94 // set_last_run_mod(modname)
95 int ModApiClient::l_set_last_run_mod(lua_State *L)
96 {
97         if (!lua_isstring(L, 1))
98                 return 0;
99
100         const char *mod = lua_tostring(L, 1);
101         getScriptApiBase(L)->setOriginDirect(mod);
102         lua_pushboolean(L, true);
103         return 1;
104 }
105
106 // print(text)
107 int ModApiClient::l_print(lua_State *L)
108 {
109         NO_MAP_LOCK_REQUIRED;
110         std::string text = luaL_checkstring(L, 1);
111         rawstream << text << std::endl;
112         return 0;
113 }
114
115 // display_chat_message(message)
116 int ModApiClient::l_display_chat_message(lua_State *L)
117 {
118         if (!lua_isstring(L, 1))
119                 return 0;
120
121         std::string message = luaL_checkstring(L, 1);
122         getClient(L)->pushToChatQueue(new ChatMessage(utf8_to_wide(message)));
123         lua_pushboolean(L, true);
124         return 1;
125 }
126
127 // send_chat_message(message)
128 int ModApiClient::l_send_chat_message(lua_State *L)
129 {
130         if (!lua_isstring(L, 1))
131                 return 0;
132
133         // If server disabled this API, discard
134
135         if (checkCSMRestrictionFlag(CSM_RF_CHAT_MESSAGES))
136                 return 0;
137
138         std::string message = luaL_checkstring(L, 1);
139         getClient(L)->sendChatMessage(utf8_to_wide(message));
140         return 0;
141 }
142
143 // clear_out_chat_queue()
144 int ModApiClient::l_clear_out_chat_queue(lua_State *L)
145 {
146         getClient(L)->clearOutChatQueue();
147         return 0;
148 }
149
150 // get_player_names()
151 int ModApiClient::l_get_player_names(lua_State *L)
152 {
153         if (checkCSMRestrictionFlag(CSM_RF_READ_PLAYERINFO))
154                 return 0;
155
156         const std::list<std::string> &plist = getClient(L)->getConnectedPlayerNames();
157         lua_createtable(L, plist.size(), 0);
158         int newTable = lua_gettop(L);
159         int index = 1;
160         std::list<std::string>::const_iterator iter;
161         for (iter = plist.begin(); iter != plist.end(); ++iter) {
162                 lua_pushstring(L, (*iter).c_str());
163                 lua_rawseti(L, newTable, index);
164                 index++;
165         }
166         return 1;
167 }
168
169 // show_formspec(formspec)
170 int ModApiClient::l_show_formspec(lua_State *L)
171 {
172         if (!lua_isstring(L, 1) || !lua_isstring(L, 2))
173                 return 0;
174
175         ClientEvent *event = new ClientEvent();
176         event->type = CE_SHOW_LOCAL_FORMSPEC;
177         event->show_formspec.formname = new std::string(luaL_checkstring(L, 1));
178         event->show_formspec.formspec = new std::string(luaL_checkstring(L, 2));
179         getClient(L)->pushToEventQueue(event);
180         lua_pushboolean(L, true);
181         return 1;
182 }
183
184 // send_respawn()
185 int ModApiClient::l_send_respawn(lua_State *L)
186 {
187         getClient(L)->sendRespawn();
188         return 0;
189 }
190
191 // disconnect()
192 int ModApiClient::l_disconnect(lua_State *L)
193 {
194         // Stops badly written Lua code form causing boot loops
195         if (getClient(L)->isShutdown()) {
196                 lua_pushboolean(L, false);
197                 return 1;
198         }
199
200         g_gamecallback->disconnect();
201         lua_pushboolean(L, true);
202         return 1;
203 }
204
205 // gettext(text)
206 int ModApiClient::l_gettext(lua_State *L)
207 {
208         std::string text = strgettext(std::string(luaL_checkstring(L, 1)));
209         lua_pushstring(L, text.c_str());
210
211         return 1;
212 }
213
214 // get_node_or_nil(pos)
215 // pos = {x=num, y=num, z=num}
216 int ModApiClient::l_get_node_or_nil(lua_State *L)
217 {
218         // pos
219         v3s16 pos = read_v3s16(L, 1);
220
221         // Do it
222         bool pos_ok;
223         MapNode n = getClient(L)->CSMGetNode(pos, &pos_ok);
224         if (pos_ok) {
225                 // Return node
226                 pushnode(L, n, getClient(L)->ndef());
227         } else {
228                 lua_pushnil(L);
229         }
230         return 1;
231 }
232
233 // get_langauge()
234 int ModApiClient::l_get_language(lua_State *L)
235 {
236 #ifdef _WIN32
237         char *locale = setlocale(LC_ALL, NULL);
238 #else
239         char *locale = setlocale(LC_MESSAGES, NULL);
240 #endif
241         std::string lang = gettext("LANG_CODE");
242         if (lang == "LANG_CODE")
243                 lang = "";
244
245         lua_pushstring(L, locale);
246         lua_pushstring(L, lang.c_str());
247         return 2;
248 }
249
250 // get_meta(pos)
251 int ModApiClient::l_get_meta(lua_State *L)
252 {
253         v3s16 p = read_v3s16(L, 1);
254
255         // check restrictions first
256         bool pos_ok;
257         getClient(L)->CSMGetNode(p, &pos_ok);
258         if (!pos_ok)
259                 return 0;
260
261         NodeMetadata *meta = getEnv(L)->getMap().getNodeMetadata(p);
262         NodeMetaRef::createClient(L, meta);
263         return 1;
264 }
265
266 // sound_play(spec, parameters)
267 int ModApiClient::l_sound_play(lua_State *L)
268 {
269         ISoundManager *sound = getClient(L)->getSoundManager();
270
271         SimpleSoundSpec spec;
272         read_soundspec(L, 1, spec);
273
274         float gain = 1.0f;
275         float pitch = 1.0f;
276         bool looped = false;
277         s32 handle;
278
279         if (lua_istable(L, 2)) {
280                 getfloatfield(L, 2, "gain", gain);
281                 getfloatfield(L, 2, "pitch", pitch);
282                 getboolfield(L, 2, "loop", looped);
283
284                 lua_getfield(L, 2, "pos");
285                 if (!lua_isnil(L, -1)) {
286                         v3f pos = read_v3f(L, -1) * BS;
287                         lua_pop(L, 1);
288                         handle = sound->playSoundAt(
289                                         spec.name, looped, gain * spec.gain, pos, pitch);
290                         lua_pushinteger(L, handle);
291                         return 1;
292                 }
293         }
294
295         handle = sound->playSound(spec.name, looped, gain * spec.gain, spec.fade, pitch);
296         lua_pushinteger(L, handle);
297
298         return 1;
299 }
300
301 // sound_stop(handle)
302 int ModApiClient::l_sound_stop(lua_State *L)
303 {
304         s32 handle = luaL_checkinteger(L, 1);
305
306         getClient(L)->getSoundManager()->stopSound(handle);
307
308         return 0;
309 }
310
311 // sound_fade(handle, step, gain)
312 int ModApiClient::l_sound_fade(lua_State *L)
313 {
314         s32 handle = luaL_checkinteger(L, 1);
315         float step = readParam<float>(L, 2);
316         float gain = readParam<float>(L, 3);
317         getClient(L)->getSoundManager()->fadeSound(handle, step, gain);
318         return 0;
319 }
320
321 // get_server_info()
322 int ModApiClient::l_get_server_info(lua_State *L)
323 {
324         Client *client = getClient(L);
325         Address serverAddress = client->getServerAddress();
326         lua_newtable(L);
327         lua_pushstring(L, client->getAddressName().c_str());
328         lua_setfield(L, -2, "address");
329         lua_pushstring(L, serverAddress.serializeString().c_str());
330         lua_setfield(L, -2, "ip");
331         lua_pushinteger(L, serverAddress.getPort());
332         lua_setfield(L, -2, "port");
333         lua_pushinteger(L, client->getProtoVersion());
334         lua_setfield(L, -2, "protocol_version");
335         return 1;
336 }
337
338 // get_item_def(itemstring)
339 int ModApiClient::l_get_item_def(lua_State *L)
340 {
341         IGameDef *gdef = getGameDef(L);
342         assert(gdef);
343
344         IItemDefManager *idef = gdef->idef();
345         assert(idef);
346
347         if (checkCSMRestrictionFlag(CSM_RF_READ_ITEMDEFS))
348                 return 0;
349
350         if (!lua_isstring(L, 1))
351                 return 0;
352
353         std::string name = readParam<std::string>(L, 1);
354         if (!idef->isKnown(name))
355                 return 0;
356         const ItemDefinition &def = idef->get(name);
357
358         push_item_definition_full(L, def);
359
360         return 1;
361 }
362
363 // get_node_def(nodename)
364 int ModApiClient::l_get_node_def(lua_State *L)
365 {
366         IGameDef *gdef = getGameDef(L);
367         assert(gdef);
368
369         const NodeDefManager *ndef = gdef->ndef();
370         assert(ndef);
371
372         if (!lua_isstring(L, 1))
373                 return 0;
374
375         if (checkCSMRestrictionFlag(CSM_RF_READ_NODEDEFS))
376                 return 0;
377
378         std::string name = readParam<std::string>(L, 1);
379         const ContentFeatures &cf = ndef->get(ndef->getId(name));
380         if (cf.name != name) // Unknown node. | name = <whatever>, cf.name = ignore
381                 return 0;
382
383         push_content_features(L, cf);
384
385         return 1;
386 }
387
388 // get_privilege_list()
389 int ModApiClient::l_get_privilege_list(lua_State *L)
390 {
391         const Client *client = getClient(L);
392         lua_newtable(L);
393         for (const std::string &priv : client->getPrivilegeList()) {
394                 lua_pushboolean(L, true);
395                 lua_setfield(L, -2, priv.c_str());
396         }
397         return 1;
398 }
399
400 // get_builtin_path()
401 int ModApiClient::l_get_builtin_path(lua_State *L)
402 {
403         lua_pushstring(L, BUILTIN_MOD_NAME ":");
404         return 1;
405 }
406
407 // get_csm_restrictions()
408 int ModApiClient::l_get_csm_restrictions(lua_State *L)
409 {
410         u64 flags = getClient(L)->getCSMRestrictionFlags();
411         const CSMFlagDesc *flagdesc = flagdesc_csm_restriction;
412
413         lua_newtable(L);
414         for (int i = 0; flagdesc[i].name; i++) {
415                 setboolfield(L, -1, flagdesc[i].name, !!(flags & flagdesc[i].flag));
416         }
417         return 1;
418 }
419
420 // send_damage(damage)
421 int ModApiClient::l_send_damage(lua_State *L)
422 {
423         u16 damage = luaL_checknumber(L, 1);
424         getClient(L)->sendDamage(damage);
425         return 0;
426 }
427
428 // place_node(pos)
429 int ModApiClient::l_place_node(lua_State *L)
430 {
431         Client *client = getClient(L);
432         ClientMap &map = client->getEnv().getClientMap();
433         LocalPlayer *player = client->getEnv().getLocalPlayer();
434         ItemStack selected_item, hand_item;
435         player->getWieldedItem(&selected_item, &hand_item);
436         const ItemDefinition &selected_def = selected_item.getDefinition(getGameDef(L)->idef());
437         v3s16 pos = read_v3s16(L, 1);
438         PointedThing pointed;
439         pointed.type = POINTEDTHING_NODE;
440         pointed.node_abovesurface = pos;
441         pointed.node_undersurface = pos;
442         NodeMetadata *meta = map.getNodeMetadata(pos);
443         g_game->nodePlacement(selected_def, selected_item, pos, pos, pointed, meta, true);
444         return 0;
445 }
446
447 // dig_node(pos)
448 int ModApiClient::l_dig_node(lua_State *L)
449 {
450         Client *client = getClient(L);
451         v3s16 pos = read_v3s16(L, 1);
452         PointedThing pointed;
453         pointed.type = POINTEDTHING_NODE;
454         pointed.node_abovesurface = pos;
455         pointed.node_undersurface = pos;
456         client->interact(INTERACT_START_DIGGING, pointed);
457         client->interact(INTERACT_DIGGING_COMPLETED, pointed);
458         client->removeNode(pos);
459         return 0;
460 }
461
462 // get_inventory(location)
463 int ModApiClient::l_get_inventory(lua_State *L)
464 {
465         Client *client = getClient(L);
466         InventoryLocation inventory_location;
467         Inventory *inventory;
468         std::string location;
469
470         location = readParam<std::string>(L, 1);
471
472         try {
473                 inventory_location.deSerialize(location);
474                 inventory = client->getInventory(inventory_location);
475                 if (! inventory)
476                         throw SerializationError(std::string("Attempt to access nonexistant inventory (") + location + ")");
477                 push_inventory(L, inventory);
478         } catch (SerializationError &) {
479                 lua_pushnil(L);
480         }
481
482         return 1;
483 }
484
485 // set_keypress(key_setting, pressed) -> returns true on success
486 int ModApiClient::l_set_keypress(lua_State *L)
487 {
488         std::string setting_name = "keymap_" + readParam<std::string>(L, 1);
489         bool pressed = lua_isboolean(L, 2) && readParam<bool>(L, 2);
490         try {
491                 KeyPress keyCode = getKeySetting(setting_name.c_str());
492                 if (pressed)
493                         g_game->input->setKeypress(keyCode);
494                 else
495                         g_game->input->unsetKeypress(keyCode);
496                 lua_pushboolean(L, true);
497         } catch (SettingNotFoundException &) {
498                 lua_pushboolean(L, false);
499         }
500         return 1;
501 }
502
503 // drop_selected_item()
504 int ModApiClient::l_drop_selected_item(lua_State *L)
505 {
506         g_game->dropSelectedItem();
507         return 0;
508 }
509
510 // get_objects_inside_radius(pos, radius)
511 int ModApiClient::l_get_objects_inside_radius(lua_State *L)
512 {
513         ClientEnvironment &env = getClient(L)->getEnv();
514
515         v3f pos = checkFloatPos(L, 1);
516         float radius = readParam<float>(L, 2) * BS;
517
518         std::vector<DistanceSortedActiveObject> objs;
519         env.getActiveObjects(pos, radius, objs);
520
521         int i = 0;
522         lua_createtable(L, objs.size(), 0);
523         for (const auto obj : objs) {
524                 push_objectRef(L, obj.obj->getId());
525                 lua_rawseti(L, -2, ++i);
526         }
527         return 1;
528 }
529
530 // make_screenshot()
531 int ModApiClient::l_make_screenshot(lua_State *L)
532 {
533         getClient(L)->makeScreenshot();
534         return 0;
535 }
536
537 /*
538 `pointed_thing`
539 ---------------
540
541 * `{type="nothing"}`
542 * `{type="node", under=pos, above=pos}`
543     * Indicates a pointed node selection box.
544     * `under` refers to the node position behind the pointed face.
545     * `above` refers to the node position in front of the pointed face.
546 * `{type="object", ref=ObjectRef}`
547
548 Exact pointing location (currently only `Raycast` supports these fields):
549
550 * `pointed_thing.intersection_point`: The absolute world coordinates of the
551   point on the selection box which is pointed at. May be in the selection box
552   if the pointer is in the box too.
553 * `pointed_thing.box_id`: The ID of the pointed selection box (counting starts
554   from 1).
555 * `pointed_thing.intersection_normal`: Unit vector, points outwards of the
556   selected selection box. This specifies which face is pointed at.
557   Is a null vector `{x = 0, y = 0, z = 0}` when the pointer is inside the
558   selection box.
559 */
560
561 // interact(action, pointed_thing)
562 int ModApiClient::l_interact(lua_State *L)
563 {
564         std::string action_str = readParam<std::string>(L, 1);
565         InteractAction action;
566
567         if (action_str == "start_digging")
568                 action = INTERACT_START_DIGGING;
569         else if (action_str == "stop_digging")
570                 action = INTERACT_STOP_DIGGING;
571         else if (action_str == "digging_completed")
572                 action = INTERACT_DIGGING_COMPLETED;
573         else if (action_str == "place")
574                 action = INTERACT_PLACE;
575         else if (action_str == "use")
576                 action = INTERACT_USE;
577         else if (action_str == "activate")
578                 action = INTERACT_ACTIVATE;
579         else
580                 return 0;
581
582         lua_getfield(L, 2, "type");
583         if (! lua_isstring(L, -1))
584                 return 0;
585         std::string type_str = lua_tostring(L, -1);
586         lua_pop(L, 1);
587
588         PointedThingType type;
589
590         if (type_str == "nothing")
591                 type = POINTEDTHING_NOTHING;
592         else if (type_str == "node")
593                 type = POINTEDTHING_NODE;
594         else if (type_str == "object")
595                 type = POINTEDTHING_OBJECT;
596         else
597                 return 0;
598
599         PointedThing pointed;
600         pointed.type = type;
601         ClientObjectRef *obj;
602
603         switch (type) {
604         case POINTEDTHING_NODE:
605                 lua_getfield(L, 2, "under");
606                 pointed.node_undersurface = check_v3s16(L, -1);
607
608                 lua_getfield(L, 2, "above");
609                 pointed.node_abovesurface = check_v3s16(L, -1);
610                 break;
611         case POINTEDTHING_OBJECT:
612                 lua_getfield(L, 2, "ref");
613                 obj = ClientObjectRef::checkobject(L, -1);
614                 pointed.object_id = obj->getClientActiveObject()->getId();
615                 break;
616         default:
617                 break;
618         }
619
620         getClient(L)->interact(action, pointed);
621         lua_pushboolean(L, true);
622         return 1;
623 }
624
625 StringMap *table_to_stringmap(lua_State *L, int index)
626 {
627         StringMap *m = new StringMap;
628
629         lua_pushvalue(L, index);
630         lua_pushnil(L);
631
632         while (lua_next(L, -2)) {
633                 lua_pushvalue(L, -2);
634                 std::basic_string<char> key = lua_tostring(L, -1);
635                 std::basic_string<char> value = lua_tostring(L, -2);
636                 (*m)[key] = value;
637                 lua_pop(L, 2);
638         }
639
640         lua_pop(L, 1);
641
642         return m;
643 }
644
645 // send_inventory_fields(formname, fields)
646 // Only works if the inventory form was opened beforehand.
647 int ModApiClient::l_send_inventory_fields(lua_State *L)
648 {
649         std::string formname = luaL_checkstring(L, 1);
650         StringMap *fields = table_to_stringmap(L, 2);
651
652         getClient(L)->sendInventoryFields(formname, *fields);
653         return 0;
654 }
655
656 // send_nodemeta_fields(position, formname, fields)
657 int ModApiClient::l_send_nodemeta_fields(lua_State *L)
658 {
659         v3s16 pos = check_v3s16(L, 1);
660         std::string formname = luaL_checkstring(L, 2);
661         StringMap *m = table_to_stringmap(L, 3);
662
663         getClient(L)->sendNodemetaFields(pos, formname, *m);
664         return 0;
665 }
666
667 void ModApiClient::Initialize(lua_State *L, int top)
668 {
669         API_FCT(get_current_modname);
670         API_FCT(get_modpath);
671         API_FCT(print);
672         API_FCT(display_chat_message);
673         API_FCT(send_chat_message);
674         API_FCT(clear_out_chat_queue);
675         API_FCT(get_player_names);
676         API_FCT(set_last_run_mod);
677         API_FCT(get_last_run_mod);
678         API_FCT(show_formspec);
679         API_FCT(send_respawn);
680         API_FCT(gettext);
681         API_FCT(get_node_or_nil);
682         API_FCT(disconnect);
683         API_FCT(get_meta);
684         API_FCT(sound_play);
685         API_FCT(sound_stop);
686         API_FCT(sound_fade);
687         API_FCT(get_server_info);
688         API_FCT(get_item_def);
689         API_FCT(get_node_def);
690         API_FCT(get_privilege_list);
691         API_FCT(get_builtin_path);
692         API_FCT(get_language);
693         API_FCT(get_csm_restrictions);
694         API_FCT(send_damage);
695         API_FCT(place_node);
696         API_FCT(dig_node);
697         API_FCT(get_inventory);
698         API_FCT(set_keypress);
699         API_FCT(drop_selected_item);
700         API_FCT(get_objects_inside_radius);
701         API_FCT(make_screenshot);
702         API_FCT(interact);
703         API_FCT(send_inventory_fields);
704         API_FCT(send_nodemeta_fields);
705 }