X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fclient.cpp;h=83711688896be6314a905a5b46e2f275d91db38b;hb=5e61f64ce259fe0b23cbb377b44e90a0fbc820d7;hp=0f689a7145971eb08797caf45a5a26cd391bce5b;hpb=f3ad75691aea30d2d68aab19fbfa9031409c39d7;p=oweals%2Fminetest.git diff --git a/src/client.cpp b/src/client.cpp index 0f689a714..837116888 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -22,31 +22,40 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "client.h" +#include "network/clientopcodes.h" +#include "network/connection.h" +#include "network/networkpacket.h" #include "threading/mutex_auto_lock.h" +#include "client/clientevent.h" +#include "client/gameui.h" #include "client/renderingengine.h" +#include "client/tile.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 "modchannels.h" #include "mods.h" #include "profiler.h" +#include "shader.h" #include "gettext.h" #include "clientmap.h" #include "clientmedia.h" #include "version.h" -#include "database-sqlite3.h" +#include "database/database-sqlite3.h" #include "serialization.h" #include "guiscalingfilter.h" #include "script/scripting_client.h" #include "game.h" +#include "chatmessage.h" +#include "translation.h" extern gui::IGUIEnvironment* guienv; @@ -62,11 +71,11 @@ Client::Client( IWritableTextureSource *tsrc, IWritableShaderSource *shsrc, IWritableItemDefManager *itemdef, - IWritableNodeDefManager *nodedef, + NodeDefManager *nodedef, ISoundManager *sound, MtEventManager *event, bool ipv6, - GameUIFlags *game_ui_flags + GameUI *game_ui ): m_tsrc(tsrc), m_shsrc(shsrc), @@ -80,7 +89,7 @@ Client::Client( tsrc, this ), m_particle_manager(&m_env), - m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this), + m_con(new con::Connection(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this)), m_address_name(address_name), m_server_ser_ver(SER_FMT_VER_INVALID), m_last_chat_message_sent(time(NULL)), @@ -88,7 +97,8 @@ Client::Client( m_chosen_auth_mech(AUTH_MECHANISM_NONE), m_media_downloader(new ClientMediaDownloader()), m_state(LC_Created), - m_game_ui_flags(game_ui_flags) + m_game_ui(game_ui), + m_modchannel_mgr(new ModChannelMgr()) { // Add local player m_env.setLocalPlayer(new LocalPlayer(this, playername)); @@ -104,18 +114,38 @@ Client::Client( m_script->setEnv(&m_env); } -void Client::loadMods() +void Client::loadBuiltin() { // Load builtin scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath()); - // If modding is not enabled, don't load mods, just builtin + m_script->loadModFromMemory(BUILTIN_MOD_NAME); +} + +void Client::loadMods() +{ + // Don't permit to load mods twice + if (m_mods_loaded) { + return; + } + + // If modding is not enabled or flavour disable it, don't load mods, just builtin if (!m_modding_enabled) { + warningstream << "Client side mods are disabled by configuration." << std::endl; + return; + } + + if (checkCSMFlavourLimit(CSMFlavourLimit::CSM_FL_LOAD_CLIENT_MODS)) { + warningstream << "Client side mods are disabled by server." << std::endl; + // If mods loading is disabled and builtin integrity is wrong, disconnect user. + if (!checkBuiltinIntegrity()) { + // @TODO disconnect user + } return; } + ClientModConfiguration modconf(getClientModsLuaPath()); m_mods = modconf.getMods(); - std::vector unsatisfied_mods = modconf.getUnsatisfiedMods(); // complain about mods with unsatisfied dependencies if (!modconf.isConsistent()) { modconf.printUnsatisfiedModsError(); @@ -136,6 +166,18 @@ void Client::loadMods() } scanModIntoMemory(mod.name, mod.path); } + + // Load and run "mod" scripts + for (const ModSpec &mod : m_mods) + m_script->loadModFromMemory(mod.name); + + m_mods_loaded = true; +} + +bool Client::checkBuiltinIntegrity() +{ + // @TODO + return true; } void Client::scanModSubfolder(const std::string &mod_name, const std::string &mod_path, @@ -143,9 +185,9 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo { std::string full_path = mod_path + DIR_DELIM + mod_subpath; std::vector mod = fs::GetDirListing(full_path); - for (unsigned int j=0; j < mod.size(); j++){ - std::string filename = mod[j].name; - if (mod[j].dir) { + 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; @@ -155,20 +197,6 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo } } -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"; @@ -216,7 +244,7 @@ bool Client::isShutdown() Client::~Client() { m_shutdown = true; - m_con.Disconnect(); + m_con->Disconnect(); m_mesh_update_thread.stop(); m_mesh_update_thread.wait(); @@ -229,10 +257,8 @@ Client::~Client() delete m_inventory_from_server; // Delete detached inventories - for (std::unordered_map::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 @@ -248,27 +274,18 @@ Client::~Client() void Client::connect(Address address, bool is_local_server) { - DSTACK(FUNCTION_NAME); - initLocalMapSaving(address, m_address_name, is_local_server); - m_con.SetTimeoutMs(0); - m_con.Connect(address); + m_con->SetTimeoutMs(0); + m_con->Connect(address); } void Client::step(float dtime) { - DSTACK(FUNCTION_NAME); - // Limit a bit - if(dtime > 2.0) + if (dtime > 2.0) dtime = 2.0; - if(m_ignore_damage_timer > dtime) - m_ignore_damage_timer -= dtime; - else - m_ignore_damage_timer = 0.0; - m_animation_time += dtime; if(m_animation_time > 60.0) m_animation_time -= 60.0; @@ -301,6 +318,10 @@ void Client::step(float dtime) initial_step = false; } else if(m_state == LC_Created) { + if (m_is_registration_confirmation_state) { + // Waiting confirmation + return; + } float &counter = m_connection_reinit_timer; counter -= dtime; if(counter <= 0.0) { @@ -309,31 +330,7 @@ void Client::step(float dtime) LocalPlayer *myplayer = m_env.getLocalPlayer(); FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment."); - u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ? - CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN; - - if (proto_version_min < 25) { - // Send TOSERVER_INIT_LEGACY - // [0] u16 TOSERVER_INIT_LEGACY - // [2] u8 SER_FMT_VER_HIGHEST_READ - // [3] u8[20] player_name - // [23] u8[28] password (new in some version) - // [51] u16 minimum supported network protocol version (added sometime) - // [53] u16 maximum supported network protocol version (added later than the previous one) - - char pName[PLAYERNAME_SIZE]; - char pPassword[PASSWORD_SIZE]; - memset(pName, 0, PLAYERNAME_SIZE * sizeof(char)); - memset(pPassword, 0, PASSWORD_SIZE * sizeof(char)); - - std::string hashed_password = translate_password(myplayer->getName(), m_password); - snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName()); - snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str()); - - sendLegacyInit(pName, pPassword); - } - if (CLIENT_PROTOCOL_VERSION_MAX >= 25) - sendInit(myplayer->getName()); + sendInit(myplayer->getName()); } // Not connected, return @@ -402,7 +399,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); @@ -415,23 +412,16 @@ void Client::step(float dtime) ClientEnvEvent envEvent = m_env.getClientEnvEvent(); if (envEvent.type == CEE_PLAYER_DAMAGE) { - if (m_ignore_damage_timer <= 0) { - u8 damage = envEvent.player_damage.amount; + u8 damage = envEvent.player_damage.amount; - if (envEvent.player_damage.send_to_server) - sendDamage(damage); + if (envEvent.player_damage.send_to_server) + sendDamage(damage); - // Add to ClientEvent queue - ClientEvent event; - event.type = CE_PLAYER_DAMAGE; - event.player_damage.amount = damage; - m_client_event_queue.push(event); - } - } - // 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); + // Add to ClientEvent queue + ClientEvent *event = new ClientEvent(); + event->type = CE_PLAYER_DAMAGE; + event->player_damage.amount = damage; + m_client_event_queue.push(event); } } @@ -555,15 +545,13 @@ void Client::step(float dtime) Update positions of sounds attached to objects */ { - for(std::unordered_map::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()); } } @@ -627,8 +615,7 @@ 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 \""<createImageFromFile(rfile); - if(!img){ + if (!img) { errorstream<<"Client: Cannot create image from data of " <<"file \""<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[] = { @@ -663,8 +649,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 \""<loadSoundData(name, data); @@ -675,9 +660,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: " <<"\""<loadTranslation(data); + return true; + } + + errorstream << "Client: Don't know how to load file \"" + << filename << "\"" << std::endl; return false; } @@ -731,9 +727,8 @@ void Client::request_media(const std::vector &file_requests) pkt << (u16) (file_requests_size & 0xFFFF); - for(std::vector::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); @@ -764,7 +759,6 @@ void Client::initLocalMapSaving(const Address &address, void Client::ReceiveAll() { - DSTACK(FUNCTION_NAME); u64 start_ms = porting::getTimeMs(); for(;;) { @@ -790,9 +784,8 @@ void Client::ReceiveAll() void Client::Receive() { - DSTACK(FUNCTION_NAME); NetworkPacket pkt; - m_con.Receive(&pkt); + m_con->Receive(&pkt); ProcessData(&pkt); } @@ -807,8 +800,6 @@ inline void Client::handleCommand(NetworkPacket* pkt) */ void Client::ProcessData(NetworkPacket *pkt) { - DSTACK(FUNCTION_NAME); - ToClientCommand command = (ToClientCommand) pkt->getCommand(); u32 sender_peer_id = pkt->getPeerId(); @@ -859,7 +850,7 @@ void Client::ProcessData(NetworkPacket *pkt) void Client::Send(NetworkPacket* pkt) { - m_con.Send(PEER_ID_SERVER, + m_con->Send(PEER_ID_SERVER, serverCommandFactoryTable[pkt->getCommand()].channel, pkt, serverCommandFactoryTable[pkt->getCommand()].reliable); @@ -973,22 +964,6 @@ AuthMechanism Client::choseAuthMech(const u32 mechs) return AUTH_MECHANISM_NONE; } -void Client::sendLegacyInit(const char* playerName, const char* playerPassword) -{ - NetworkPacket pkt(TOSERVER_INIT_LEGACY, - 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2); - - u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ? - CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN; - - pkt << (u8) SER_FMT_VER_HIGHEST_READ; - pkt.putRawString(playerName,PLAYERNAME_SIZE); - pkt.putRawString(playerPassword, PASSWORD_SIZE); - pkt << (u16) proto_version_min << (u16) CLIENT_PROTOCOL_VERSION_MAX; - - Send(&pkt); -} - void Client::sendInit(const std::string &playerName) { NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size())); @@ -996,16 +971,25 @@ void Client::sendInit(const std::string &playerName) // we don't support network compression yet u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE; - u16 proto_version_min = g_settings->getFlag("send_pre_v25_init") ? - CLIENT_PROTOCOL_VERSION_MIN_LEGACY : CLIENT_PROTOCOL_VERSION_MIN; - pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes; - pkt << (u16) proto_version_min << (u16) CLIENT_PROTOCOL_VERSION_MAX; + pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX; pkt << playerName; Send(&pkt); } +void Client::promptConfirmRegistration(AuthMechanism chosen_auth_mechanism) +{ + m_chosen_auth_mech = chosen_auth_mechanism; + m_is_registration_confirmation_state = true; +} + +void Client::confirmRegistration() +{ + m_is_registration_confirmation_state = false; + startAuth(m_chosen_auth_mech); +} + void Client::startAuth(AuthMechanism chosen_auth_mechanism) { m_chosen_auth_mech = chosen_auth_mechanism; @@ -1019,7 +1003,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; @@ -1061,12 +1045,8 @@ void Client::sendDeletedBlocks(std::vector &blocks) pkt << (u8) blocks.size(); - u32 k = 0; - for(std::vector::iterator - j = blocks.begin(); - j != blocks.end(); ++j) { - pkt << *j; - k++; + for (const v3s16 &block : blocks) { + pkt << block; } Send(&pkt); @@ -1088,9 +1068,8 @@ void Client::sendRemovedSounds(std::vector &soundList) pkt << (u16) (server_ids & 0xFFFF); - for(std::vector::iterator i = soundList.begin(); - i != soundList.end(); ++i) - pkt << *i; + for (int sound_id : soundList) + pkt << sound_id; Send(&pkt); } @@ -1205,64 +1184,28 @@ void Client::sendChangePassword(const std::string &oldpassword, if (player == NULL) return; - std::string playername = player->getName(); - if (m_proto_ver >= 25) { - // get into sudo mode and then send new password to server - m_password = oldpassword; - m_new_password = newpassword; - startAuth(choseAuthMech(m_sudo_auth_methods)); - } else { - std::string oldpwd = translate_password(playername, oldpassword); - std::string newpwd = translate_password(playername, newpassword); - - NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE); - - for (u8 i = 0; i < PASSWORD_SIZE; i++) { - pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0); - } - - for (u8 i = 0; i < PASSWORD_SIZE; i++) { - pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0); - } - Send(&pkt); - } + // get into sudo mode and then send new password to server + m_password = oldpassword; + m_new_password = newpassword; + startAuth(choseAuthMech(m_sudo_auth_methods)); } void Client::sendDamage(u8 damage) { - DSTACK(FUNCTION_NAME); - NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8)); pkt << damage; Send(&pkt); } -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); -} - void Client::sendRespawn() { - DSTACK(FUNCTION_NAME); - NetworkPacket pkt(TOSERVER_RESPAWN, 0); Send(&pkt); } void Client::sendReady() { - DSTACK(FUNCTION_NAME); - NetworkPacket pkt(TOSERVER_CLIENT_READY, 1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash)); @@ -1276,7 +1219,7 @@ void Client::sendReady() void Client::sendPlayerPos() { LocalPlayer *myplayer = m_env.getLocalPlayer(); - if(myplayer == NULL) + if (!myplayer) return; ClientMap &map = m_env.getClientMap(); @@ -1302,20 +1245,6 @@ void Client::sendPlayerPos() myplayer->last_camera_fov = camera_fov; myplayer->last_wanted_range = wanted_range; - //infostream << "Sending Player Position information" << std::endl; - - u16 our_peer_id; - { - //MutexAutoLock lock(m_con_mutex); //bulk comment-out - our_peer_id = m_con.GetPeerID(); - } - - // Set peer id if not set already - if(myplayer->peer_id == PEER_ID_INEXISTENT) - myplayer->peer_id = our_peer_id; - - assert(myplayer->peer_id == our_peer_id); - NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1); writePlayerPos(myplayer, &map, &pkt); @@ -1326,16 +1255,9 @@ void Client::sendPlayerPos() void Client::sendPlayerItem(u16 item) { LocalPlayer *myplayer = m_env.getLocalPlayer(); - if(myplayer == NULL) + if (!myplayer) return; - u16 our_peer_id = m_con.GetPeerID(); - - // Set peer id if not set already - if(myplayer->peer_id == PEER_ID_INEXISTENT) - myplayer->peer_id = our_peer_id; - assert(myplayer->peer_id == our_peer_id); - NetworkPacket pkt(TOSERVER_PLAYERITEM, 2); pkt << item; @@ -1353,15 +1275,27 @@ void Client::removeNode(v3s16 p) catch(InvalidPositionException &e) { } - for(std::map::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); } @@ -1378,10 +1312,8 @@ void Client::addNode(v3s16 p, MapNode n, bool remove_metadata) catch(InvalidPositionException &e) { } - for(std::map::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); } } @@ -1518,19 +1450,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 @@ -1542,14 +1496,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)); } } } @@ -1627,22 +1580,32 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur } } -ClientEvent Client::getClientEvent() +ClientEvent *Client::getClientEvent() { FATAL_ERROR_IF(m_client_event_queue.empty(), "Cannot getClientEvent, queue is empty."); - ClientEvent event = m_client_event_queue.front(); + ClientEvent *event = m_client_event_queue.front(); m_client_event_queue.pop(); return event; } +bool Client::connectedToServer() +{ + return m_con->Connected(); +} + +const Address Client::getServerAddress() +{ + return m_con->GetPeerAddress(PEER_ID_SERVER); +} + 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 { @@ -1709,9 +1672,8 @@ void Client::afterContentReceived() text = wgettext("Initializing nodes..."); 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)) - m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt"); + for (const auto &path : getTextureDirs()) + m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt"); m_nodedef->setNodeRegistrationStatus(true); m_nodedef->runNodeResolveCallbacks(); delete[] text; @@ -1736,7 +1698,6 @@ void Client::afterContentReceived() if (g_settings->getBool("enable_client_modding")) { m_script->on_client_ready(m_env.getLocalPlayer()); - m_script->on_connect(); } text = wgettext("Done!"); @@ -1747,13 +1708,13 @@ void Client::afterContentReceived() float Client::getRTT() { - return m_con.getPeerStat(PEER_ID_SERVER,con::AVG_RTT); + return m_con->getPeerStat(PEER_ID_SERVER,con::AVG_RTT); } float Client::getCurRate() { - return (m_con.getLocalStat(con::CUR_INC_RATE) + - m_con.getLocalStat(con::CUR_DL_RATE)); + return (m_con->getLocalStat(con::CUR_INC_RATE) + + m_con->getLocalStat(con::CUR_DL_RATE)); } void Client::makeScreenshot() @@ -1806,7 +1767,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(); } @@ -1820,34 +1782,14 @@ bool Client::shouldShowMinimap() const return !m_minimap_disabled_by_server; } -void Client::showGameChat(const bool show) +void Client::pushToEventQueue(ClientEvent *event) { - m_game_ui_flags->show_chat = show; -} - -void Client::showGameHud(const bool show) -{ - m_game_ui_flags->show_hud = show; + m_client_event_queue.push(event); } 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; + m_game_ui->showMinimap(show); } // IGameDef interface @@ -1856,7 +1798,7 @@ IItemDefManager* Client::getItemDefManager() { return m_itemdef; } -INodeDefManager* Client::getNodeDefManager() +const NodeDefManager* Client::getNodeDefManager() { return m_nodedef; } @@ -1896,7 +1838,7 @@ ParticleManager* Client::getParticleManager() return &m_particle_manager; } -scene::IAnimatedMesh* Client::getMesh(const std::string &filename) +scene::IAnimatedMesh* Client::getMesh(const std::string &filename, bool cache) { StringMap::const_iterator it = m_mesh_data.find(filename); if (it == m_mesh_data.end()) { @@ -1915,10 +1857,9 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename) 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(); - RenderingEngine::get_mesh_cache()->removeMesh(mesh); + if (!cache) + RenderingEngine::get_mesh_cache()->removeMesh(mesh); return mesh; } @@ -1960,3 +1901,57 @@ std::string Client::getModStoragePath() const { return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage"; } + +/* + * Mod channels + */ + +bool Client::joinModChannel(const std::string &channel) +{ + if (m_modchannel_mgr->channelRegistered(channel)) + return false; + + NetworkPacket pkt(TOSERVER_MODCHANNEL_JOIN, 2 + channel.size()); + pkt << channel; + Send(&pkt); + + m_modchannel_mgr->joinChannel(channel, 0); + return true; +} + +bool Client::leaveModChannel(const std::string &channel) +{ + if (!m_modchannel_mgr->channelRegistered(channel)) + return false; + + NetworkPacket pkt(TOSERVER_MODCHANNEL_LEAVE, 2 + channel.size()); + pkt << channel; + Send(&pkt); + + m_modchannel_mgr->leaveChannel(channel, 0); + return true; +} + +bool Client::sendModChannelMessage(const std::string &channel, const std::string &message) +{ + if (!m_modchannel_mgr->canWriteOnChannel(channel)) + return false; + + if (message.size() > STRING_MAX_LEN) { + warningstream << "ModChannel message too long, dropping before sending " + << " (" << message.size() << " > " << STRING_MAX_LEN << ", channel: " + << channel << ")" << std::endl; + return false; + } + + // @TODO: do some client rate limiting + NetworkPacket pkt(TOSERVER_MODCHANNEL_MSG, 2 + channel.size() + 2 + message.size()); + pkt << channel << message; + Send(&pkt); + return true; +} + +ModChannel* Client::getModChannel(const std::string &channel) +{ + return m_modchannel_mgr->getModChannel(channel); +}