+ if(num_processed_meshes > 0)
+ g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
+ }
+
+ /*
+ Load fetched media
+ */
+ if (m_media_receive_started) {
+ bool all_stopped = true;
+ for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
+ thread != m_media_fetch_threads.end(); ++thread) {
+ all_stopped &= !(*thread)->IsRunning();
+ while (!(*thread)->m_file_data.empty()) {
+ std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front();
+ ++m_media_received_count;
+
+ bool success = loadMedia(out.second, out.first);
+ if(success){
+ verbosestream<<"Client: Loaded received media: "
+ <<"\""<<out.first<<"\". Caching."<<std::endl;
+ } else{
+ infostream<<"Client: Failed to load received media: "
+ <<"\""<<out.first<<"\". Not caching."<<std::endl;
+ continue;
+ }
+
+ bool did = fs::CreateAllDirs(getMediaCacheDir());
+ if(!did){
+ errorstream<<"Could not create media cache directory"
+ <<std::endl;
+ }
+
+ {
+ std::map<std::string, std::string>::iterator n;
+ n = m_media_name_sha1_map.find(out.first);
+ if(n == m_media_name_sha1_map.end())
+ errorstream<<"The server sent a file that has not "
+ <<"been announced."<<std::endl;
+ else
+ m_media_cache.update_sha1(out.second);
+ }
+ }
+ }
+ if (all_stopped) {
+ std::list<MediaRequest> fetch_failed;
+ for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin();
+ thread != m_media_fetch_threads.end(); ++thread) {
+ for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin();
+ request != (*thread)->m_failed.end(); ++request)
+ fetch_failed.push_back(*request);
+ (*thread)->m_failed.clear();
+ }
+ if (fetch_failed.size() > 0) {
+ infostream << "Failed to remote-fetch " << fetch_failed.size() << " files. "
+ << "Requesting them the usual way." << std::endl;
+ request_media(fetch_failed);
+ }
+ }
+ }
+
+ /*
+ If the server didn't update the inventory in a while, revert
+ the local inventory (so the player notices the lag problem
+ and knows something is wrong).
+ */
+ if(m_inventory_from_server)
+ {
+ float interval = 10.0;
+ float count_before = floor(m_inventory_from_server_age / interval);
+
+ m_inventory_from_server_age += dtime;
+
+ float count_after = floor(m_inventory_from_server_age / interval);
+
+ if(count_after != count_before)
+ {
+ // Do this every <interval> seconds after TOCLIENT_INVENTORY
+ // Reset the locally changed inventory to the authoritative inventory
+ Player *player = m_env.getLocalPlayer();
+ player->inventory = *m_inventory_from_server;
+ m_inventory_updated = true;
+ }
+ }
+
+ /*
+ Update positions of sounds attached to objects
+ */
+ {
+ for(std::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;
+ ClientActiveObject *cao = m_env.getActiveObject(object_id);
+ if(!cao)
+ continue;
+ v3f pos = cao->getPosition();
+ m_sound->updateSoundPosition(client_id, pos);
+ }
+ }
+
+ /*
+ Handle removed remotely initiated sounds
+ */
+ m_removed_sounds_check_timer += dtime;
+ if(m_removed_sounds_check_timer >= 2.32)
+ {
+ m_removed_sounds_check_timer = 0;
+ // Find removed sounds and clear references to them
+ std::set<s32> removed_server_ids;
+ for(std::map<s32, int>::iterator
+ i = m_sounds_server_to_client.begin();
+ i != m_sounds_server_to_client.end();)
+ {
+ s32 server_id = i->first;
+ int client_id = i->second;
+ i++;
+ if(!m_sound->soundExists(client_id)){
+ m_sounds_server_to_client.erase(server_id);
+ m_sounds_client_to_server.erase(client_id);
+ m_sounds_to_objects.erase(client_id);
+ removed_server_ids.insert(server_id);
+ }
+ }
+ // Sync to server
+ if(removed_server_ids.size() != 0)
+ {
+ std::ostringstream os(std::ios_base::binary);
+ writeU16(os, TOSERVER_REMOVED_SOUNDS);
+ writeU16(os, removed_server_ids.size());
+ for(std::set<s32>::iterator i = removed_server_ids.begin();
+ i != removed_server_ids.end(); i++)
+ writeS32(os, *i);
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ Send(0, data, true);
+ }
+ }
+}
+
+bool Client::loadMedia(const std::string &data, const std::string &filename)
+{
+ // Silly irrlicht's const-incorrectness
+ Buffer<char> data_rw(data.c_str(), data.size());
+
+ std::string name;
+
+ const char *image_ext[] = {
+ ".png", ".jpg", ".bmp", ".tga",
+ ".pcx", ".ppm", ".psd", ".wal", ".rgb",
+ NULL
+ };
+ name = removeStringEnd(filename, image_ext);
+ if(name != "")
+ {
+ verbosestream<<"Client: Attempting to load image "
+ <<"file \""<<filename<<"\""<<std::endl;
+
+ io::IFileSystem *irrfs = m_device->getFileSystem();
+ video::IVideoDriver *vdrv = m_device->getVideoDriver();
+
+ // Create an irrlicht memory file
+ io::IReadFile *rfile = irrfs->createMemoryReadFile(
+ *data_rw, data_rw.getSize(), "_tempreadfile");
+ assert(rfile);
+ // Read image
+ video::IImage *img = vdrv->createImageFromFile(rfile);
+ 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;
+ }
+ }
+
+ const char *sound_ext[] = {
+ ".0.ogg", ".1.ogg", ".2.ogg", ".3.ogg", ".4.ogg",
+ ".5.ogg", ".6.ogg", ".7.ogg", ".8.ogg", ".9.ogg",
+ ".ogg", NULL
+ };
+ name = removeStringEnd(filename, sound_ext);
+ if(name != "")
+ {
+ verbosestream<<"Client: Attempting to load sound "
+ <<"file \""<<filename<<"\""<<std::endl;
+ m_sound->loadSoundData(name, data);
+ return true;
+ }
+
+ const char *model_ext[] = {
+ ".x", ".b3d", ".md2", ".obj",
+ NULL
+ };
+ name = removeStringEnd(filename, model_ext);
+ if(name != "")
+ {
+ verbosestream<<"Client: Storing model into Irrlicht: "
+ <<"\""<<filename<<"\""<<std::endl;
+
+ io::IFileSystem *irrfs = m_device->getFileSystem();
+ io::IReadFile *rfile = irrfs->createMemoryReadFile(
+ *data_rw, data_rw.getSize(), filename.c_str());
+ assert(rfile);
+
+ scene::ISceneManager *smgr = m_device->getSceneManager();
+ scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
+ smgr->getMeshCache()->addMesh(filename.c_str(), mesh);
+
+ return true;