Modernize client code (#6250)
[oweals/minetest.git] / src / client.cpp
index 5681c3ddbefb9c3e0156be2773fdaaef142e316e..f7834a02b46117ad030ebe1b6a11572b6980a45f 100644 (file)
@@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiscalingfilter.h"
 #include "script/scripting_client.h"
 #include "game.h"
+#include "chatmessage.h"
 
 extern gui::IGUIEnvironment* guienv;
 
@@ -104,17 +105,17 @@ Client::Client(
        m_script->setEnv(&m_env);
 }
 
-void Client::initMods()
+void Client::loadMods()
 {
-       m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME);
+       // Load builtin
+       scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath());
 
        // If modding is not enabled, don't load mods, just builtin
        if (!m_modding_enabled) {
                return;
        }
-
        ClientModConfiguration modconf(getClientModsLuaPath());
-       std::vector<ModSpec> mods = modconf.getMods();
+       m_mods = modconf.getMods();
        std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
        // complain about mods with unsatisfied dependencies
        if (!modconf.isConsistent()) {
@@ -123,28 +124,52 @@ void Client::initMods()
 
        // Print mods
        infostream << "Client Loading mods: ";
-       for (std::vector<ModSpec>::const_iterator i = mods.begin();
-               i != mods.end(); ++i) {
-               infostream << (*i).name << " ";
-       }
-
+       for (const ModSpec &mod : m_mods)
+               infostream << mod.name << " ";
        infostream << std::endl;
+
        // Load and run "mod" scripts
-       for (std::vector<ModSpec>::const_iterator it = mods.begin();
-               it != mods.end(); ++it) {
-               const ModSpec &mod = *it;
+       for (const ModSpec &mod : m_mods) {
                if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
                        throw ModError("Error loading mod \"" + mod.name +
                                "\": Mod name does not follow naming conventions: "
                                        "Only characters [a-z0-9_] are allowed.");
                }
-               std::string script_path = mod.path + DIR_DELIM + "init.lua";
-               infostream << "  [" << padStringRight(mod.name, 12) << "] [\""
-                       << script_path << "\"]" << std::endl;
-               m_script->loadMod(script_path, mod.name);
+               scanModIntoMemory(mod.name, mod.path);
+       }
+}
+
+void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path,
+                       std::string mod_subpath)
+{
+       std::string full_path = mod_path + DIR_DELIM + mod_subpath;
+       std::vector<fs::DirListNode> mod = fs::GetDirListing(full_path);
+       for (unsigned int j=0; j < mod.size(); j++){
+               std::string filename = mod[j].name;
+               if (mod[j].dir) {
+                       scanModSubfolder(mod_name, mod_path, mod_subpath
+                                       + filename + DIR_DELIM);
+                       continue;
+               }
+               std::replace( mod_subpath.begin(), mod_subpath.end(), DIR_DELIM_CHAR, '/');
+               m_mod_files[mod_name + ":" + mod_subpath + filename] = full_path  + filename;
        }
 }
 
+void Client::initMods()
+{
+       m_script->loadModFromMemory(BUILTIN_MOD_NAME);
+
+       // If modding is not enabled, don't load mods, just builtin
+       if (!m_modding_enabled) {
+               return;
+       }
+
+       // Load and run "mod" scripts
+       for (const ModSpec &mod : m_mods)
+               m_script->loadModFromMemory(mod.name);
+}
+
 const std::string &Client::getBuiltinLuaPath()
 {
        static const std::string builtin_dir = porting::path_share + DIR_DELIM + "builtin";
@@ -378,7 +403,7 @@ void Client::step(float dtime)
        // Control local player (0ms)
        LocalPlayer *player = m_env.getLocalPlayer();
        assert(player);
-       player->applyControl(dtime);
+       player->applyControl(dtime, &m_env);
 
        // Step environment
        m_env.step(dtime);
@@ -1336,8 +1361,22 @@ void Client::removeNode(v3s16 p)
        }
 }
 
+/**
+ * Helper function for Client Side Modding
+ * Flavour is applied there, this should not be used for core engine
+ * @param p
+ * @param is_valid_position
+ * @return
+ */
 MapNode Client::getNode(v3s16 p, bool *is_valid_position)
 {
+       if (checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOOKUP_NODES)) {
+               v3s16 ppos = floatToInt(m_env.getLocalPlayer()->getPosition(), BS);
+               if ((u32) ppos.getDistanceFrom(p) > m_csm_noderange_limit) {
+                       *is_valid_position = false;
+                       return MapNode();
+               }
+       }
        return m_env.getMap().getNodeNoEx(p, is_valid_position);
 }
 
@@ -1494,12 +1533,34 @@ u16 Client::getHP()
        return player->hp;
 }
 
-bool Client::getChatMessage(std::wstring &message)
+bool Client::getChatMessage(std::wstring &res)
 {
-       if(m_chat_queue.size() == 0)
+       if (m_chat_queue.empty())
                return false;
-       message = m_chat_queue.front();
+
+       ChatMessage *chatMessage = m_chat_queue.front();
        m_chat_queue.pop();
+
+       res = L"";
+
+       switch (chatMessage->type) {
+               case CHATMESSAGE_TYPE_RAW:
+               case CHATMESSAGE_TYPE_ANNOUNCE:
+               case CHATMESSAGE_TYPE_SYSTEM:
+                       res = chatMessage->message;
+                       break;
+               case CHATMESSAGE_TYPE_NORMAL: {
+                       if (!chatMessage->sender.empty())
+                               res = L"<" + chatMessage->sender + L"> " + chatMessage->message;
+                       else
+                               res = chatMessage->message;
+                       break;
+               }
+               default:
+                       break;
+       }
+
+       delete chatMessage;
        return true;
 }
 
@@ -1518,14 +1579,13 @@ void Client::typeChatMessage(const std::wstring &message)
        sendChatMessage(message);
 
        // Show locally
-       if (message[0] != L'/')
-       {
+       if (message[0] != L'/') {
                // compatibility code
                if (m_proto_ver < 29) {
                        LocalPlayer *player = m_env.getLocalPlayer();
                        assert(player);
                        std::wstring name = narrow_to_wide(player->getName());
-                       pushToChatQueue((std::wstring)L"<" + name + L"> " + message);
+                       pushToChatQueue(new ChatMessage(CHATMESSAGE_TYPE_NORMAL, message, name));
                }
        }
 }
@@ -1782,7 +1842,8 @@ void Client::makeScreenshot()
                        } else {
                                sstr << "Failed to save screenshot '" << filename << "'";
                        }
-                       pushToChatQueue(narrow_to_wide(sstr.str()));
+                       pushToChatQueue(new ChatMessage(CHATMESSAGE_TYPE_SYSTEM,
+                                       narrow_to_wide(sstr.str())));
                        infostream << sstr.str() << std::endl;
                        image->drop();
                }
@@ -1898,6 +1959,17 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
        return mesh;
 }
 
+const std::string* Client::getModFile(const std::string &filename)
+{
+       StringMap::const_iterator it = m_mod_files.find(filename);
+       if (it == m_mod_files.end()) {
+               errorstream << "Client::getModFile(): File not found: \"" << filename
+                       << "\"" << std::endl;
+               return NULL;
+       }
+       return &it->second;
+}
+
 bool Client::registerModStorage(ModMetadata *storage)
 {
        if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) {