3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
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.
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.
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.
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"
32 #include "l_internal.h"
33 #include "l_clientobject.h"
34 #include "lua_api/l_nodemeta.h"
35 #include "gui/mainmenumanager.h"
37 #include "util/string.h"
39 #include "client/keycode.h"
41 #define checkCSMRestrictionFlag(flag) \
42 ( getClient(L)->checkCSMRestrictionFlag(CSMRestrictionFlags::flag) )
44 // Not the same as FlagDesc, which contains an `u32 flag`
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
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},
65 // get_current_modname()
66 int ModApiClient::l_get_current_modname(lua_State *L)
68 lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME);
72 // get_modpath(modname)
73 int ModApiClient::l_get_modpath(lua_State *L)
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());
83 int ModApiClient::l_get_last_run_mod(lua_State *L)
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()) {
89 lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str());
94 // set_last_run_mod(modname)
95 int ModApiClient::l_set_last_run_mod(lua_State *L)
97 if (!lua_isstring(L, 1))
100 const char *mod = lua_tostring(L, 1);
101 getScriptApiBase(L)->setOriginDirect(mod);
102 lua_pushboolean(L, true);
107 int ModApiClient::l_print(lua_State *L)
109 NO_MAP_LOCK_REQUIRED;
110 std::string text = luaL_checkstring(L, 1);
111 rawstream << text << std::endl;
115 // display_chat_message(message)
116 int ModApiClient::l_display_chat_message(lua_State *L)
118 if (!lua_isstring(L, 1))
121 std::string message = luaL_checkstring(L, 1);
122 getClient(L)->pushToChatQueue(new ChatMessage(utf8_to_wide(message)));
123 lua_pushboolean(L, true);
127 // send_chat_message(message)
128 int ModApiClient::l_send_chat_message(lua_State *L)
130 if (!lua_isstring(L, 1))
133 // If server disabled this API, discard
135 if (checkCSMRestrictionFlag(CSM_RF_CHAT_MESSAGES))
138 std::string message = luaL_checkstring(L, 1);
139 getClient(L)->sendChatMessage(utf8_to_wide(message));
143 // clear_out_chat_queue()
144 int ModApiClient::l_clear_out_chat_queue(lua_State *L)
146 getClient(L)->clearOutChatQueue();
150 // get_player_names()
151 int ModApiClient::l_get_player_names(lua_State *L)
153 if (checkCSMRestrictionFlag(CSM_RF_READ_PLAYERINFO))
156 const std::list<std::string> &plist = getClient(L)->getConnectedPlayerNames();
157 lua_createtable(L, plist.size(), 0);
158 int newTable = lua_gettop(L);
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);
169 // show_formspec(formspec)
170 int ModApiClient::l_show_formspec(lua_State *L)
172 if (!lua_isstring(L, 1) || !lua_isstring(L, 2))
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);
185 int ModApiClient::l_send_respawn(lua_State *L)
187 getClient(L)->sendRespawn();
192 int ModApiClient::l_disconnect(lua_State *L)
194 // Stops badly written Lua code form causing boot loops
195 if (getClient(L)->isShutdown()) {
196 lua_pushboolean(L, false);
200 g_gamecallback->disconnect();
201 lua_pushboolean(L, true);
206 int ModApiClient::l_gettext(lua_State *L)
208 std::string text = strgettext(std::string(luaL_checkstring(L, 1)));
209 lua_pushstring(L, text.c_str());
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)
219 v3s16 pos = read_v3s16(L, 1);
223 MapNode n = getClient(L)->CSMGetNode(pos, &pos_ok);
226 pushnode(L, n, getClient(L)->ndef());
234 int ModApiClient::l_get_language(lua_State *L)
237 char *locale = setlocale(LC_ALL, NULL);
239 char *locale = setlocale(LC_MESSAGES, NULL);
241 std::string lang = gettext("LANG_CODE");
242 if (lang == "LANG_CODE")
245 lua_pushstring(L, locale);
246 lua_pushstring(L, lang.c_str());
251 int ModApiClient::l_get_meta(lua_State *L)
253 v3s16 p = read_v3s16(L, 1);
255 // check restrictions first
257 getClient(L)->CSMGetNode(p, &pos_ok);
261 NodeMetadata *meta = getEnv(L)->getMap().getNodeMetadata(p);
262 NodeMetaRef::createClient(L, meta);
266 // sound_play(spec, parameters)
267 int ModApiClient::l_sound_play(lua_State *L)
269 ISoundManager *sound = getClient(L)->getSoundManager();
271 SimpleSoundSpec spec;
272 read_soundspec(L, 1, spec);
279 if (lua_istable(L, 2)) {
280 getfloatfield(L, 2, "gain", gain);
281 getfloatfield(L, 2, "pitch", pitch);
282 getboolfield(L, 2, "loop", looped);
284 lua_getfield(L, 2, "pos");
285 if (!lua_isnil(L, -1)) {
286 v3f pos = read_v3f(L, -1) * BS;
288 handle = sound->playSoundAt(
289 spec.name, looped, gain * spec.gain, pos, pitch);
290 lua_pushinteger(L, handle);
295 handle = sound->playSound(spec.name, looped, gain * spec.gain, spec.fade, pitch);
296 lua_pushinteger(L, handle);
301 // sound_stop(handle)
302 int ModApiClient::l_sound_stop(lua_State *L)
304 s32 handle = luaL_checkinteger(L, 1);
306 getClient(L)->getSoundManager()->stopSound(handle);
311 // sound_fade(handle, step, gain)
312 int ModApiClient::l_sound_fade(lua_State *L)
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);
322 int ModApiClient::l_get_server_info(lua_State *L)
324 Client *client = getClient(L);
325 Address serverAddress = client->getServerAddress();
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");
338 // get_item_def(itemstring)
339 int ModApiClient::l_get_item_def(lua_State *L)
341 IGameDef *gdef = getGameDef(L);
344 IItemDefManager *idef = gdef->idef();
347 if (checkCSMRestrictionFlag(CSM_RF_READ_ITEMDEFS))
350 if (!lua_isstring(L, 1))
353 std::string name = readParam<std::string>(L, 1);
354 if (!idef->isKnown(name))
356 const ItemDefinition &def = idef->get(name);
358 push_item_definition_full(L, def);
363 // get_node_def(nodename)
364 int ModApiClient::l_get_node_def(lua_State *L)
366 IGameDef *gdef = getGameDef(L);
369 const NodeDefManager *ndef = gdef->ndef();
372 if (!lua_isstring(L, 1))
375 if (checkCSMRestrictionFlag(CSM_RF_READ_NODEDEFS))
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
383 push_content_features(L, cf);
388 // get_privilege_list()
389 int ModApiClient::l_get_privilege_list(lua_State *L)
391 const Client *client = getClient(L);
393 for (const std::string &priv : client->getPrivilegeList()) {
394 lua_pushboolean(L, true);
395 lua_setfield(L, -2, priv.c_str());
400 // get_builtin_path()
401 int ModApiClient::l_get_builtin_path(lua_State *L)
403 lua_pushstring(L, BUILTIN_MOD_NAME ":");
407 // get_csm_restrictions()
408 int ModApiClient::l_get_csm_restrictions(lua_State *L)
410 u64 flags = getClient(L)->getCSMRestrictionFlags();
411 const CSMFlagDesc *flagdesc = flagdesc_csm_restriction;
414 for (int i = 0; flagdesc[i].name; i++) {
415 setboolfield(L, -1, flagdesc[i].name, !!(flags & flagdesc[i].flag));
420 // send_damage(damage)
421 int ModApiClient::l_send_damage(lua_State *L)
423 u16 damage = luaL_checknumber(L, 1);
424 getClient(L)->sendDamage(damage);
429 int ModApiClient::l_place_node(lua_State *L)
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);
448 int ModApiClient::l_dig_node(lua_State *L)
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);
461 // get_inventory(location)
462 int ModApiClient::l_get_inventory(lua_State *L)
464 Client *client = getClient(L);
465 InventoryLocation inventory_location;
466 Inventory *inventory;
467 std::string location;
469 location = readParam<std::string>(L, 1);
472 inventory_location.deSerialize(location);
473 inventory = client->getInventory(inventory_location);
475 throw SerializationError(std::string("Attempt to access nonexistant inventory (") + location + ")");
476 push_inventory(L, inventory);
477 } catch (SerializationError &) {
484 // set_keypress(key_setting, pressed) -> returns true on success
485 int ModApiClient::l_set_keypress(lua_State *L)
487 std::string setting_name = "keymap_" + readParam<std::string>(L, 1);
488 bool pressed = lua_isboolean(L, 2) && readParam<bool>(L, 2);
490 KeyPress keyCode = getKeySetting(setting_name.c_str());
492 g_game->input->setKeypress(keyCode);
494 g_game->input->unsetKeypress(keyCode);
495 lua_pushboolean(L, true);
496 } catch (SettingNotFoundException &) {
497 lua_pushboolean(L, false);
502 // drop_selected_item()
503 int ModApiClient::l_drop_selected_item(lua_State *L)
505 g_game->dropSelectedItem();
509 // get_objects_inside_radius(pos, radius)
510 int ModApiClient::l_get_objects_inside_radius(lua_State *L)
512 ClientEnvironment &env = getClient(L)->getEnv();
514 v3f pos = checkFloatPos(L, 1);
515 float radius = readParam<float>(L, 2) * BS;
517 std::vector<DistanceSortedActiveObject> objs;
518 env.getActiveObjects(pos, radius, objs);
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);
530 int ModApiClient::l_make_screenshot(lua_State *L)
532 getClient(L)->makeScreenshot();
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}`
547 Exact pointing location (currently only `Raycast` supports these fields):
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
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
560 // interact(action, pointed_thing)
561 int ModApiClient::l_interact(lua_State *L)
563 std::string action_str = readParam<std::string>(L, 1);
564 InteractAction action;
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;
581 lua_getfield(L, 2, "type");
582 if (! lua_isstring(L, -1))
584 std::string type_str = lua_tostring(L, -1);
587 PointedThingType type;
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;
598 PointedThing pointed;
600 ClientObjectRef *obj;
603 case POINTEDTHING_NODE:
604 lua_getfield(L, 2, "under");
605 pointed.node_undersurface = check_v3s16(L, -1);
607 lua_getfield(L, 2, "above");
608 pointed.node_abovesurface = check_v3s16(L, -1);
610 case POINTEDTHING_OBJECT:
611 lua_getfield(L, 2, "ref");
612 obj = ClientObjectRef::checkobject(L, -1);
613 pointed.object_id = obj->getClientActiveObject()->getId();
619 getClient(L)->interact(action, pointed);
620 lua_pushboolean(L, true);
624 StringMap *table_to_stringmap(lua_State *L, int index)
626 StringMap *m = new StringMap;
628 lua_pushvalue(L, index);
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);
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)
648 std::string formname = luaL_checkstring(L, 1);
649 StringMap *fields = table_to_stringmap(L, 2);
651 getClient(L)->sendInventoryFields(formname, *fields);
655 // send_nodemeta_fields(position, formname, fields)
656 int ModApiClient::l_send_nodemeta_fields(lua_State *L)
658 v3s16 pos = check_v3s16(L, 1);
659 std::string formname = luaL_checkstring(L, 2);
660 StringMap *m = table_to_stringmap(L, 3);
662 getClient(L)->sendNodemetaFields(pos, formname, *m);
666 void ModApiClient::Initialize(lua_State *L, int top)
668 API_FCT(get_current_modname);
669 API_FCT(get_modpath);
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);
680 API_FCT(get_node_or_nil);
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);
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);
702 API_FCT(send_inventory_fields);
703 API_FCT(send_nodemeta_fields);