From 23677be951b4dc2b9cc174ceb92f0e0b8e8eb867 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Tue, 25 Jun 2019 21:18:08 +0200 Subject: [PATCH] Load CSM environment after the restrictions are known Safety-guards for CSM callbacks to abort on a bad implementation Only run callbacks when the mods are loaded (and with it: builtin) Duplication checks inside constructors --- src/client/camera.cpp | 4 ++-- src/client/client.cpp | 35 +++++++++++++--------------- src/client/client.h | 1 - src/client/game.cpp | 20 +++++++--------- src/network/clientpackethandler.cpp | 5 ++-- src/script/cpp_api/s_base.cpp | 7 ++++++ src/script/cpp_api/s_security.cpp | 1 + src/script/lua_api/l_camera.cpp | 21 ++++++++++------- src/script/lua_api/l_localplayer.cpp | 22 ++++++++++------- src/script/scripting_client.cpp | 3 +-- 10 files changed, 64 insertions(+), 55 deletions(-) diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 7e953d4c7..025bd081d 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -99,9 +99,9 @@ bool Camera::successfullyCreated(std::string &error_message) error_message.clear(); } - if (g_settings->getBool("enable_client_modding")) { + if (m_client->modsLoaded()) m_client->getScript()->on_camera_ready(this); - } + return error_message.empty(); } diff --git a/src/client/client.cpp b/src/client/client.cpp index 4c5485325..9535acc8e 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -108,15 +108,6 @@ Client::Client( m_minimap = new Minimap(this); } m_cache_save_interval = g_settings->getU16("server_map_save_interval"); - - m_modding_enabled = g_settings->getBool("enable_client_modding"); - // Only create the client script environment if client scripting is enabled by the - // client. - if (m_modding_enabled) { - m_script = new ClientScripting(this); - m_env.setScript(m_script); - m_script->setEnv(&m_env); - } } void Client::loadMods() @@ -124,9 +115,8 @@ void Client::loadMods() // Don't load mods twice. // If client scripting is disabled by the client, don't load builtin or // client-provided mods. - if (m_mods_loaded || !m_modding_enabled) { + if (m_mods_loaded || !g_settings->getBool("enable_client_modding")) return; - } // If client scripting is disabled by the server, don't load builtin or // client-provided mods. @@ -135,11 +125,13 @@ void Client::loadMods() if (checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOAD_CLIENT_MODS)) { warningstream << "Client-provided mod loading is disabled by server." << std::endl; - // This line is needed because builtin is not loaded - m_modding_enabled = false; return; } + m_script = new ClientScripting(this); + m_env.setScript(m_script); + m_script->setEnv(&m_env); + // Load builtin scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath()); m_script->loadModFromMemory(BUILTIN_MOD_NAME); @@ -185,9 +177,15 @@ void Client::loadMods() for (const ModSpec &mod : m_mods) m_script->loadModFromMemory(mod.name); + // Mods are done loading. Unlock callbacks + m_mods_loaded = true; + // Run a callback when mods are loaded m_script->on_mods_loaded(); - m_mods_loaded = true; + if (m_state == LC_Ready) + m_script->on_client_ready(m_env.getLocalPlayer()); + if (m_camera) + m_script->on_camera_ready(m_camera); } bool Client::checkBuiltinIntegrity() @@ -239,7 +237,7 @@ const ModSpec* Client::getModSpec(const std::string &modname) const void Client::Stop() { m_shutdown = true; - if (m_modding_enabled) + if (m_mods_loaded) m_script->on_shutdown(); //request all client managed threads to stop m_mesh_update_thread.stop(); @@ -249,7 +247,7 @@ void Client::Stop() m_localdb->endSave(); } - if (m_modding_enabled) + if (m_mods_loaded) delete m_script; } @@ -1497,7 +1495,7 @@ void Client::typeChatMessage(const std::wstring &message) return; // If message was consumed by script API, don't send it to server - if (m_modding_enabled && m_script->on_sending_message(wide_to_utf8(message))) + if (m_mods_loaded && m_script->on_sending_message(wide_to_utf8(message))) return; // Send to others @@ -1693,9 +1691,8 @@ void Client::afterContentReceived() m_state = LC_Ready; sendReady(); - if (g_settings->getBool("enable_client_modding")) { + if (m_mods_loaded) m_script->on_client_ready(m_env.getLocalPlayer()); - } text = wgettext("Done!"); RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 100); diff --git a/src/client/client.h b/src/client/client.h index 85456fe4d..6dad48c3d 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -397,7 +397,6 @@ public: } ClientScripting *getScript() { return m_script; } - const bool moddingEnabled() const { return m_modding_enabled; } const bool modsLoaded() const { return m_mods_loaded; } void pushToEventQueue(ClientEvent *event); diff --git a/src/client/game.cpp b/src/client/game.cpp index 5bf41bcd6..b5508f2cb 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -184,7 +184,7 @@ struct LocalFormspecHandler : public TextDest return; } - if (m_client && m_client->moddingEnabled()) + if (m_client && m_client->modsLoaded()) m_client->getScript()->on_formspec_input(m_formname, fields); } @@ -1870,7 +1870,7 @@ void Game::processKeyInput() } else if (wasKeyDown(KeyType::CMD)) { openConsole(0.2, L"/"); } else if (wasKeyDown(KeyType::CMD_LOCAL)) { - if (client->moddingEnabled()) + if (client->modsLoaded()) openConsole(0.2, L"."); else m_game_ui->showStatusText(wgettext("Client side scripting is disabled")); @@ -2026,7 +2026,7 @@ void Game::openInventory() InventoryLocation inventoryloc; inventoryloc.setCurrentPlayer(); - if (!client->moddingEnabled() + if (!client->modsLoaded() || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { TextDest *txt_dst = new TextDestPlayerInventory(client); auto *&formspec = m_game_ui->updateFormspec(""); @@ -2516,9 +2516,8 @@ void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam) void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam) { - if (client->moddingEnabled()) { + if (client->modsLoaded()) client->getScript()->on_damage_taken(event->player_damage.amount); - } // Damage flash and hurt tilt are not used at death if (client->getHP() > 0) { @@ -2546,7 +2545,7 @@ void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation * { // If client scripting is enabled, deathscreen is handled by CSM code in // builtin/client/init.lua - if (client->moddingEnabled()) + if (client->modsLoaded()) client->getScript()->on_death(); else showDeathFormspec(); @@ -3033,9 +3032,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) else runData.repeat_rightclick_timer = 0; - if (selected_def.usable && input->getLeftState()) { - if (input->getLeftClicked() && (!client->moddingEnabled() + if (input->getLeftClicked() && (!client->modsLoaded() || !client->getScript()->on_item_use(selected_item, pointed))) client->interact(INTERACT_USE, pointed); } else if (pointed.type == POINTEDTHING_NODE) { @@ -3240,7 +3238,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, soundmaker->m_player_rightpunch_sound = def.sound_place; - if (client->moddingEnabled()) + if (client->modsLoaded()) client->getScript()->on_placenode(pointed, def); } else { soundmaker->m_player_rightpunch_sound = @@ -3496,7 +3494,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, if (!runData.digging) { infostream << "Started digging" << std::endl; runData.dig_instantly = runData.dig_time_complete == 0; - if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n)) + if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n)) return; client->interact(INTERACT_START_DIGGING, pointed); runData.digging = true; @@ -3556,7 +3554,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, bool is_valid_position; MapNode wasnode = map.getNode(nodepos, &is_valid_position); if (is_valid_position) { - if (client->moddingEnabled() && + if (client->modsLoaded() && client->getScript()->on_dignode(nodepos, wasnode)) { return; } diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 91829474e..a8ae8a5ef 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -415,7 +415,7 @@ void Client::handleCommand_ChatMessage(NetworkPacket *pkt) chatMessage->type = (ChatMessageType) message_type; // @TODO send this to CSM using ChatMessage object - if (moddingEnabled() && m_script->on_receiving_message( + if (modsLoaded() && m_script->on_receiving_message( wide_to_utf8(chatMessage->message))) { // Message was consumed by CSM and should not be handled by client delete chatMessage; @@ -532,9 +532,8 @@ void Client::handleCommand_HP(NetworkPacket* pkt) player->hp = hp; - if (moddingEnabled()) { + if (modsLoaded()) m_script->on_hp_modification(hp); - } if (hp < oldhp) { // Add to ClientEvent queue diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index e73f613ce..caa335d76 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -232,6 +232,13 @@ void ScriptApiBase::loadModFromMemory(const std::string &mod_name) void ScriptApiBase::runCallbacksRaw(int nargs, RunCallbacksMode mode, const char *fxn) { +#ifndef SERVER + // Hard fail for bad guarded callbacks + // Only run callbacks when the scripting enviroment is loaded + FATAL_ERROR_IF(m_type == ScriptingType::Client && + !getClient()->modsLoaded(), fxn); +#endif + #ifdef SCRIPTAPI_LOCK_DEBUG assert(m_lock_recursion_count > 0); #endif diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 876c7761a..d972be980 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -264,6 +264,7 @@ void ScriptApiSecurity::initializeSecurityClient() }; static const char *debug_whitelist[] = { "getinfo", + "traceback" }; #if USE_LUAJIT diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp index 462006777..80071b3b8 100644 --- a/src/script/lua_api/l_camera.cpp +++ b/src/script/lua_api/l_camera.cpp @@ -31,19 +31,24 @@ LuaCamera::LuaCamera(Camera *m) : m_camera(m) void LuaCamera::create(lua_State *L, Camera *m) { + lua_getglobal(L, "core"); + luaL_checktype(L, -1, LUA_TTABLE); + int objectstable = lua_gettop(L); + lua_getfield(L, -1, "camera"); + + // Duplication check + if (lua_type(L, -1) == LUA_TUSERDATA) { + lua_pop(L, 1); + return; + } + LuaCamera *o = new LuaCamera(m); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); lua_setmetatable(L, -2); - int camera_object = lua_gettop(L); - - lua_getglobal(L, "core"); - luaL_checktype(L, -1, LUA_TTABLE); - int coretable = lua_gettop(L); - - lua_pushvalue(L, camera_object); - lua_setfield(L, coretable, "camera"); + lua_pushvalue(L, lua_gettop(L)); + lua_setfield(L, objectstable, "camera"); } int LuaCamera::l_set_camera_mode(lua_State *L) diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp index 7444d0e88..3e14e48e4 100644 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@ -30,20 +30,24 @@ LuaLocalPlayer::LuaLocalPlayer(LocalPlayer *m) : m_localplayer(m) void LuaLocalPlayer::create(lua_State *L, LocalPlayer *m) { + lua_getglobal(L, "core"); + luaL_checktype(L, -1, LUA_TTABLE); + int objectstable = lua_gettop(L); + lua_getfield(L, -1, "localplayer"); + + // Duplication check + if (lua_type(L, -1) == LUA_TUSERDATA) { + lua_pop(L, 1); + return; + } + LuaLocalPlayer *o = new LuaLocalPlayer(m); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); lua_setmetatable(L, -2); - // Keep localplayer object stack id - int localplayer_object = lua_gettop(L); - - lua_getglobal(L, "core"); - luaL_checktype(L, -1, LUA_TTABLE); - int coretable = lua_gettop(L); - - lua_pushvalue(L, localplayer_object); - lua_setfield(L, coretable, "localplayer"); + lua_pushvalue(L, lua_gettop(L)); + lua_setfield(L, objectstable, "localplayer"); } int LuaLocalPlayer::l_get_velocity(lua_State *L) diff --git a/src/script/scripting_client.cpp b/src/script/scripting_client.cpp index 86e5f2874..239c98e57 100644 --- a/src/script/scripting_client.cpp +++ b/src/script/scripting_client.cpp @@ -84,8 +84,7 @@ void ClientScripting::InitializeModApi(lua_State *L, int top) void ClientScripting::on_client_ready(LocalPlayer *localplayer) { - lua_State *L = getStack(); - LuaLocalPlayer::create(L, localplayer); + LuaLocalPlayer::create(getStack(), localplayer); } void ClientScripting::on_camera_ready(Camera *camera)