diff options
Diffstat (limited to 'src/client.cpp')
-rw-r--r-- | src/client.cpp | 597 |
1 files changed, 306 insertions, 291 deletions
diff --git a/src/client.cpp b/src/client.cpp index 5476aad0e..a5228132d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -32,172 +32,33 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include "network/clientopcodes.h" #include "filesys.h" -#include "porting.h" #include "mapblock_mesh.h" #include "mapblock.h" #include "minimap.h" -#include "settings.h" +#include "mods.h" #include "profiler.h" #include "gettext.h" -#include "log.h" -#include "nodemetadata.h" -#include "itemdef.h" -#include "shader.h" #include "clientmap.h" #include "clientmedia.h" -#include "sound.h" -#include "IMeshCache.h" -#include "config.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" extern gui::IGUIEnvironment* guienv; /* - QueuedMeshUpdate -*/ - -QueuedMeshUpdate::QueuedMeshUpdate(): - p(-1337,-1337,-1337), - data(NULL), - ack_block_to_server(false) -{ -} - -QueuedMeshUpdate::~QueuedMeshUpdate() -{ - if(data) - delete data; -} - -/* - MeshUpdateQueue -*/ - -MeshUpdateQueue::MeshUpdateQueue() -{ -} - -MeshUpdateQueue::~MeshUpdateQueue() -{ - MutexAutoLock lock(m_mutex); - - for(std::vector<QueuedMeshUpdate*>::iterator - i = m_queue.begin(); - i != m_queue.end(); ++i) - { - QueuedMeshUpdate *q = *i; - delete q; - } -} - -/* - peer_id=0 adds with nobody to send to -*/ -void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent) -{ - DSTACK(FUNCTION_NAME); - - assert(data); // pre-condition - - MutexAutoLock lock(m_mutex); - - if(urgent) - m_urgents.insert(p); - - /* - Find if block is already in queue. - If it is, update the data and quit. - */ - for(std::vector<QueuedMeshUpdate*>::iterator - i = m_queue.begin(); - i != m_queue.end(); ++i) - { - QueuedMeshUpdate *q = *i; - if(q->p == p) - { - if(q->data) - delete q->data; - q->data = data; - if(ack_block_to_server) - q->ack_block_to_server = true; - return; - } - } - - /* - Add the block - */ - QueuedMeshUpdate *q = new QueuedMeshUpdate; - q->p = p; - q->data = data; - q->ack_block_to_server = ack_block_to_server; - m_queue.push_back(q); -} - -// Returned pointer must be deleted -// Returns NULL if queue is empty -QueuedMeshUpdate *MeshUpdateQueue::pop() -{ - MutexAutoLock lock(m_mutex); - - bool must_be_urgent = !m_urgents.empty(); - for(std::vector<QueuedMeshUpdate*>::iterator - i = m_queue.begin(); - i != m_queue.end(); ++i) - { - QueuedMeshUpdate *q = *i; - if(must_be_urgent && m_urgents.count(q->p) == 0) - continue; - m_queue.erase(i); - m_urgents.erase(q->p); - return q; - } - return NULL; -} - -/* - MeshUpdateThread -*/ - -void MeshUpdateThread::enqueueUpdate(v3s16 p, MeshMakeData *data, - bool ack_block_to_server, bool urgent) -{ - m_queue_in.addBlock(p, data, ack_block_to_server, urgent); - deferUpdate(); -} - -void MeshUpdateThread::doUpdate() -{ - QueuedMeshUpdate *q; - while ((q = m_queue_in.pop())) { - - ScopeProfiler sp(g_profiler, "Client: Mesh making"); - - MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset); - - MeshUpdateResult r; - r.p = q->p; - r.mesh = mesh_new; - r.ack_block_to_server = q->ack_block_to_server; - - m_queue_out.push_back(r); - - delete q; - } -} - -/* Client */ Client::Client( IrrlichtDevice *device, const char *playername, - std::string password, + const std::string &password, + const std::string &address_name, MapDrawControl &control, IWritableTextureSource *tsrc, IWritableShaderSource *shsrc, @@ -205,7 +66,8 @@ Client::Client( IWritableNodeDefManager *nodedef, ISoundManager *sound, MtEventManager *event, - bool ipv6 + bool ipv6, + GameUIFlags *game_ui_flags ): m_packetcounter_timer(0.0), m_connection_reinit_timer(0.1), @@ -218,9 +80,9 @@ Client::Client( m_nodedef(nodedef), m_sound(sound), m_event(event), - m_mesh_update_thread(), + m_mesh_update_thread(this), m_env( - new ClientMap(this, this, control, + new ClientMap(this, control, device->getSceneManager()->getRootSceneNode(), device->getSceneManager(), 666), device->getSceneManager(), @@ -228,8 +90,10 @@ Client::Client( ), m_particle_manager(&m_env), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this), + m_address_name(address_name), m_device(device), m_camera(NULL), + m_minimap(NULL), m_minimap_disabled_by_server(false), m_server_ser_ver(SER_FMT_VER_INVALID), m_proto_ver(0), @@ -240,6 +104,8 @@ Client::Client( m_animation_time(0), m_crack_level(-1), m_crack_pos(0,0,0), + m_last_chat_message_sent(time(NULL)), + m_chat_message_allowance(5.0f), m_map_seed(0), m_password(password), m_chosen_auth_mech(AUTH_MECHANISM_NONE), @@ -255,23 +121,95 @@ Client::Client( m_recommended_send_interval(0.1), m_removed_sounds_check_timer(0), m_state(LC_Created), - m_localdb(NULL) + m_localdb(NULL), + m_script(NULL), + m_mod_storage_save_timer(10.0f), + m_game_ui_flags(game_ui_flags), + m_shutdown(false) { // Add local player m_env.setLocalPlayer(new LocalPlayer(this, playername)); - m_mapper = new Mapper(device, this); + if (g_settings->getBool("enable_minimap")) { + m_minimap = new Minimap(device, this); + } m_cache_save_interval = g_settings->getU16("server_map_save_interval"); - m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); - m_cache_enable_shaders = g_settings->getBool("enable_shaders"); - m_cache_use_tangent_vertices = m_cache_enable_shaders && ( - g_settings->getBool("enable_bumpmapping") || - g_settings->getBool("enable_parallax_occlusion")); + m_modding_enabled = g_settings->getBool("enable_client_modding"); + m_script = new ClientScripting(this); + m_env.setScript(m_script); + m_script->setEnv(&m_env); +} + +void Client::initMods() +{ + m_script->loadMod(getBuiltinLuaPath() + DIR_DELIM "init.lua", BUILTIN_MOD_NAME); + + // 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(); + std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods(); + // complain about mods with unsatisfied dependencies + if (!modconf.isConsistent()) { + modconf.printUnsatisfiedModsError(); + } + + // Print mods + infostream << "Client Loading mods: "; + for (std::vector<ModSpec>::const_iterator i = mods.begin(); + i != mods.end(); ++i) { + infostream << (*i).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; + 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); + } +} + +const std::string &Client::getBuiltinLuaPath() +{ + static const std::string builtin_dir = porting::path_share + DIR_DELIM + "builtin"; + return builtin_dir; +} + +const std::string &Client::getClientModsLuaPath() +{ + static const std::string clientmods_dir = porting::path_share + DIR_DELIM + "clientmods"; + return clientmods_dir; +} + +const std::vector<ModSpec>& Client::getMods() const +{ + static std::vector<ModSpec> client_modspec_temp; + return client_modspec_temp; +} + +const ModSpec* Client::getModSpec(const std::string &modname) const +{ + return NULL; } void Client::Stop() { + m_shutdown = true; + // Don't disable this part when modding is disabled, it's used in builtin + m_script->on_shutdown(); //request all client managed threads to stop m_mesh_update_thread.stop(); // Save local server map @@ -279,18 +217,18 @@ void Client::Stop() infostream << "Local map saving ended." << std::endl; m_localdb->endSave(); } + + delete m_script; } bool Client::isShutdown() { - - if (!m_mesh_update_thread.isRunning()) return true; - - return false; + return m_shutdown || !m_mesh_update_thread.isRunning(); } Client::~Client() { + m_shutdown = true; m_con.Disconnect(); m_mesh_update_thread.stop(); @@ -319,16 +257,14 @@ Client::~Client() m_device->getSceneManager()->getMeshCache()->removeMesh(mesh); } - delete m_mapper; + delete m_minimap; } -void Client::connect(Address address, - const std::string &address_name, - bool is_local_server) +void Client::connect(Address address, bool is_local_server) { DSTACK(FUNCTION_NAME); - initLocalMapSaving(address, address_name, is_local_server); + initLocalMapSaving(address, m_address_name, is_local_server); m_con.SetTimeoutMs(0); m_con.Connect(address); @@ -467,6 +403,14 @@ void Client::step(float dtime) } /* + Send pending messages on out chat queue + */ + if (!m_out_chat_queue.empty() && canSendChatMessage()) { + sendChatMessage(m_out_chat_queue.front()); + m_out_chat_queue.pop(); + } + + /* Handle environment */ // Control local player (0ms) @@ -476,20 +420,19 @@ void Client::step(float dtime) // Step environment m_env.step(dtime); + m_sound->step(dtime); /* Get events */ - for(;;) { - ClientEnvEvent event = m_env.getClientEvent(); - if(event.type == CEE_NONE) { - break; - } - else if(event.type == CEE_PLAYER_DAMAGE) { - if(m_ignore_damage_timer <= 0) { - u8 damage = event.player_damage.amount; + while (m_env.hasClientEnvEvents()) { + ClientEnvEvent envEvent = m_env.getClientEnvEvent(); + + if (envEvent.type == CEE_PLAYER_DAMAGE) { + if (m_ignore_damage_timer <= 0) { + u8 damage = envEvent.player_damage.amount; - if(event.player_damage.send_to_server) + if (envEvent.player_damage.send_to_server) sendDamage(damage); // Add to ClientEvent queue @@ -499,9 +442,10 @@ void Client::step(float dtime) m_client_event_queue.push(event); } } - else if(event.type == CEE_PLAYER_BREATH) { - u16 breath = event.player_breath.amount; - sendBreath(breath); + // Protocol v29 or greater obsoleted this event + else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) { + u16 breath = envEvent.player_breath.amount; + sendBreath(breath); } } @@ -555,20 +499,24 @@ void Client::step(float dtime) minimap_mapblock = r.mesh->moveMinimapMapblock(); if (minimap_mapblock == NULL) do_mapper_update = false; - } - if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) { - delete r.mesh; - } else { - // Replace with the new mesh - block->mesh = r.mesh; + bool is_empty = true; + for (int l = 0; l < MAX_TILE_LAYERS; l++) + if (r.mesh->getMesh(l)->getMeshBufferCount() != 0) + is_empty = false; + + if (is_empty) + delete r.mesh; + else + // Replace with the new mesh + block->mesh = r.mesh; } } else { delete r.mesh; } - if (do_mapper_update) - m_mapper->addBlock(r.p, minimap_mapblock); + if (m_minimap && do_mapper_update) + m_minimap->addBlock(r.p, minimap_mapblock); if (r.ack_block_to_server) { /* @@ -590,7 +538,6 @@ void Client::step(float dtime) 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; } @@ -663,6 +610,18 @@ void Client::step(float dtime) } } + m_mod_storage_save_timer -= dtime; + if (m_mod_storage_save_timer <= 0.0f) { + verbosestream << "Saving registered mod storages." << std::endl; + m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval"); + for (UNORDERED_MAP<std::string, ModMetadata *>::const_iterator + it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { + if (it->second->isModified()) { + it->second->save(getModStoragePath()); + } + } + } + // Write server map if (m_localdb && m_localdb_save_interval.step(dtime, m_cache_save_interval)) { @@ -799,14 +758,6 @@ void Client::request_media(const std::vector<std::string> &file_requests) << file_requests.size() << " files. packet size)" << std::endl; } -void Client::received_media() -{ - NetworkPacket pkt(TOSERVER_RECEIVED_MEDIA, 0); - Send(&pkt); - infostream << "Client: Notifying server that we received all media" - << std::endl; -} - void Client::initLocalMapSaving(const Address &address, const std::string &hostname, bool is_local_server) @@ -822,7 +773,7 @@ void Client::initLocalMapSaving(const Address &address, fs::CreateAllDirs(world_path); - m_localdb = new Database_SQLite3(world_path); + m_localdb = new MapDatabaseSQLite3(world_path); m_localdb->beginSave(); actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl; } @@ -830,7 +781,7 @@ void Client::initLocalMapSaving(const Address &address, void Client::ReceiveAll() { DSTACK(FUNCTION_NAME); - u32 start_ms = porting::getTimeMs(); + u64 start_ms = porting::getTimeMs(); for(;;) { // Limit time even if there would be huge amounts of data to @@ -1217,13 +1168,50 @@ void Client::sendInventoryAction(InventoryAction *a) Send(&pkt); } +bool Client::canSendChatMessage() const +{ + u32 now = time(NULL); + float time_passed = now - m_last_chat_message_sent; + + float virt_chat_message_allowance = m_chat_message_allowance + time_passed * + (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f); + + if (virt_chat_message_allowance < 1.0f) + return false; + + return true; +} + void Client::sendChatMessage(const std::wstring &message) { - NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16)); + const s16 max_queue_size = g_settings->getS16("max_out_chat_queue_size"); + if (canSendChatMessage()) { + u32 now = time(NULL); + float time_passed = now - m_last_chat_message_sent; + m_last_chat_message_sent = time(NULL); - pkt << message; + m_chat_message_allowance += time_passed * (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f); + if (m_chat_message_allowance > CLIENT_CHAT_MESSAGE_LIMIT_PER_10S) + m_chat_message_allowance = CLIENT_CHAT_MESSAGE_LIMIT_PER_10S; - Send(&pkt); + m_chat_message_allowance -= 1.0f; + + NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16)); + + pkt << message; + + Send(&pkt); + } else if (m_out_chat_queue.size() < (u16) max_queue_size || max_queue_size == -1) { + m_out_chat_queue.push(message); + } else { + infostream << "Could not queue chat message because maximum out chat queue size (" + << max_queue_size << ") is reached." << std::endl; + } +} + +void Client::clearOutChatQueue() +{ + m_out_chat_queue = std::queue<std::wstring>(); } void Client::sendChangePassword(const std::string &oldpassword, @@ -1270,6 +1258,10 @@ void Client::sendBreath(u16 breath) { DSTACK(FUNCTION_NAME); + // Protocol v29 make this obsolete + if (m_proto_ver >= 29) + return; + NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16)); pkt << breath; Send(&pkt); @@ -1384,6 +1376,11 @@ void Client::removeNode(v3s16 p) } } +MapNode Client::getNode(v3s16 p, bool *is_valid_position) +{ + return m_env.getMap().getNodeNoEx(p, is_valid_position); +} + void Client::addNode(v3s16 p, MapNode n, bool remove_metadata) { //TimeTaker timer1("Client::addNode()"); @@ -1495,44 +1492,6 @@ void Client::inventoryAction(InventoryAction *a) delete a; } -ClientActiveObject * Client::getSelectedActiveObject( - f32 max_d, - v3f from_pos_f_on_map, - core::line3d<f32> shootline_on_map - ) -{ - std::vector<DistanceSortedActiveObject> objects; - - m_env.getActiveObjects(from_pos_f_on_map, max_d, objects); - - // Sort them. - // After this, the closest object is the first in the array. - std::sort(objects.begin(), objects.end()); - - for(unsigned int i=0; i<objects.size(); i++) - { - ClientActiveObject *obj = objects[i].obj; - - aabb3f *selection_box = obj->getSelectionBox(); - if(selection_box == NULL) - continue; - - v3f pos = obj->getPosition(); - - aabb3f offsetted_box( - selection_box->MinEdge + pos, - selection_box->MaxEdge + pos - ); - - if(offsetted_box.intersectsWithLine(shootline_on_map)) - { - return obj; - } - } - - return NULL; -} - float Client::getAnimationTime() { return m_animation_time; @@ -1543,6 +1502,11 @@ int Client::getCrackLevel() return m_crack_level; } +v3s16 Client::getCrackPos() +{ + return m_crack_pos; +} + void Client::setCrack(int level, v3s16 pos) { int old_crack_level = m_crack_level; @@ -1585,47 +1549,37 @@ void Client::typeChatMessage(const std::wstring &message) if(message == L"") return; + // If message was ate by script API, don't send it to server + if (m_script->on_sending_message(wide_to_utf8(message))) { + return; + } + // Send to others sendChatMessage(message); // Show locally - if (message[0] == L'/') - { - m_chat_queue.push((std::wstring)L"issued command: " + message); - } - else + if (message[0] != L'/') { - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player != NULL); - std::wstring name = narrow_to_wide(player->getName()); - m_chat_queue.push((std::wstring)L"<" + name + L"> " + message); + // compatibility code + if (m_proto_ver < 29) { + LocalPlayer *player = m_env.getLocalPlayer(); + assert(player != NULL); + std::wstring name = narrow_to_wide(player->getName()); + pushToChatQueue((std::wstring)L"<" + name + L"> " + message); + } } } void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) { + // Check if the block exists to begin with. In the case when a non-existing + // neighbor is automatically added, it may not. In that case we don't want + // to tell the mesh update thread about it. MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p); - if(b == NULL) + if (b == NULL) return; - /* - Create a task to update the mesh of the block - */ - - MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders, - m_cache_use_tangent_vertices); - - { - //TimeTaker timer("data fill"); - // Release: ~0ms - // Debug: 1-6ms, avg=2ms - data->fill(b); - data->setCrack(m_crack_level, m_crack_pos); - data->setSmoothLighting(m_cache_smooth_lighting); - } - - // Add task to queue - m_mesh_update_thread.enqueueUpdate(p, data, ack_to_server, urgent); + m_mesh_update_thread.updateBlock(&m_env.getMap(), p, ack_to_server, urgent); } void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent) @@ -1691,14 +1645,11 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur ClientEvent Client::getClientEvent() { - ClientEvent event; - if (m_client_event_queue.empty()) { - event.type = CE_NONE; - } - else { - event = m_client_event_queue.front(); - m_client_event_queue.pop(); - } + FATAL_ERROR_IF(m_client_event_queue.empty(), + "Cannot getClientEvent, queue is empty."); + + ClientEvent event = m_client_event_queue.front(); + m_client_event_queue.pop(); return event; } @@ -1713,9 +1664,10 @@ float Client::mediaReceiveProgress() typedef struct TextureUpdateArgs { IrrlichtDevice *device; gui::IGUIEnvironment *guienv; - u32 last_time_ms; + u64 last_time_ms; u16 last_percent; const wchar_t* text_base; + ITextureSource *tsrc; } TextureUpdateArgs; void texture_update_progress(void *args, u32 progress, u32 max_progress) @@ -1725,10 +1677,10 @@ void texture_update_progress(void *args, u32 progress, u32 max_progress) // update the loading menu -- if neccessary bool do_draw = false; - u32 time_ms = targs->last_time_ms; + u64 time_ms = targs->last_time_ms; if (cur_percent != targs->last_percent) { targs->last_percent = cur_percent; - time_ms = getTimeMs(); + time_ms = porting::getTimeMs(); // only draw when the user will notice something: do_draw = (time_ms - targs->last_time_ms > 100); } @@ -1737,8 +1689,8 @@ 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, 0, - 72 + (u16) ((18. / 100.) * (double) targs->last_percent)); + draw_load_screen(strm.str(), targs->device, targs->guienv, targs->tsrc, 0, + 72 + (u16) ((18. / 100.) * (double) targs->last_percent), true); } } @@ -1758,21 +1710,21 @@ void Client::afterContentReceived(IrrlichtDevice *device) // Rebuild inherited images and recreate textures infostream<<"- Rebuilding images and textures"<<std::endl; - draw_load_screen(text,device, guienv, 0, 70); + draw_load_screen(text,device, 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, 0, 71); + draw_load_screen(text, device, 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, 0, 72); + draw_load_screen(text, device, 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)) @@ -1786,9 +1738,10 @@ void Client::afterContentReceived(IrrlichtDevice *device) TextureUpdateArgs tu_args; tu_args.device = device; tu_args.guienv = guienv; - tu_args.last_time_ms = getTimeMs(); + tu_args.last_time_ms = porting::getTimeMs(); tu_args.last_percent = 0; tu_args.text_base = wgettext("Initializing nodes"); + tu_args.tsrc = m_tsrc; m_nodedef->updateTextures(this, texture_update_progress, &tu_args); delete[] tu_args.text_base; @@ -1798,29 +1751,29 @@ void Client::afterContentReceived(IrrlichtDevice *device) m_state = LC_Ready; sendReady(); + + if (g_settings->getBool("enable_client_modding")) { + m_script->on_client_ready(m_env.getLocalPlayer()); + m_script->on_connect(); + } + text = wgettext("Done!"); - draw_load_screen(text, device, guienv, 0, 100); + draw_load_screen(text, device, guienv, m_tsrc, 0, 100); infostream<<"Client::afterContentReceived() done"<<std::endl; delete[] text; } -float Client::getRTT(void) +float Client::getRTT() { return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT); } -float Client::getCurRate(void) +float Client::getCurRate() { - return ( m_con.getLocalStat(con::CUR_INC_RATE) + + return (m_con.getLocalStat(con::CUR_INC_RATE) + m_con.getLocalStat(con::CUR_DL_RATE)); } -float Client::getAvgRate(void) -{ - return ( m_con.getLocalStat(con::AVG_INC_RATE) + - m_con.getLocalStat(con::AVG_DL_RATE)); -} - void Client::makeScreenshot(IrrlichtDevice *device) { irr::video::IVideoDriver *driver = device->getVideoDriver(); @@ -1871,7 +1824,7 @@ void Client::makeScreenshot(IrrlichtDevice *device) } else { sstr << "Failed to save screenshot '" << filename << "'"; } - m_chat_queue.push(narrow_to_wide(sstr.str())); + pushToChatQueue(narrow_to_wide(sstr.str())); infostream << sstr.str() << std::endl; image->drop(); } @@ -1880,6 +1833,41 @@ void Client::makeScreenshot(IrrlichtDevice *device) raw_image->drop(); } +bool Client::shouldShowMinimap() const +{ + return !m_minimap_disabled_by_server; +} + +void Client::showGameChat(const bool show) +{ + m_game_ui_flags->show_chat = show; +} + +void Client::showGameHud(const bool show) +{ + m_game_ui_flags->show_hud = show; +} + +void Client::showMinimap(const bool show) +{ + m_game_ui_flags->show_minimap = show; +} + +void Client::showProfiler(const bool show) +{ + m_game_ui_flags->show_profiler_graph = show; +} + +void Client::showGameFog(const bool show) +{ + m_game_ui_flags->force_fog_off = !show; +} + +void Client::showGameDebug(const bool show) +{ + m_game_ui_flags->show_debug = show; +} + // IGameDef interface // Under envlock IItemDefManager* Client::getItemDefManager() @@ -1956,3 +1944,30 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename) smgr->getMeshCache()->removeMesh(mesh); return mesh; } + +bool Client::registerModStorage(ModMetadata *storage) +{ + if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) { + errorstream << "Unable to register same mod storage twice. Storage name: " + << storage->getModName() << std::endl; + return false; + } + + m_mod_storages[storage->getModName()] = storage; + return true; +} + +void Client::unregisterModStorage(const std::string &name) +{ + UNORDERED_MAP<std::string, ModMetadata *>::const_iterator it = m_mod_storages.find(name); + if (it != m_mod_storages.end()) { + // Save unconditionaly on unregistration + it->second->save(getModStoragePath()); + m_mod_storages.erase(name); + } +} + +std::string Client::getModStoragePath() const +{ + return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage"; +} |