]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_client.cpp
484af2ec35e64fc055a9ff10ebed83e0677e2e26
[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         return 0;
459 }
460
461 // get_inventory(location)
462 int ModApiClient::l_get_inventory(lua_State *L)
463 {
464         Client *client = getClient(L);
465         InventoryLocation inventory_location;
466         Inventory *inventory;
467         std::string location;
468
469         location = readParam<std::string>(L, 1);
470
471         try {
472                 inventory_location.deSerialize(location);
473                 inventory = client->getInventory(inventory_location);
474                 if (! inventory)
475                         throw SerializationError(std::string("Attempt to access nonexistant inventory (") + location + ")");
476                 push_inventory(L, inventory);
477         } catch (SerializationError &) {
478                 lua_pushnil(L);
479         }
480
481         return 1;
482 }
483
484 // set_keypress(key_setting, pressed) -> returns true on success
485 int ModApiClient::l_set_keypress(lua_State *L)
486 {
487         std::string setting_name = "keymap_" + readParam<std::string>(L, 1);
488         bool pressed = lua_isboolean(L, 2) && readParam<bool>(L, 2);
489         try {
490                 KeyPress keyCode = getKeySetting(setting_name.c_str());
491                 if (pressed)
492                         g_game->input->setKeypress(keyCode);
493                 else
494                         g_game->input->unsetKeypress(keyCode);
495                 lua_pushboolean(L, true);
496         } catch (SettingNotFoundException &) {
497                 lua_pushboolean(L, false);
498         }
499         return 1;
500 }
501
502 // drop_selected_item()
503 int ModApiClient::l_drop_selected_item(lua_State *L)
504 {
505         g_game->dropSelectedItem();
506         return 0;
507 }
508
509 // get_objects_inside_radius(pos, radius)
510 int ModApiClient::l_get_objects_inside_radius(lua_State *L)
511 {
512         ClientEnvironment &env = getClient(L)->getEnv();
513
514         v3f pos = checkFloatPos(L, 1);
515         float radius = readParam<float>(L, 2) * BS;
516
517         std::vector<DistanceSortedActiveObject> objs;
518         env.getActiveObjects(pos, radius, objs);
519
520         int i = 0;
521         lua_createtable(L, objs.size(), 0);
522         for (const auto obj : objs) {
523                 ClientObjectRef::create(L, obj.obj);                                                    // TODO: getObjectRefOrCreate
524                 lua_rawseti(L, -2, ++i);
525         }
526         return 1;
527 }
528
529 // make_screenshot()
530 int ModApiClient::l_make_screenshot(lua_State *L)
531 {
532         getClient(L)->makeScreenshot();
533         return 0;
534 }
535
536 /*
537 `pointed_thing`
538 ---------------
539
540 * `{type="nothing"}`
541 * `{type="node", under=pos, above=pos}`
542     * Indicates a pointed node selection box.
543     * `under` refers to the node position behind the pointed face.
544     * `above` refers to the node position in front of the pointed face.
545 * `{type="object", ref=ObjectRef}`
546
547 Exact pointing location (currently only `Raycast` supports these fields):
548
549 * `pointed_thing.intersection_point`: The absolute world coordinates of the
550   point on the selection box which is pointed at. May be in the selection box
551   if the pointer is in the box too.
552 * `pointed_thing.box_id`: The ID of the pointed selection box (counting starts
553   from 1).
554 * `pointed_thing.intersection_normal`: Unit vector, points outwards of the
555   selected selection box. This specifies which face is pointed at.
556   Is a null vector `{x = 0, y = 0, z = 0}` when the pointer is inside the
557   selection box.
558 */
559
560 // interact(action, pointed_thing)
561 int ModApiClient::l_interact(lua_State *L)
562 {
563         std::string action_str = readParam<std::string>(L, 1);
564         InteractAction action;
565
566         if (action_str == "start_digging")
567                 action = INTERACT_START_DIGGING;
568         else if (action_str == "stop_digging")
569                 action = INTERACT_STOP_DIGGING;
570         else if (action_str == "digging_completed")
571                 action = INTERACT_DIGGING_COMPLETED;
572         else if (action_str == "place")
573                 action = INTERACT_PLACE;
574         else if (action_str == "use")
575                 action = INTERACT_USE;
576         else if (action_str == "activate")
577                 action = INTERACT_ACTIVATE;
578         else
579                 return 0;
580
581         lua_getfield(L, 2, "type");
582         if (! lua_isstring(L, -1))
583                 return 0;
584         std::string type_str = lua_tostring(L, -1);
585         lua_pop(L, 1);
586
587         PointedThingType type;
588
589         if (type_str == "nothing")
590                 type = POINTEDTHING_NOTHING;
591         else if (type_str == "node")
592                 type = POINTEDTHING_NODE;
593         else if (type_str == "object")
594                 type = POINTEDTHING_OBJECT;
595         else
596                 return 0;
597
598         PointedThing pointed;
599         pointed.type = type;
600         ClientObjectRef *obj;
601
602         switch (type) {
603         case POINTEDTHING_NODE:
604                 lua_getfield(L, 2, "under");
605                 pointed.node_undersurface = check_v3s16(L, -1);
606
607                 lua_getfield(L, 2, "above");
608                 pointed.node_abovesurface = check_v3s16(L, -1);
609                 break;
610         case POINTEDTHING_OBJECT:
611                 lua_getfield(L, 2, "ref");
612                 obj = ClientObjectRef::checkobject(L, -1);
613                 pointed.object_id = obj->getClientActiveObject()->getId();
614                 break;
615         default:
616                 break;
617         }
618
619         getClient(L)->interact(action, pointed);
620         lua_pushboolean(L, true);
621         return 1;
622 }
623
624 StringMap *table_to_stringmap(lua_State *L, int index)
625 {
626         StringMap *m = new StringMap;
627
628         lua_pushvalue(L, index);
629         lua_pushnil(L);
630
631         while (lua_next(L, -2)) {
632                 lua_pushvalue(L, -2);
633                 std::basic_string<char> key = lua_tostring(L, -1);
634                 std::basic_string<char> value = lua_tostring(L, -2);
635                 (*m)[key] = value;
636                 lua_pop(L, 2);
637         }
638
639         lua_pop(L, 1);
640
641         return m;
642 }
643
644 // send_inventory_fields(formname, fields)
645 // Only works if the inventory form was opened beforehand.
646 int ModApiClient::l_send_inventory_fields(lua_State *L)
647 {
648         std::string formname = luaL_checkstring(L, 1);
649         StringMap *fields = table_to_stringmap(L, 2);
650
651         getClient(L)->sendInventoryFields(formname, *fields);
652         return 0;
653 }
654
655 // send_nodemeta_fields(position, formname, fields)
656 int ModApiClient::l_send_nodemeta_fields(lua_State *L)
657 {
658         v3s16 pos = check_v3s16(L, 1);
659         std::string formname = luaL_checkstring(L, 2);
660         StringMap *m = table_to_stringmap(L, 3);
661
662         getClient(L)->sendNodemetaFields(pos, formname, *m);
663         return 0;
664 }
665
666 void ModApiClient::Initialize(lua_State *L, int top)
667 {
668         API_FCT(get_current_modname);
669         API_FCT(get_modpath);
670         API_FCT(print);
671         API_FCT(display_chat_message);
672         API_FCT(send_chat_message);
673         API_FCT(clear_out_chat_queue);
674         API_FCT(get_player_names);
675         API_FCT(set_last_run_mod);
676         API_FCT(get_last_run_mod);
677         API_FCT(show_formspec);
678         API_FCT(send_respawn);
679         API_FCT(gettext);
680         API_FCT(get_node_or_nil);
681         API_FCT(disconnect);
682         API_FCT(get_meta);
683         API_FCT(sound_play);
684         API_FCT(sound_stop);
685         API_FCT(sound_fade);
686         API_FCT(get_server_info);
687         API_FCT(get_item_def);
688         API_FCT(get_node_def);
689         API_FCT(get_privilege_list);
690         API_FCT(get_builtin_path);
691         API_FCT(get_language);
692         API_FCT(get_csm_restrictions);
693         API_FCT(send_damage);
694         API_FCT(place_node);
695         API_FCT(dig_node);
696         API_FCT(get_inventory);
697         API_FCT(set_keypress);
698         API_FCT(drop_selected_item);
699         API_FCT(get_objects_inside_radius);
700         API_FCT(make_screenshot);
701         API_FCT(interact);
702         API_FCT(send_inventory_fields);
703         API_FCT(send_nodemeta_fields);
704 }