]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/client.cpp
GenericCAO: Fix light position for non-players, remove deprecated initialisation...
[dragonfireclient.git] / src / client.cpp
index 9892e08cd7f9d0310eee0fc103242afb2fe33202..ab0eddcdcaf0c7780c0c8823a639eb5b8d98ff29 100644 (file)
@@ -22,31 +22,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <sstream>
 #include <cmath>
 #include <IFileSystem.h>
+#include "client.h"
+#include "network/clientopcodes.h"
+#include "network/networkpacket.h"
 #include "threading/mutex_auto_lock.h"
+#include "client/renderingengine.h"
 #include "util/auth.h"
 #include "util/directiontables.h"
 #include "util/pointedthing.h"
 #include "util/serialize.h"
 #include "util/string.h"
 #include "util/srp.h"
-#include "client.h"
-#include "network/clientopcodes.h"
 #include "filesys.h"
 #include "mapblock_mesh.h"
 #include "mapblock.h"
 #include "minimap.h"
 #include "mods.h"
 #include "profiler.h"
+#include "shader.h"
 #include "gettext.h"
 #include "clientmap.h"
 #include "clientmedia.h"
 #include "version.h"
-#include "drawscene.h"
 #include "database-sqlite3.h"
 #include "serialization.h"
 #include "guiscalingfilter.h"
 #include "script/scripting_client.h"
 #include "game.h"
+#include "chatmessage.h"
 
 extern gui::IGUIEnvironment* guienv;
 
@@ -55,7 +58,6 @@ extern gui::IGUIEnvironment* guienv;
 */
 
 Client::Client(
-               IrrlichtDevice *device,
                const char *playername,
                const std::string &password,
                const std::string &address_name,
@@ -77,16 +79,12 @@ Client::Client(
        m_event(event),
        m_mesh_update_thread(this),
        m_env(
-               new ClientMap(this, control,
-                       device->getSceneManager()->getRootSceneNode(),
-                       device->getSceneManager(), 666),
-               device->getSceneManager(),
-               tsrc, this, device
+               new ClientMap(this, control, 666),
+               tsrc, this
        ),
        m_particle_manager(&m_env),
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
        m_address_name(address_name),
-       m_device(device),
        m_server_ser_ver(SER_FMT_VER_INVALID),
        m_last_chat_message_sent(time(NULL)),
        m_password(password),
@@ -99,7 +97,7 @@ Client::Client(
        m_env.setLocalPlayer(new LocalPlayer(this, playername));
 
        if (g_settings->getBool("enable_minimap")) {
-               m_minimap = new Minimap(device, this);
+               m_minimap = new Minimap(this);
        }
        m_cache_save_interval = g_settings->getU16("server_map_save_interval");
 
@@ -109,17 +107,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()) {
@@ -128,28 +126,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 (const fs::DirListNode &j : mod) {
+               std::string filename = j.name;
+               if (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";
@@ -210,19 +232,16 @@ Client::~Client()
        delete m_inventory_from_server;
 
        // Delete detached inventories
-       for (std::unordered_map<std::string, Inventory*>::iterator
-                       i = m_detached_inventories.begin();
-                       i != m_detached_inventories.end(); ++i) {
-               delete i->second;
+       for (auto &m_detached_inventorie : m_detached_inventories) {
+               delete m_detached_inventorie.second;
        }
 
        // cleanup 3d model meshes on client shutdown
-       while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
-               scene::IAnimatedMesh *mesh =
-                       m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
+       while (RenderingEngine::get_mesh_cache()->getMeshCount() != 0) {
+               scene::IAnimatedMesh *mesh = RenderingEngine::get_mesh_cache()->getMeshByIndex(0);
 
                if (mesh)
-                       m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
+                       RenderingEngine::get_mesh_cache()->removeMesh(mesh);
        }
 
        delete m_minimap;
@@ -384,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);
@@ -537,15 +556,13 @@ void Client::step(float dtime)
                Update positions of sounds attached to objects
        */
        {
-               for(std::unordered_map<int, u16>::iterator i = m_sounds_to_objects.begin();
-                               i != m_sounds_to_objects.end(); ++i) {
-                       int client_id = i->first;
-                       u16 object_id = i->second;
+               for (auto &m_sounds_to_object : m_sounds_to_objects) {
+                       int client_id = m_sounds_to_object.first;
+                       u16 object_id = m_sounds_to_object.second;
                        ClientActiveObject *cao = m_env.getActiveObject(object_id);
-                       if(!cao)
+                       if (!cao)
                                continue;
-                       v3f pos = cao->getPosition();
-                       m_sound->updateSoundPosition(client_id, pos);
+                       m_sound->updateSoundPosition(client_id, cao->getPosition());
                }
        }
 
@@ -609,13 +626,12 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
                NULL
        };
        name = removeStringEnd(filename, image_ext);
-       if(name != "")
-       {
+       if (!name.empty()) {
                verbosestream<<"Client: Attempting to load image "
                <<"file \""<<filename<<"\""<<std::endl;
 
-               io::IFileSystem *irrfs = m_device->getFileSystem();
-               video::IVideoDriver *vdrv = m_device->getVideoDriver();
+               io::IFileSystem *irrfs = RenderingEngine::get_filesystem();
+               video::IVideoDriver *vdrv = RenderingEngine::get_video_driver();
 
                // Create an irrlicht memory file
                io::IReadFile *rfile = irrfs->createMemoryReadFile(
@@ -625,18 +641,17 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
 
                // Read image
                video::IImage *img = vdrv->createImageFromFile(rfile);
-               if(!img){
+               if (!img) {
                        errorstream<<"Client: Cannot create image from data of "
                                        <<"file \""<<filename<<"\""<<std::endl;
                        rfile->drop();
                        return false;
                }
-               else {
-                       m_tsrc->insertSourceImage(filename, img);
-                       img->drop();
-                       rfile->drop();
-                       return true;
-               }
+
+               m_tsrc->insertSourceImage(filename, img);
+               img->drop();
+               rfile->drop();
+               return true;
        }
 
        const char *sound_ext[] = {
@@ -645,8 +660,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
                ".ogg", NULL
        };
        name = removeStringEnd(filename, sound_ext);
-       if(name != "")
-       {
+       if (!name.empty()) {
                verbosestream<<"Client: Attempting to load sound "
                <<"file \""<<filename<<"\""<<std::endl;
                m_sound->loadSoundData(name, data);
@@ -657,9 +671,9 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
                ".x", ".b3d", ".md2", ".obj",
                NULL
        };
+
        name = removeStringEnd(filename, model_ext);
-       if(name != "")
-       {
+       if (!name.empty()) {
                verbosestream<<"Client: Storing model into memory: "
                                <<"\""<<filename<<"\""<<std::endl;
                if(m_mesh_data.count(filename))
@@ -713,9 +727,8 @@ void Client::request_media(const std::vector<std::string> &file_requests)
 
        pkt << (u16) (file_requests_size & 0xFFFF);
 
-       for(std::vector<std::string>::const_iterator i = file_requests.begin();
-                       i != file_requests.end(); ++i) {
-               pkt << (*i);
+       for (const std::string &file_request : file_requests) {
+               pkt << file_request;
        }
 
        Send(&pkt);
@@ -1001,7 +1014,7 @@ void Client::startAuth(AuthMechanism chosen_auth_mechanism)
                                &verifier, &salt);
 
                        NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
-                       resp_pkt << salt << verifier << (u8)((m_password == "") ? 1 : 0);
+                       resp_pkt << salt << verifier << (u8)((m_password.empty()) ? 1 : 0);
 
                        Send(&resp_pkt);
                        break;
@@ -1043,12 +1056,8 @@ void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
 
        pkt << (u8) blocks.size();
 
-       u32 k = 0;
-       for(std::vector<v3s16>::iterator
-                       j = blocks.begin();
-                       j != blocks.end(); ++j) {
-               pkt << *j;
-               k++;
+       for (const v3s16 &block : blocks) {
+               pkt << block;
        }
 
        Send(&pkt);
@@ -1070,9 +1079,8 @@ void Client::sendRemovedSounds(std::vector<s32> &soundList)
 
        pkt << (u16) (server_ids & 0xFFFF);
 
-       for(std::vector<s32>::iterator i = soundList.begin();
-                       i != soundList.end(); ++i)
-               pkt << *i;
+       for (int sound_id : soundList)
+               pkt << sound_id;
 
        Send(&pkt);
 }
@@ -1335,15 +1343,27 @@ void Client::removeNode(v3s16 p)
        catch(InvalidPositionException &e) {
        }
 
-       for(std::map<v3s16, MapBlock *>::iterator
-                       i = modified_blocks.begin();
-                       i != modified_blocks.end(); ++i) {
-               addUpdateMeshTaskWithEdge(i->first, false, true);
+       for (const auto &modified_block : modified_blocks) {
+               addUpdateMeshTaskWithEdge(modified_block.first, false, true);
        }
 }
 
+/**
+ * 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 {};
+               }
+       }
        return m_env.getMap().getNodeNoEx(p, is_valid_position);
 }
 
@@ -1360,10 +1380,8 @@ void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
        catch(InvalidPositionException &e) {
        }
 
-       for(std::map<v3s16, MapBlock *>::iterator
-                       i = modified_blocks.begin();
-                       i != modified_blocks.end(); ++i) {
-               addUpdateMeshTaskWithEdge(i->first, false, true);
+       for (const auto &modified_block : modified_blocks) {
+               addUpdateMeshTaskWithEdge(modified_block.first, false, true);
        }
 }
 
@@ -1500,19 +1518,41 @@ 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;
 }
 
 void Client::typeChatMessage(const std::wstring &message)
 {
        // Discard empty line
-       if(message == L"")
+       if (message.empty())
                return;
 
        // If message was ate by script API, don't send it to server
@@ -1524,14 +1564,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));
                }
        }
 }
@@ -1623,12 +1662,11 @@ float Client::mediaReceiveProgress()
 {
        if (m_media_downloader)
                return m_media_downloader->getProgress();
-       else
-               return 1.0; // downloader only exists when not yet done
+
+       return 1.0; // downloader only exists when not yet done
 }
 
 typedef struct TextureUpdateArgs {
-       IrrlichtDevice *device;
        gui::IGUIEnvironment *guienv;
        u64 last_time_ms;
        u16 last_percent;
@@ -1655,12 +1693,12 @@ void texture_update_progress(void *args, u32 progress, u32 max_progress)
                        targs->last_time_ms = time_ms;
                        std::basic_stringstream<wchar_t> strm;
                        strm << targs->text_base << " " << targs->last_percent << "%...";
-                       draw_load_screen(strm.str(), targs->device, targs->guienv, targs->tsrc, 0,
+                       RenderingEngine::draw_load_screen(strm.str(), targs->guienv, targs->tsrc, 0,
                                72 + (u16) ((18. / 100.) * (double) targs->last_percent), true);
                }
 }
 
-void Client::afterContentReceived(IrrlichtDevice *device)
+void Client::afterContentReceived()
 {
        infostream<<"Client::afterContentReceived() started"<<std::endl;
        assert(m_itemdef_received); // pre-condition
@@ -1672,28 +1710,28 @@ void Client::afterContentReceived(IrrlichtDevice *device)
        // Clear cached pre-scaled 2D GUI images, as this cache
        // might have images with the same name but different
        // content from previous sessions.
-       guiScalingCacheClear(device->getVideoDriver());
+       guiScalingCacheClear();
 
        // Rebuild inherited images and recreate textures
        infostream<<"- Rebuilding images and textures"<<std::endl;
-       draw_load_screen(text,device, guienv, m_tsrc, 0, 70);
+       RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 70);
        m_tsrc->rebuildImagesAndTextures();
        delete[] text;
 
        // Rebuild shaders
        infostream<<"- Rebuilding shaders"<<std::endl;
        text = wgettext("Rebuilding shaders...");
-       draw_load_screen(text, device, guienv, m_tsrc, 0, 71);
+       RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 71);
        m_shsrc->rebuildShaders();
        delete[] text;
 
        // Update node aliases
        infostream<<"- Updating node aliases"<<std::endl;
        text = wgettext("Initializing nodes...");
-       draw_load_screen(text, device, guienv, m_tsrc, 0, 72);
+       RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 72);
        m_nodedef->updateAliases(m_itemdef);
        std::string texture_path = g_settings->get("texture_path");
-       if (texture_path != "" && fs::IsDir(texture_path))
+       if (!texture_path.empty() && fs::IsDir(texture_path))
                m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
        m_nodedef->setNodeRegistrationStatus(true);
        m_nodedef->runNodeResolveCallbacks();
@@ -1702,7 +1740,6 @@ void Client::afterContentReceived(IrrlichtDevice *device)
        // Update node textures and assign shaders to each tile
        infostream<<"- Updating node textures"<<std::endl;
        TextureUpdateArgs tu_args;
-       tu_args.device = device;
        tu_args.guienv = guienv;
        tu_args.last_time_ms = porting::getTimeMs();
        tu_args.last_percent = 0;
@@ -1724,7 +1761,7 @@ void Client::afterContentReceived(IrrlichtDevice *device)
        }
 
        text = wgettext("Done!");
-       draw_load_screen(text, device, guienv, m_tsrc, 0, 100);
+       RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 100);
        infostream<<"Client::afterContentReceived() done"<<std::endl;
        delete[] text;
 }
@@ -1742,7 +1779,7 @@ float Client::getCurRate()
 
 void Client::makeScreenshot()
 {
-       irr::video::IVideoDriver *driver = m_device->getVideoDriver();
+       irr::video::IVideoDriver *driver = RenderingEngine::get_video_driver();
        irr::video::IImage* const raw_image = driver->createScreenShot();
 
        if (!raw_image)
@@ -1790,7 +1827,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();
                }
@@ -1857,10 +1895,7 @@ IShaderSource* Client::getShaderSource()
 {
        return m_shsrc;
 }
-scene::ISceneManager* Client::getSceneManager()
-{
-       return m_device->getSceneManager();
-}
+
 u16 Client::allocateUnknownNodeId(const std::string &name)
 {
        errorstream << "Client::allocateUnknownNodeId(): "
@@ -1892,25 +1927,34 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
                return NULL;
        }
        const std::string &data    = it->second;
-       scene::ISceneManager *smgr = m_device->getSceneManager();
 
        // Create the mesh, remove it from cache and return it
        // This allows unique vertex colors and other properties for each instance
        Buffer<char> data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht
-       io::IFileSystem *irrfs = m_device->getFileSystem();
-       io::IReadFile *rfile   = irrfs->createMemoryReadFile(
+       io::IReadFile *rfile   = RenderingEngine::get_filesystem()->createMemoryReadFile(
                        *data_rw, data_rw.getSize(), filename.c_str());
        FATAL_ERROR_IF(!rfile, "Could not create/open RAM file");
 
-       scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
+       scene::IAnimatedMesh *mesh = RenderingEngine::get_scene_manager()->getMesh(rfile);
        rfile->drop();
        // NOTE: By playing with Irrlicht refcounts, maybe we could cache a bunch
        // of uniquely named instances and re-use them
        mesh->grab();
-       smgr->getMeshCache()->removeMesh(mesh);
+       RenderingEngine::get_mesh_cache()->removeMesh(mesh);
        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()) {