Load CSM environment after the restrictions are known
authorSmallJoker <mk939@ymail.com>
Tue, 25 Jun 2019 19:18:08 +0000 (21:18 +0200)
committerSmallJoker <mk939@ymail.com>
Sat, 14 Sep 2019 17:42:25 +0000 (19:42 +0200)
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
src/client/client.cpp
src/client/client.h
src/client/game.cpp
src/network/clientpackethandler.cpp
src/script/cpp_api/s_base.cpp
src/script/cpp_api/s_security.cpp
src/script/lua_api/l_camera.cpp
src/script/lua_api/l_localplayer.cpp
src/script/scripting_client.cpp

index 7e953d4c7f5ccb56f4431af882b45eb2d3fa2f87..025bd081d46c5272be5e30443b1da727677ff3fb 100644 (file)
@@ -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();
 }
 
index 4c54853255d6e050206c39b0bf00507dee1d0bce..9535acc8e57ebcd18af6bc1bd374cf7cc94ddd6c 100644 (file)
@@ -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);
index 85456fe4de32f53118c5b74103d6ea328d668310..6dad48c3df46262d75b23e541a1ad36f8d568cc7 100644 (file)
@@ -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);
index 5bf41bcd6efd0bb99ed83bdacc1d92923849f29e..b5508f2cb5d654e55c1940dd7c45b3c9452c6f5f 100644 (file)
@@ -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;
                        }
index 91829474e35d3ca6e242c47057fa9134dee7eb44..a8ae8a5ef46f8eb9bd8fd002d66423917762201f 100644 (file)
@@ -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
index e73f613ce6a1013071ac037eace60366db2c74cb..caa335d769a67fce6743ddf4013af43918e5f4ec 100644 (file)
@@ -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
index 876c7761a0c262a0d31bb257e8021d0cdaadf128..d972be980178abeb9b4eb23ceab0da27623a724d 100644 (file)
@@ -264,6 +264,7 @@ void ScriptApiSecurity::initializeSecurityClient()
        };
        static const char *debug_whitelist[] = {
                "getinfo",
+               "traceback"
        };
 
 #if USE_LUAJIT
index 462006777feb967b819a40bc6fef666130df260c..80071b3b8f2f3b8a256624da04230617735074d4 100644 (file)
@@ -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)
index 7444d0e88803783fbf4f00cb237d3e2d1592c95b..3e14e48e42bd477aae0ecd502cc76bb4f0984283 100644 (file)
@@ -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)
index 86e5f2874a482f4af69c2452f74dff9bf9fe466b..239c98e57ee412e76b43661acea43dec0f9eedad 100644 (file)
@@ -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)