X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fclient.cpp;h=ca6c80c4d2696376d4a879e0a1fe524f21058796;hb=5a34f40d80ea1a339b599bc11db549a6bd86912f;hp=f9908ad2cbdc6d5f1b1625523e02080a9738d21e;hpb=06cdce1e1231fb5946ac9750f9b53b53c6f7e5f8;p=minetest.git diff --git a/src/client.cpp b/src/client.cpp index f9908ad2c..ca6c80c4d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -19,10 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include +#include #include "clientserver.h" -#include "jmutexautolock.h" +#include "jthread/jmutexautolock.h" #include "main.h" #include +#include "filesys.h" #include "porting.h" #include "mapsector.h" #include "mapblock_mesh.h" @@ -36,26 +38,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #include "shader.h" #include -#include "sha1.h" #include "base64.h" #include "clientmap.h" -#include "filecache.h" +#include "clientmedia.h" #include "sound.h" #include "util/string.h" -#include "hex.h" #include "IMeshCache.h" +#include "serialization.h" #include "util/serialize.h" #include "config.h" #include "util/directiontables.h" - -#if USE_CURL -#include -#endif - -static std::string getMediaCacheDir() -{ - return porting::path_user + DIR_DELIM + "cache" + DIR_DELIM + "media"; -} +#include "util/pointedthing.h" +#include "version.h" /* QueuedMeshUpdate @@ -80,7 +74,6 @@ QueuedMeshUpdate::~QueuedMeshUpdate() MeshUpdateQueue::MeshUpdateQueue() { - m_mutex.Init(); } MeshUpdateQueue::~MeshUpdateQueue() @@ -175,7 +168,7 @@ void * MeshUpdateThread::Thread() BEGIN_DEBUG_EXCEPTION_HANDLER - while(getRun()) + while(!StopRequested()) { /*// Wait for output queue to flush. // Allow 2 in queue, this makes less frametime jitter. @@ -221,45 +214,9 @@ void * MeshUpdateThread::Thread() return NULL; } -void * MediaFetchThread::Thread() -{ - ThreadStarted(); - - log_register_thread("MediaFetchThread"); - - DSTACK(__FUNCTION_NAME); - - BEGIN_DEBUG_EXCEPTION_HANDLER - - #if USE_CURL - CURL *curl; - CURLcode res; - for (std::list::iterator i = m_file_requests.begin(); - i != m_file_requests.end(); ++i) { - curl = curl_easy_init(); - assert(curl); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_URL, (m_remote_url + i->name).c_str()); - curl_easy_setopt(curl, CURLOPT_FAILONERROR, true); - std::ostringstream stream; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &stream); - res = curl_easy_perform(curl); - if (res == CURLE_OK) { - std::string data = stream.str(); - m_file_data.push_back(make_pair(i->name, data)); - } else { - m_failed.push_back(*i); - infostream << "cURL request failed for " << i->name << " (" << curl_easy_strerror(res) << ")"<< std::endl; - } - curl_easy_cleanup(curl); - } - #endif - - END_DEBUG_EXCEPTION_HANDLER(errorstream) - - return NULL; -} +/* + Client +*/ Client::Client( IrrlichtDevice *device, @@ -301,12 +258,9 @@ Client::Client( m_map_seed(0), m_password(password), m_access_denied(false), - m_media_cache(getMediaCacheDir()), - m_media_receive_started(false), - m_media_count(0), - m_media_received_count(0), m_itemdef_received(false), m_nodedef_received(false), + m_media_downloader(new ClientMediaDownloader()), m_time_of_day_set(false), m_last_time_of_day_f(-1), m_time_of_day_update_timer(0), @@ -330,9 +284,20 @@ Client::Client( m_env.addPlayer(player); } +} - for (size_t i = 0; i < g_settings->getU16("media_fetch_threads"); ++i) - m_media_fetch_threads.push_back(new MediaFetchThread(this)); +void Client::Stop() +{ + //request all client managed threads to stop + m_mesh_update_thread.Stop(); +} + +bool Client::isShutdown() +{ + + if (!m_mesh_update_thread.IsRunning()) return true; + + return false; } Client::~Client() @@ -342,11 +307,10 @@ Client::~Client() m_con.Disconnect(); } - m_mesh_update_thread.setRun(false); - while(m_mesh_update_thread.IsRunning()) - sleep_ms(100); + m_mesh_update_thread.Stop(); + m_mesh_update_thread.Wait(); while(!m_mesh_update_thread.m_queue_out.empty()) { - MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front(); + MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx(); delete r.mesh; } @@ -362,10 +326,6 @@ Client::~Client() } } - for (std::list::iterator i = m_media_fetch_threads.begin(); - i != m_media_fetch_threads.end(); ++i) - delete *i; - // cleanup 3d model meshes on client shutdown while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) { scene::IAnimatedMesh * mesh = @@ -423,13 +383,6 @@ void Client::step(float dtime) // 0ms ReceiveAll(); } - - { - //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device); - // 0ms - //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - m_con.RunTimeouts(dtime); - } /* Packet counter @@ -471,7 +424,7 @@ void Client::step(float dtime) core::list deleted_blocks; - float delete_unused_sectors_timeout = + float delete_unused_sectors_timeout = g_settings->getFloat("client_delete_unused_sectors_timeout"); // Delete sector blocks @@ -578,7 +531,7 @@ void Client::step(float dtime) writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX); // Send as unreliable - Send(0, data, false); + Send(1, data, false); } // Not connected, return @@ -637,7 +590,7 @@ void Client::step(float dtime) writeV3S16(&reply[2+1+6*k], *j); k++; } - m_con.Send(PEER_ID_SERVER, 1, reply, true); + m_con.Send(PEER_ID_SERVER, 2, reply, true); if(i == deleted_blocks.end()) break; @@ -746,7 +699,7 @@ void Client::step(float dtime) while(!m_mesh_update_thread.m_queue_out.empty()) { num_processed_meshes++; - MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front(); + MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx(); MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); if(block) { @@ -785,7 +738,7 @@ void Client::step(float dtime) reply[2] = 1; writeV3S16(&reply[3], r.p); // Send as reliable - m_con.Send(PEER_ID_SERVER, 1, reply, true); + m_con.Send(PEER_ID_SERVER, 2, reply, true); } } if(num_processed_meshes > 0) @@ -795,57 +748,12 @@ void Client::step(float dtime) /* Load fetched media */ - if (m_media_receive_started) { - bool all_stopped = true; - for (std::list::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 out = (*thread)->m_file_data.pop_front(); - if(m_media_received_count < m_media_count) - m_media_received_count++; - - bool success = loadMedia(out.second, out.first); - if(success){ - verbosestream<<"Client: Loaded received media: " - <<"\""<::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."< fetch_failed; - for (std::list::iterator thread = m_media_fetch_threads.begin(); - thread != m_media_fetch_threads.end(); ++thread) { - for (std::list::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 (m_media_downloader && m_media_downloader->isStarted()) { + m_media_downloader->step(this); + if (m_media_downloader->isDone()) { + received_media(); + delete m_media_downloader; + m_media_downloader = NULL; } } @@ -926,7 +834,7 @@ void Client::step(float dtime) std::string s = os.str(); SharedBuffer data((u8*)s.c_str(), s.size()); // Send as reliable - Send(0, data, true); + Send(1, data, true); } } } @@ -993,30 +901,12 @@ bool Client::loadMedia(const std::string &data, const std::string &filename) name = removeStringEnd(filename, model_ext); if(name != "") { - verbosestream<<"Client: Storing model into Irrlicht: " + verbosestream<<"Client: Storing model into memory: " <<"\""<getSceneManager(); - - //check if mesh was already cached - scene::IAnimatedMesh *mesh = - smgr->getMeshCache()->getMeshByName(filename.c_str()); - - if (mesh != NULL) { - errorstream << "Multiple models with name: " << filename.c_str() << - " found replacing previous model!" << std::endl; - - smgr->getMeshCache()->removeMesh(mesh); - mesh = 0; - } - - io::IFileSystem *irrfs = m_device->getFileSystem(); - io::IReadFile *rfile = irrfs->createMemoryReadFile( - *data_rw, data_rw.getSize(), filename.c_str()); - assert(rfile); - - mesh = smgr->getMesh(rfile); - smgr->getMeshCache()->addMesh(filename.c_str(), mesh); - rfile->drop(); + if(m_mesh_data.count(filename)) + errorstream<<"Multiple models with name \""< &file_requests) +void Client::request_media(const std::list &file_requests) { std::ostringstream os(std::ios_base::binary); writeU16(os, TOSERVER_REQUEST_MEDIA); writeU16(os, file_requests.size()); - for(std::list::const_iterator i = file_requests.begin(); + for(std::list::const_iterator i = file_requests.begin(); i != file_requests.end(); ++i) { - os<name); + os< data((u8*)s.c_str(), s.size()); // Send as reliable - Send(0, data, true); + Send(1, data, true); infostream<<"Client: Sending media request list to server (" < data((u8*)s.c_str(), s.size()); + // Send as reliable + Send(1, data, true); + infostream<<"Client: Notifying server that we received all media" + <= index+1) && data[index]){ + remove_metadata = false; + } + + addNode(p, n, remove_metadata); } else if(command == TOCLIENT_BLOCKDATA) { @@ -1356,8 +1265,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) std::istringstream is(datastring, std::ios_base::binary); //t3.stop(); - //m_env.printPlayers(infostream); - //TimeTaker t4("player get", m_device); Player *player = m_env.getLocalPlayer(); assert(player != NULL); @@ -1654,96 +1561,49 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); - // Mesh update thread must be stopped while - // updating content definitions - assert(!m_mesh_update_thread.IsRunning()); - int num_files = readU16(is); infostream<<"Client: Received media announcement: packet size: " < file_requests; + if (m_media_downloader == NULL || + m_media_downloader->isStarted()) { + const char *problem = m_media_downloader ? + "we already saw another announcement" : + "all media has been received already"; + errorstream<<"Client: Received media announcement but " + <addFile(name, sha1_raw); } - std::string remote_media = ""; + std::vector remote_media; try { - remote_media = deSerializeString(is); + Strfnd sf(deSerializeString(is)); + while(!sf.atend()) { + std::string baseurl = trim(sf.next(",")); + if(baseurl != "") + m_media_downloader->addRemoteServer(baseurl); + } } catch(SerializationError) { // not supported by server or turned off } - m_media_count = file_requests.size(); - m_media_receive_started = true; - - if (remote_media == "" || !USE_CURL) { - request_media(file_requests); - } else { - #if USE_CURL - std::list::iterator cur = m_media_fetch_threads.begin(); - for(std::list::iterator i = file_requests.begin(); - i != file_requests.end(); ++i) { - (*cur)->m_file_requests.push_back(*i); - cur++; - if (cur == m_media_fetch_threads.end()) - cur = m_media_fetch_threads.begin(); - } - for (std::list::iterator i = m_media_fetch_threads.begin(); - i != m_media_fetch_threads.end(); ++i) { - (*i)->m_remote_url = remote_media; - (*i)->Start(); - } - #endif - - // notify server we received everything - std::ostringstream os(std::ios_base::binary); - writeU16(os, TOSERVER_RECEIVED_MEDIA); - std::string s = os.str(); - SharedBuffer data((u8*)s.c_str(), s.size()); - // Send as reliable - Send(0, data, true); - } - ClientEvent event; - event.type = CE_TEXTURES_UPDATED; - m_client_event_queue.push_back(event); + m_media_downloader->step(this); } else if(command == TOCLIENT_MEDIA) { @@ -1769,67 +1629,32 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) <::iterator n; - n = m_media_name_sha1_map.find(name); - if(n == m_media_name_sha1_map.end()) - errorstream<<"The server sent a file that has not " - <<"been announced."<conventionalTransferDone( + name, data, this); } - - ClientEvent event; - event.type = CE_TEXTURES_UPDATED; - m_client_event_queue.push_back(event); } else if(command == TOCLIENT_TOOLDEF) { @@ -2003,6 +1828,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) float size = readF1000(is); bool collisiondetection = readU8(is); std::string texture = deSerializeLongString(is); + bool vertical = false; + try { + vertical = readU8(is); + } catch (...) {} ClientEvent event; event.type = CE_SPAWN_PARTICLE; @@ -2014,6 +1843,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.spawn_particle.size = size; event.spawn_particle.collisiondetection = collisiondetection; + event.spawn_particle.vertical = vertical; event.spawn_particle.texture = new std::string(texture); m_client_event_queue.push_back(event); @@ -2038,6 +1868,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) bool collisiondetection = readU8(is); std::string texture = deSerializeLongString(is); u32 id = readU32(is); + bool vertical = false; + try { + vertical = readU8(is); + } catch (...) {} ClientEvent event; event.type = CE_ADD_PARTICLESPAWNER; @@ -2056,6 +1890,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.add_particlespawner.minsize = minsize; event.add_particlespawner.maxsize = maxsize; event.add_particlespawner.collisiondetection = collisiondetection; + event.add_particlespawner.vertical = vertical; event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.id = id; @@ -2090,6 +1925,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) u32 dir = readU32(is); v2f align = readV2F1000(is); v2f offset = readV2F1000(is); + v3f world_pos; + try{ + world_pos = readV3F1000(is); + }catch(SerializationError &e) {}; ClientEvent event; event.type = CE_HUDADD; @@ -2104,6 +1943,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.hudadd.dir = dir; event.hudadd.align = new v2f(align); event.hudadd.offset = new v2f(offset); + event.hudadd.world_pos = new v3f(world_pos); m_client_event_queue.push_back(event); } else if(command == TOCLIENT_HUDRM) @@ -2119,9 +1959,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) m_client_event_queue.push_back(event); } else if(command == TOCLIENT_HUDCHANGE) - { + { std::string sdata; v2f v2fdata; + v3f v3fdata; u32 intdata = 0; std::string datastring((char *)&data[2], datasize - 2); @@ -2135,6 +1976,8 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) v2fdata = readV2F1000(is); else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT) sdata = deSerializeString(is); + else if (stat == HUD_STAT_WORLD_POS) + v3fdata = readV3F1000(is); else intdata = readU32(is); @@ -2143,12 +1986,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.hudchange.id = id; event.hudchange.stat = (HudElementStat)stat; event.hudchange.v2fdata = new v2f(v2fdata); + event.hudchange.v3fdata = new v3f(v3fdata); event.hudchange.sdata = new std::string(sdata); event.hudchange.data = intdata; m_client_event_queue.push_back(event); } else if(command == TOCLIENT_HUD_SET_FLAGS) - { + { std::string datastring((char *)&data[2], datasize - 2); std::istringstream is(datastring, std::ios_base::binary); @@ -2176,8 +2020,45 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) s32 hotbar_itemcount = readS32((u8*) value.c_str()); if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX) player->hud_hotbar_itemcount = hotbar_itemcount; + } else if (param == HUD_PARAM_HOTBAR_IMAGE) { + ((LocalPlayer *) player)->hotbar_image = value; + } else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) { + ((LocalPlayer *) player)->hotbar_selected_image = value; } } + else if(command == TOCLIENT_SET_SKY) + { + std::string datastring((char *)&data[2], datasize - 2); + std::istringstream is(datastring, std::ios_base::binary); + + video::SColor *bgcolor = new video::SColor(readARGB8(is)); + std::string *type = new std::string(deSerializeString(is)); + u16 count = readU16(is); + std::vector *params = new std::vector; + for(size_t i=0; ipush_back(deSerializeString(is)); + + ClientEvent event; + event.type = CE_SET_SKY; + event.set_sky.bgcolor = bgcolor; + event.set_sky.type = type; + event.set_sky.params = params; + m_client_event_queue.push_back(event); + } + else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO) + { + std::string datastring((char *)&data[2], datasize - 2); + std::istringstream is(datastring, std::ios_base::binary); + + bool do_override = readU8(is); + float day_night_ratio_f = (float)readU16(is) / 65536; + + ClientEvent event; + event.type = CE_OVERRIDE_DAY_NIGHT_RATIO; + event.override_day_night_ratio.do_override = do_override; + event.override_day_night_ratio.ratio_f = day_night_ratio_f; + m_client_event_queue.push_back(event); + } else { infostream<<"Client: Ignoring unknown command " @@ -2253,7 +2134,7 @@ void Client::sendNodemetaFields(v3s16 p, const std::string &formname, Send(0, data, true); } -void Client::sendInventoryFields(const std::string &formname, +void Client::sendInventoryFields(const std::string &formname, const std::map &fields) { std::ostringstream os(std::ios_base::binary); @@ -2457,7 +2338,7 @@ void Client::sendPlayerPos() writeV3S32(&data[2], position); writeV3S32(&data[2+12], speed); writeS32(&data[2+12+12], pitch); - writeS32(&data[2+12+12+4], yaw); + writeS32(&data[2+12+12+4], yaw); writeU32(&data[2+12+12+4+4], keyPressed); // Send as unreliable Send(0, data, false); @@ -2509,7 +2390,7 @@ void Client::removeNode(v3s16 p) } } -void Client::addNode(v3s16 p, MapNode n) +void Client::addNode(v3s16 p, MapNode n, bool remove_metadata) { TimeTaker timer1("Client::addNode()"); @@ -2518,7 +2399,7 @@ void Client::addNode(v3s16 p, MapNode n) try { //TimeTaker timer3("Client::addNode(): addNodeAndUpdate"); - m_env.getMap().addNodeAndUpdate(p, n, modified_blocks); + m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata); } catch(InvalidPositionException &e) {} @@ -2875,6 +2756,14 @@ ClientEvent Client::getClientEvent() return m_client_event_queue.pop_front(); } +float Client::mediaReceiveProgress() +{ + if (m_media_downloader) + return m_media_downloader->getProgress(); + else + return 1.0; // downloader only exists when not yet done +} + void draw_load_screen(const std::wstring &text, IrrlichtDevice* device, gui::IGUIFont* font, float dtime=0 ,int percent=0, bool clouds=true); @@ -2883,12 +2772,8 @@ void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font) infostream<<"Client::afterContentReceived() started"<rebuildImagesAndTextures(); @@ -2983,3 +2868,31 @@ MtEventManager* Client::getEventManager() return m_event; } +scene::IAnimatedMesh* Client::getMesh(const std::string &filename) +{ + std::map::const_iterator i = + m_mesh_data.find(filename); + if(i == m_mesh_data.end()){ + errorstream<<"Client::getMesh(): Mesh not found: \""<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 data_rw(data.c_str(), data.size()); // Const-incorrect Irrlicht + io::IFileSystem *irrfs = m_device->getFileSystem(); + io::IReadFile *rfile = irrfs->createMemoryReadFile( + *data_rw, data_rw.getSize(), filename.c_str()); + assert(rfile); + scene::IAnimatedMesh *mesh = smgr->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); + return mesh; +} +