diff options
author | Quentin Bazin <quent42340@gmail.com> | 2018-11-28 20:01:49 +0100 |
---|---|---|
committer | SmallJoker <SmallJoker@users.noreply.github.com> | 2018-11-28 20:01:49 +0100 |
commit | 5f1cd555cd9d1c64426e173b30b5b792d211c835 (patch) | |
tree | 2c8508467d3bf28d549cce2d25144fa8ef42beae /src/client.cpp | |
parent | ddd9317b733857630499972179caebc236b4d991 (diff) | |
download | minetest-5f1cd555cd9d1c64426e173b30b5b792d211c835.tar.xz |
Move client-specific files to 'src/client' (#7902)
Update Android.mk
Remove 'src/client' from include_directories
Diffstat (limited to 'src/client.cpp')
-rw-r--r-- | src/client.cpp | 1970 |
1 files changed, 0 insertions, 1970 deletions
diff --git a/src/client.cpp b/src/client.cpp deleted file mode 100644 index 17ee3a10a..000000000 --- a/src/client.cpp +++ /dev/null @@ -1,1970 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include <iostream> -#include <algorithm> -#include <sstream> -#include <cmath> -#include <IFileSystem.h> -#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/sound.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 "filesys.h" -#include "mapblock_mesh.h" -#include "mapblock.h" -#include "minimap.h" -#include "modchannels.h" -#include "content/mods.h" -#include "profiler.h" -#include "shader.h" -#include "gettext.h" -#include "clientmap.h" -#include "clientmedia.h" -#include "version.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; - -/* - Client -*/ - -Client::Client( - const char *playername, - const std::string &password, - const std::string &address_name, - MapDrawControl &control, - IWritableTextureSource *tsrc, - IWritableShaderSource *shsrc, - IWritableItemDefManager *itemdef, - NodeDefManager *nodedef, - ISoundManager *sound, - MtEventManager *event, - bool ipv6, - GameUI *game_ui -): - m_tsrc(tsrc), - m_shsrc(shsrc), - m_itemdef(itemdef), - m_nodedef(nodedef), - m_sound(sound), - m_event(event), - m_mesh_update_thread(this), - m_env( - new ClientMap(this, control, 666), - tsrc, this - ), - m_particle_manager(&m_env), - 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)), - m_password(password), - m_chosen_auth_mech(AUTH_MECHANISM_NONE), - m_media_downloader(new ClientMediaDownloader()), - m_state(LC_Created), - m_game_ui(game_ui), - m_modchannel_mgr(new ModChannelMgr()) -{ - // Add local player - m_env.setLocalPlayer(new LocalPlayer(this, playername)); - - if (g_settings->getBool("enable_minimap")) { - m_minimap = new Minimap(this); - } - m_cache_save_interval = g_settings->getU16("server_map_save_interval"); - - m_modding_enabled = g_settings->getBool("enable_client_modding"); - // Only create the client script environment if client modding is enabled - if (m_modding_enabled) { - m_script = new ClientScripting(this); - m_env.setScript(m_script); - m_script->setEnv(&m_env); - } -} - -void Client::loadMods() -{ - // Don't load mods twice - if (m_mods_loaded) { - return; - } - - // If client modding is not enabled, don't load client-provided CSM mods or - // builtin. - if (!m_modding_enabled) { - warningstream << "Client side mods are disabled by configuration." << std::endl; - return; - } - - // Load builtin - scanModIntoMemory(BUILTIN_MOD_NAME, getBuiltinLuaPath()); - m_script->loadModFromMemory(BUILTIN_MOD_NAME); - - // If the server has disabled client-provided CSM mod loading, don't load - // client-provided CSM mods. Builtin is loaded so needs verfying. - if (checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOAD_CLIENT_MODS)) { - warningstream << "Client side mods are disabled by server." << std::endl; - // If builtin integrity is wrong, disconnect user - if (!checkBuiltinIntegrity()) { - // @TODO disconnect user - } - return; - } - - ClientModConfiguration modconf(getClientModsLuaPath()); - m_mods = modconf.getMods(); - // complain about mods with unsatisfied dependencies - if (!modconf.isConsistent()) { - modconf.printUnsatisfiedModsError(); - } - - // Print mods - infostream << "Client Loading mods: "; - for (const ModSpec &mod : m_mods) - infostream << mod.name << " "; - infostream << std::endl; - - // Load and run "mod" scripts - 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."); - } - scanModIntoMemory(mod.name, mod.path); - } - - // Load and run "mod" scripts - for (const ModSpec &mod : m_mods) - m_script->loadModFromMemory(mod.name); - - // Run a callback when mods are loaded - m_script->on_mods_loaded(); - m_mods_loaded = true; -} - -bool Client::checkBuiltinIntegrity() -{ - // @TODO - return true; -} - -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; - } -} - -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; - if (m_modding_enabled) - m_script->on_shutdown(); - //request all client managed threads to stop - m_mesh_update_thread.stop(); - // Save local server map - if (m_localdb) { - infostream << "Local map saving ended." << std::endl; - m_localdb->endSave(); - } - - if (m_modding_enabled) - delete m_script; -} - -bool Client::isShutdown() -{ - return m_shutdown || !m_mesh_update_thread.isRunning(); -} - -Client::~Client() -{ - m_shutdown = true; - m_con->Disconnect(); - - deleteAuthData(); - - 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_frontNoEx(); - delete r.mesh; - } - - - delete m_inventory_from_server; - - // Delete detached inventories - for (auto &m_detached_inventorie : m_detached_inventories) { - delete m_detached_inventorie.second; - } - - // cleanup 3d model meshes on client shutdown - while (RenderingEngine::get_mesh_cache()->getMeshCount() != 0) { - scene::IAnimatedMesh *mesh = RenderingEngine::get_mesh_cache()->getMeshByIndex(0); - - if (mesh) - RenderingEngine::get_mesh_cache()->removeMesh(mesh); - } - - delete m_minimap; - delete m_media_downloader; -} - -void Client::connect(Address address, bool is_local_server) -{ - initLocalMapSaving(address, m_address_name, is_local_server); - - m_con->SetTimeoutMs(0); - m_con->Connect(address); -} - -void Client::step(float dtime) -{ - // Limit a bit - if (dtime > 2.0) - dtime = 2.0; - - m_animation_time += dtime; - if(m_animation_time > 60.0) - m_animation_time -= 60.0; - - m_time_of_day_update_timer += dtime; - - ReceiveAll(); - - /* - Packet counter - */ - { - float &counter = m_packetcounter_timer; - counter -= dtime; - if(counter <= 0.0) - { - counter = 20.0; - - infostream << "Client packetcounter (" << m_packetcounter_timer - << "):"<<std::endl; - m_packetcounter.print(infostream); - m_packetcounter.clear(); - } - } - - // UGLY hack to fix 2 second startup delay caused by non existent - // server client startup synchronization in local server or singleplayer mode - static bool initial_step = true; - if (initial_step) { - 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) { - counter = 2.0; - - LocalPlayer *myplayer = m_env.getLocalPlayer(); - FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment."); - - sendInit(myplayer->getName()); - } - - // Not connected, return - return; - } - - /* - Do stuff if connected - */ - - /* - Run Map's timers and unload unused data - */ - const float map_timer_and_unload_dtime = 5.25; - if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { - ScopeProfiler sp(g_profiler, "Client: map timer and unload"); - std::vector<v3s16> deleted_blocks; - m_env.getMap().timerUpdate(map_timer_and_unload_dtime, - g_settings->getFloat("client_unload_unused_data_timeout"), - g_settings->getS32("client_mapblock_limit"), - &deleted_blocks); - - /* - Send info to server - NOTE: This loop is intentionally iterated the way it is. - */ - - std::vector<v3s16>::iterator i = deleted_blocks.begin(); - std::vector<v3s16> sendlist; - for(;;) { - if(sendlist.size() == 255 || i == deleted_blocks.end()) { - if(sendlist.empty()) - break; - /* - [0] u16 command - [2] u8 count - [3] v3s16 pos_0 - [3+6] v3s16 pos_1 - ... - */ - - sendDeletedBlocks(sendlist); - - if(i == deleted_blocks.end()) - break; - - sendlist.clear(); - } - - sendlist.push_back(*i); - ++i; - } - } - - /* - 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) - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player); - player->applyControl(dtime, &m_env); - - // Step environment - m_env.step(dtime); - m_sound->step(dtime); - - /* - Get events - */ - while (m_env.hasClientEnvEvents()) { - ClientEnvEvent envEvent = m_env.getClientEnvEvent(); - - if (envEvent.type == CEE_PLAYER_DAMAGE) { - u8 damage = envEvent.player_damage.amount; - - if (envEvent.player_damage.send_to_server) - sendDamage(damage); - - // Add to ClientEvent queue - ClientEvent *event = new ClientEvent(); - event->type = CE_PLAYER_DAMAGE; - event->player_damage.amount = damage; - m_client_event_queue.push(event); - } - } - - /* - Print some info - */ - float &counter = m_avg_rtt_timer; - counter += dtime; - if(counter >= 10) { - counter = 0.0; - // connectedAndInitialized() is true, peer exists. - float avg_rtt = getRTT(); - infostream << "Client: average rtt: " << avg_rtt << std::endl; - } - - /* - Send player position to server - */ - { - float &counter = m_playerpos_send_timer; - counter += dtime; - if((m_state == LC_Ready) && (counter >= m_recommended_send_interval)) - { - counter = 0.0; - sendPlayerPos(); - } - } - - /* - Replace updated meshes - */ - { - int num_processed_meshes = 0; - while (!m_mesh_update_thread.m_queue_out.empty()) - { - num_processed_meshes++; - - MinimapMapblock *minimap_mapblock = NULL; - bool do_mapper_update = true; - - MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx(); - MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p); - if (block) { - // Delete the old mesh - delete block->mesh; - block->mesh = nullptr; - - if (r.mesh) { - minimap_mapblock = r.mesh->moveMinimapMapblock(); - if (minimap_mapblock == NULL) - do_mapper_update = false; - - 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 (m_minimap && do_mapper_update) - m_minimap->addBlock(r.p, minimap_mapblock); - - if (r.ack_block_to_server) { - /* - Acknowledge block - [0] u8 count - [1] v3s16 pos_0 - */ - sendGotBlocks(r.p); - } - } - - if (num_processed_meshes > 0) - g_profiler->graphAdd("num_processed_meshes", num_processed_meshes); - } - - /* - Load fetched media - */ - if (m_media_downloader && m_media_downloader->isStarted()) { - m_media_downloader->step(this); - if (m_media_downloader->isDone()) { - delete m_media_downloader; - m_media_downloader = NULL; - } - } - - /* - 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.0f; - float count_before = std::floor(m_inventory_from_server_age / interval); - - m_inventory_from_server_age += dtime; - - float count_after = std::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 - m_env.getLocalPlayer()->inventory = *m_inventory_from_server; - m_inventory_updated = true; - } - } - - /* - Update positions of sounds attached to objects - */ - { - 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) - continue; - m_sound->updateSoundPosition(client_id, cao->getPosition()); - } - } - - /* - 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::vector<s32> removed_server_ids; - for (std::unordered_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.push_back(server_id); - } - } - - // Sync to server - if(!removed_server_ids.empty()) { - sendRemovedSounds(removed_server_ids); - } - } - - 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 (std::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)) { - m_localdb->endSave(); - m_localdb->beginSave(); - } -} - -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.empty()) { - verbosestream<<"Client: Attempting to load image " - <<"file \""<<filename<<"\""<<std::endl; - - io::IFileSystem *irrfs = RenderingEngine::get_filesystem(); - video::IVideoDriver *vdrv = RenderingEngine::get_video_driver(); - - // Create an irrlicht memory file - io::IReadFile *rfile = irrfs->createMemoryReadFile( - *data_rw, data_rw.getSize(), "_tempreadfile"); - - FATAL_ERROR_IF(!rfile, "Could not create irrlicht memory file."); - - // 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; - } - - 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.empty()) { - 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.empty()) { - verbosestream<<"Client: Storing model into memory: " - <<"\""<<filename<<"\""<<std::endl; - if(m_mesh_data.count(filename)) - errorstream<<"Multiple models with name \""<<filename.c_str() - <<"\" found; replacing previous model"<<std::endl; - m_mesh_data[filename] = data; - return true; - } - - const char *translate_ext[] = { - ".tr", NULL - }; - name = removeStringEnd(filename, translate_ext); - if (!name.empty()) { - verbosestream << "Client: Loading translation: " - << "\"" << filename << "\"" << std::endl; - g_translations->loadTranslation(data); - return true; - } - - errorstream << "Client: Don't know how to load file \"" - << filename << "\"" << std::endl; - return false; -} - -// Virtual methods from con::PeerHandler -void Client::peerAdded(con::Peer *peer) -{ - infostream << "Client::peerAdded(): peer->id=" - << peer->id << std::endl; -} -void Client::deletingPeer(con::Peer *peer, bool timeout) -{ - infostream << "Client::deletingPeer(): " - "Server Peer is getting deleted " - << "(timeout=" << timeout << ")" << std::endl; - - if (timeout) { - m_access_denied = true; - m_access_denied_reason = gettext("Connection timed out."); - } -} - -/* - u16 command - u16 number of files requested - for each file { - u16 length of name - string name - } -*/ -void Client::request_media(const std::vector<std::string> &file_requests) -{ - std::ostringstream os(std::ios_base::binary); - writeU16(os, TOSERVER_REQUEST_MEDIA); - size_t file_requests_size = file_requests.size(); - - FATAL_ERROR_IF(file_requests_size > 0xFFFF, "Unsupported number of file requests"); - - // Packet dynamicly resized - NetworkPacket pkt(TOSERVER_REQUEST_MEDIA, 2 + 0); - - pkt << (u16) (file_requests_size & 0xFFFF); - - for (const std::string &file_request : file_requests) { - pkt << file_request; - } - - Send(&pkt); - - infostream << "Client: Sending media request list to server (" - << file_requests.size() << " files. packet size)" << std::endl; -} - -void Client::initLocalMapSaving(const Address &address, - const std::string &hostname, - bool is_local_server) -{ - if (!g_settings->getBool("enable_local_map_saving") || is_local_server) { - return; - } - - const std::string world_path = porting::path_user - + DIR_DELIM + "worlds" - + DIR_DELIM + "server_" - + hostname + "_" + std::to_string(address.getPort()); - - fs::CreateAllDirs(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; -} - -void Client::ReceiveAll() -{ - u64 start_ms = porting::getTimeMs(); - for(;;) - { - // Limit time even if there would be huge amounts of data to - // process - if(porting::getTimeMs() > start_ms + 100) - break; - - try { - Receive(); - g_profiler->graphAdd("client_received_packets", 1); - } - catch(con::NoIncomingDataException &e) { - break; - } - catch(con::InvalidIncomingDataException &e) { - infostream<<"Client::ReceiveAll(): " - "InvalidIncomingDataException: what()=" - <<e.what()<<std::endl; - } - } -} - -void Client::Receive() -{ - NetworkPacket pkt; - m_con->Receive(&pkt); - ProcessData(&pkt); -} - -inline void Client::handleCommand(NetworkPacket* pkt) -{ - const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()]; - (this->*opHandle.handler)(pkt); -} - -/* - sender_peer_id given to this shall be quaranteed to be a valid peer -*/ -void Client::ProcessData(NetworkPacket *pkt) -{ - ToClientCommand command = (ToClientCommand) pkt->getCommand(); - u32 sender_peer_id = pkt->getPeerId(); - - //infostream<<"Client: received command="<<command<<std::endl; - m_packetcounter.add((u16)command); - - /* - If this check is removed, be sure to change the queue - system to know the ids - */ - if(sender_peer_id != PEER_ID_SERVER) { - infostream << "Client::ProcessData(): Discarding data not " - "coming from server: peer_id=" << sender_peer_id - << std::endl; - return; - } - - // Command must be handled into ToClientCommandHandler - if (command >= TOCLIENT_NUM_MSG_TYPES) { - infostream << "Client: Ignoring unknown command " - << command << std::endl; - return; - } - - /* - * Those packets are handled before m_server_ser_ver is set, it's normal - * But we must use the new ToClientConnectionState in the future, - * as a byte mask - */ - if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) { - handleCommand(pkt); - return; - } - - if(m_server_ser_ver == SER_FMT_VER_INVALID) { - infostream << "Client: Server serialization" - " format invalid or not initialized." - " Skipping incoming command=" << command << std::endl; - return; - } - - /* - Handle runtime commands - */ - - handleCommand(pkt); -} - -void Client::Send(NetworkPacket* pkt) -{ - m_con->Send(PEER_ID_SERVER, - serverCommandFactoryTable[pkt->getCommand()].channel, - pkt, - serverCommandFactoryTable[pkt->getCommand()].reliable); -} - -// Will fill up 12 + 12 + 4 + 4 + 4 bytes -void writePlayerPos(LocalPlayer *myplayer, ClientMap *clientMap, NetworkPacket *pkt) -{ - v3f pf = myplayer->getPosition() * 100; - v3f sf = myplayer->getSpeed() * 100; - s32 pitch = myplayer->getPitch() * 100; - s32 yaw = myplayer->getYaw() * 100; - u32 keyPressed = myplayer->keyPressed; - // scaled by 80, so that pi can fit into a u8 - u8 fov = clientMap->getCameraFov() * 80; - u8 wanted_range = MYMIN(255, - std::ceil(clientMap->getControl().wanted_range / MAP_BLOCKSIZE)); - - v3s32 position(pf.X, pf.Y, pf.Z); - v3s32 speed(sf.X, sf.Y, sf.Z); - - /* - Format: - [0] v3s32 position*100 - [12] v3s32 speed*100 - [12+12] s32 pitch*100 - [12+12+4] s32 yaw*100 - [12+12+4+4] u32 keyPressed - [12+12+4+4+4] u8 fov*80 - [12+12+4+4+4+1] u8 ceil(wanted_range / MAP_BLOCKSIZE) - */ - *pkt << position << speed << pitch << yaw << keyPressed; - *pkt << fov << wanted_range; -} - -void Client::interact(u8 action, const PointedThing& pointed) -{ - if(m_state != LC_Ready) { - errorstream << "Client::interact() " - "Canceled (not connected)" - << std::endl; - return; - } - - LocalPlayer *myplayer = m_env.getLocalPlayer(); - if (myplayer == NULL) - return; - - /* - [0] u16 command - [2] u8 action - [3] u16 item - [5] u32 length of the next item (plen) - [9] serialized PointedThing - [9 + plen] player position information - actions: - 0: start digging (from undersurface) or use - 1: stop digging (all parameters ignored) - 2: digging completed - 3: place block or item (to abovesurface) - 4: use item - 5: perform secondary action of item - */ - - NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0); - - pkt << action; - pkt << (u16)getPlayerItem(); - - std::ostringstream tmp_os(std::ios::binary); - pointed.serialize(tmp_os); - - pkt.putLongString(tmp_os.str()); - - writePlayerPos(myplayer, &m_env.getClientMap(), &pkt); - - Send(&pkt); -} - -void Client::deleteAuthData() -{ - if (!m_auth_data) - return; - - switch (m_chosen_auth_mech) { - case AUTH_MECHANISM_FIRST_SRP: - break; - case AUTH_MECHANISM_SRP: - case AUTH_MECHANISM_LEGACY_PASSWORD: - srp_user_delete((SRPUser *) m_auth_data); - m_auth_data = NULL; - break; - case AUTH_MECHANISM_NONE: - break; - } - m_chosen_auth_mech = AUTH_MECHANISM_NONE; -} - - -AuthMechanism Client::choseAuthMech(const u32 mechs) -{ - if (mechs & AUTH_MECHANISM_SRP) - return AUTH_MECHANISM_SRP; - - if (mechs & AUTH_MECHANISM_FIRST_SRP) - return AUTH_MECHANISM_FIRST_SRP; - - if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD) - return AUTH_MECHANISM_LEGACY_PASSWORD; - - return AUTH_MECHANISM_NONE; -} - -void Client::sendInit(const std::string &playerName) -{ - NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size())); - - // we don't support network compression yet - u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE; - - pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes; - 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; - - switch (chosen_auth_mechanism) { - case AUTH_MECHANISM_FIRST_SRP: { - // send srp verifier to server - std::string verifier; - std::string salt; - generate_srp_verifier_and_salt(getPlayerName(), m_password, - &verifier, &salt); - - NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0); - resp_pkt << salt << verifier << (u8)((m_password.empty()) ? 1 : 0); - - Send(&resp_pkt); - break; - } - case AUTH_MECHANISM_SRP: - case AUTH_MECHANISM_LEGACY_PASSWORD: { - u8 based_on = 1; - - if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) { - m_password = translate_password(getPlayerName(), m_password); - based_on = 0; - } - - std::string playername_u = lowercase(getPlayerName()); - m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048, - getPlayerName().c_str(), playername_u.c_str(), - (const unsigned char *) m_password.c_str(), - m_password.length(), NULL, NULL); - char *bytes_A = 0; - size_t len_A = 0; - SRP_Result res = srp_user_start_authentication( - (struct SRPUser *) m_auth_data, NULL, NULL, 0, - (unsigned char **) &bytes_A, &len_A); - FATAL_ERROR_IF(res != SRP_OK, "Creating local SRP user failed."); - - NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0); - resp_pkt << std::string(bytes_A, len_A) << based_on; - Send(&resp_pkt); - break; - } - case AUTH_MECHANISM_NONE: - break; // not handled in this method - } -} - -void Client::sendDeletedBlocks(std::vector<v3s16> &blocks) -{ - NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size()); - - pkt << (u8) blocks.size(); - - for (const v3s16 &block : blocks) { - pkt << block; - } - - Send(&pkt); -} - -void Client::sendGotBlocks(v3s16 block) -{ - NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6); - pkt << (u8) 1 << block; - Send(&pkt); -} - -void Client::sendRemovedSounds(std::vector<s32> &soundList) -{ - size_t server_ids = soundList.size(); - assert(server_ids <= 0xFFFF); - - NetworkPacket pkt(TOSERVER_REMOVED_SOUNDS, 2 + server_ids * 4); - - pkt << (u16) (server_ids & 0xFFFF); - - for (int sound_id : soundList) - pkt << sound_id; - - Send(&pkt); -} - -void Client::sendNodemetaFields(v3s16 p, const std::string &formname, - const StringMap &fields) -{ - size_t fields_size = fields.size(); - - FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of nodemeta fields"); - - NetworkPacket pkt(TOSERVER_NODEMETA_FIELDS, 0); - - pkt << p << formname << (u16) (fields_size & 0xFFFF); - - StringMap::const_iterator it; - for (it = fields.begin(); it != fields.end(); ++it) { - const std::string &name = it->first; - const std::string &value = it->second; - pkt << name; - pkt.putLongString(value); - } - - Send(&pkt); -} - -void Client::sendInventoryFields(const std::string &formname, - const StringMap &fields) -{ - size_t fields_size = fields.size(); - FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of inventory fields"); - - NetworkPacket pkt(TOSERVER_INVENTORY_FIELDS, 0); - pkt << formname << (u16) (fields_size & 0xFFFF); - - StringMap::const_iterator it; - for (it = fields.begin(); it != fields.end(); ++it) { - const std::string &name = it->first; - const std::string &value = it->second; - pkt << name; - pkt.putLongString(value); - } - - Send(&pkt); -} - -void Client::sendInventoryAction(InventoryAction *a) -{ - std::ostringstream os(std::ios_base::binary); - - a->serialize(os); - - // Make data buffer - std::string s = os.str(); - - NetworkPacket pkt(TOSERVER_INVENTORY_ACTION, s.size()); - pkt.putRawString(s.c_str(),s.size()); - - 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) -{ - 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); - - 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; - - 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, - const std::string &newpassword) -{ - LocalPlayer *player = m_env.getLocalPlayer(); - if (player == NULL) - return; - - // 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) -{ - NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8)); - pkt << damage; - Send(&pkt); -} - -void Client::sendRespawn() -{ - NetworkPacket pkt(TOSERVER_RESPAWN, 0); - Send(&pkt); -} - -void Client::sendReady() -{ - NetworkPacket pkt(TOSERVER_CLIENT_READY, - 1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash)); - - pkt << (u8) VERSION_MAJOR << (u8) VERSION_MINOR << (u8) VERSION_PATCH - << (u8) 0 << (u16) strlen(g_version_hash); - - pkt.putRawString(g_version_hash, (u16) strlen(g_version_hash)); - Send(&pkt); -} - -void Client::sendPlayerPos() -{ - LocalPlayer *myplayer = m_env.getLocalPlayer(); - if (!myplayer) - return; - - ClientMap &map = m_env.getClientMap(); - - u8 camera_fov = map.getCameraFov(); - u8 wanted_range = map.getControl().wanted_range; - - // Save bandwidth by only updating position when something changed - if(myplayer->last_position == myplayer->getPosition() && - myplayer->last_speed == myplayer->getSpeed() && - myplayer->last_pitch == myplayer->getPitch() && - myplayer->last_yaw == myplayer->getYaw() && - myplayer->last_keyPressed == myplayer->keyPressed && - myplayer->last_camera_fov == camera_fov && - myplayer->last_wanted_range == wanted_range) - return; - - myplayer->last_position = myplayer->getPosition(); - myplayer->last_speed = myplayer->getSpeed(); - myplayer->last_pitch = myplayer->getPitch(); - myplayer->last_yaw = myplayer->getYaw(); - myplayer->last_keyPressed = myplayer->keyPressed; - myplayer->last_camera_fov = camera_fov; - myplayer->last_wanted_range = wanted_range; - - NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1); - - writePlayerPos(myplayer, &map, &pkt); - - Send(&pkt); -} - -void Client::sendPlayerItem(u16 item) -{ - LocalPlayer *myplayer = m_env.getLocalPlayer(); - if (!myplayer) - return; - - NetworkPacket pkt(TOSERVER_PLAYERITEM, 2); - - pkt << item; - - Send(&pkt); -} - -void Client::removeNode(v3s16 p) -{ - std::map<v3s16, MapBlock*> modified_blocks; - - try { - m_env.getMap().removeNodeAndUpdate(p, modified_blocks); - } - catch(InvalidPositionException &e) { - } - - for (const auto &modified_block : modified_blocks) { - addUpdateMeshTaskWithEdge(modified_block.first, false, true); - } -} - -/** - * Helper function for Client Side Modding - * CSM restrictions are 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 (checkCSMRestrictionFlag(CSMRestrictionFlags::CSM_RF_LOOKUP_NODES)) { - v3s16 ppos = floatToInt(m_env.getLocalPlayer()->getPosition(), BS); - if ((u32) ppos.getDistanceFrom(p) > m_csm_restriction_noderange) { - *is_valid_position = false; - return {}; - } - } - return m_env.getMap().getNodeNoEx(p, is_valid_position); -} - -void Client::addNode(v3s16 p, MapNode n, bool remove_metadata) -{ - //TimeTaker timer1("Client::addNode()"); - - std::map<v3s16, MapBlock*> modified_blocks; - - try { - //TimeTaker timer3("Client::addNode(): addNodeAndUpdate"); - m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata); - } - catch(InvalidPositionException &e) { - } - - for (const auto &modified_block : modified_blocks) { - addUpdateMeshTaskWithEdge(modified_block.first, false, true); - } -} - -void Client::setPlayerControl(PlayerControl &control) -{ - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player); - player->control = control; -} - -void Client::selectPlayerItem(u16 item) -{ - m_playeritem = item; - m_inventory_updated = true; - sendPlayerItem(item); -} - -// Returns true if the inventory of the local player has been -// updated from the server. If it is true, it is set to false. -bool Client::getLocalInventoryUpdated() -{ - bool updated = m_inventory_updated; - m_inventory_updated = false; - return updated; -} - -// Copies the inventory of the local player to parameter -void Client::getLocalInventory(Inventory &dst) -{ - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player); - dst = player->inventory; -} - -Inventory* Client::getInventory(const InventoryLocation &loc) -{ - switch(loc.type){ - case InventoryLocation::UNDEFINED: - {} - break; - case InventoryLocation::CURRENT_PLAYER: - { - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player); - return &player->inventory; - } - break; - case InventoryLocation::PLAYER: - { - // Check if we are working with local player inventory - LocalPlayer *player = m_env.getLocalPlayer(); - if (!player || strcmp(player->getName(), loc.name.c_str()) != 0) - return NULL; - return &player->inventory; - } - break; - case InventoryLocation::NODEMETA: - { - NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p); - if(!meta) - return NULL; - return meta->getInventory(); - } - break; - case InventoryLocation::DETACHED: - { - if (m_detached_inventories.count(loc.name) == 0) - return NULL; - return m_detached_inventories[loc.name]; - } - break; - default: - FATAL_ERROR("Invalid inventory location type."); - break; - } - return NULL; -} - -void Client::inventoryAction(InventoryAction *a) -{ - /* - Send it to the server - */ - sendInventoryAction(a); - - /* - Predict some local inventory changes - */ - a->clientApply(this, this); - - // Remove it - delete a; -} - -float Client::getAnimationTime() -{ - return m_animation_time; -} - -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; - v3s16 old_crack_pos = m_crack_pos; - - m_crack_level = level; - m_crack_pos = pos; - - if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos)) - { - // remove old crack - addUpdateMeshTaskForNode(old_crack_pos, false, true); - } - if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos)) - { - // add new crack - addUpdateMeshTaskForNode(pos, false, true); - } -} - -u16 Client::getHP() -{ - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player); - return player->hp; -} - -bool Client::getChatMessage(std::wstring &res) -{ - if (m_chat_queue.empty()) - return false; - - 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.empty()) - return; - - // If message was consumed by script API, don't send it to server - if (m_modding_enabled && m_script->on_sending_message(wide_to_utf8(message))) - return; - - // Send to others - sendChatMessage(message); - - // Show locally - 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(new ChatMessage(CHATMESSAGE_TYPE_NORMAL, message, name)); - } - } -} - -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) - return; - - m_mesh_update_thread.updateBlock(&m_env.getMap(), p, ack_to_server, urgent); -} - -void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent) -{ - try{ - addUpdateMeshTask(blockpos, ack_to_server, urgent); - } - catch(InvalidPositionException &e){} - - // Leading edge - for (int i=0;i<6;i++) - { - try{ - v3s16 p = blockpos + g_6dirs[i]; - addUpdateMeshTask(p, false, urgent); - } - catch(InvalidPositionException &e){} - } -} - -void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent) -{ - { - v3s16 p = nodepos; - infostream<<"Client::addUpdateMeshTaskForNode(): " - <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" - <<std::endl; - } - - v3s16 blockpos = getNodeBlockPos(nodepos); - v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE; - - try{ - addUpdateMeshTask(blockpos, ack_to_server, urgent); - } - catch(InvalidPositionException &e) {} - - // Leading edge - if(nodepos.X == blockpos_relative.X){ - try{ - v3s16 p = blockpos + v3s16(-1,0,0); - addUpdateMeshTask(p, false, urgent); - } - catch(InvalidPositionException &e){} - } - - if(nodepos.Y == blockpos_relative.Y){ - try{ - v3s16 p = blockpos + v3s16(0,-1,0); - addUpdateMeshTask(p, false, urgent); - } - catch(InvalidPositionException &e){} - } - - if(nodepos.Z == blockpos_relative.Z){ - try{ - v3s16 p = blockpos + v3s16(0,0,-1); - addUpdateMeshTask(p, false, urgent); - } - catch(InvalidPositionException &e){} - } -} - -ClientEvent *Client::getClientEvent() -{ - 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; -} - -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(); - - return 1.0; // downloader only exists when not yet done -} - -typedef struct TextureUpdateArgs { - gui::IGUIEnvironment *guienv; - 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) -{ - TextureUpdateArgs* targs = (TextureUpdateArgs*) args; - u16 cur_percent = ceil(progress / (double) max_progress * 100.); - - // update the loading menu -- if neccessary - bool do_draw = false; - u64 time_ms = targs->last_time_ms; - if (cur_percent != targs->last_percent) { - targs->last_percent = cur_percent; - time_ms = porting::getTimeMs(); - // only draw when the user will notice something: - do_draw = (time_ms - targs->last_time_ms > 100); - } - - if (do_draw) { - targs->last_time_ms = time_ms; - std::basic_stringstream<wchar_t> strm; - strm << targs->text_base << " " << targs->last_percent << "%..."; - RenderingEngine::draw_load_screen(strm.str(), targs->guienv, targs->tsrc, 0, - 72 + (u16) ((18. / 100.) * (double) targs->last_percent), true); - } -} - -void Client::afterContentReceived() -{ - infostream<<"Client::afterContentReceived() started"<<std::endl; - assert(m_itemdef_received); // pre-condition - assert(m_nodedef_received); // pre-condition - assert(mediaReceived()); // pre-condition - - const wchar_t* text = wgettext("Loading textures..."); - - // Clear cached pre-scaled 2D GUI images, as this cache - // might have images with the same name but different - // content from previous sessions. - guiScalingCacheClear(); - - // Rebuild inherited images and recreate textures - infostream<<"- Rebuilding images and textures"<<std::endl; - 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..."); - 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..."); - RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 72); - m_nodedef->updateAliases(m_itemdef); - for (const auto &path : getTextureDirs()) - m_nodedef->applyTextureOverrides(path + DIR_DELIM + "override.txt"); - m_nodedef->setNodeRegistrationStatus(true); - m_nodedef->runNodeResolveCallbacks(); - delete[] text; - - // Update node textures and assign shaders to each tile - infostream<<"- Updating node textures"<<std::endl; - TextureUpdateArgs tu_args; - tu_args.guienv = guienv; - 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; - - // Start mesh update thread after setting up content definitions - infostream<<"- Starting mesh update thread"<<std::endl; - m_mesh_update_thread.start(); - - m_state = LC_Ready; - sendReady(); - - if (g_settings->getBool("enable_client_modding")) { - m_script->on_client_ready(m_env.getLocalPlayer()); - } - - text = wgettext("Done!"); - RenderingEngine::draw_load_screen(text, guienv, m_tsrc, 0, 100); - infostream<<"Client::afterContentReceived() done"<<std::endl; - delete[] text; -} - -// returns the Round Trip Time -// if the RTT did not become updated within 2 seconds, e.g. before timing out, -// it returns the expired time instead -float Client::getRTT() -{ - float avg_rtt = m_con->getPeerStat(PEER_ID_SERVER, con::AVG_RTT); - float time_from_last_rtt = - m_con->getPeerStat(PEER_ID_SERVER, con::TIMEOUT_COUNTER); - if (avg_rtt + 2.0f > time_from_last_rtt) - return avg_rtt; - return time_from_last_rtt; -} - -float Client::getCurRate() -{ - return (m_con->getLocalStat(con::CUR_INC_RATE) + - m_con->getLocalStat(con::CUR_DL_RATE)); -} - -void Client::makeScreenshot() -{ - irr::video::IVideoDriver *driver = RenderingEngine::get_video_driver(); - irr::video::IImage* const raw_image = driver->createScreenShot(); - - if (!raw_image) - return; - - time_t t = time(NULL); - struct tm *tm = localtime(&t); - - char timetstamp_c[64]; - strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm); - - std::string filename_base = g_settings->get("screenshot_path") - + DIR_DELIM - + std::string("screenshot_") - + std::string(timetstamp_c); - std::string filename_ext = "." + g_settings->get("screenshot_format"); - std::string filename; - - u32 quality = (u32)g_settings->getS32("screenshot_quality"); - quality = MYMIN(MYMAX(quality, 0), 100) / 100.0 * 255; - - // Try to find a unique filename - unsigned serial = 0; - - while (serial < SCREENSHOT_MAX_SERIAL_TRIES) { - filename = filename_base + (serial > 0 ? ("_" + itos(serial)) : "") + filename_ext; - std::ifstream tmp(filename.c_str()); - if (!tmp.good()) - break; // File did not apparently exist, we'll go with it - serial++; - } - - if (serial == SCREENSHOT_MAX_SERIAL_TRIES) { - infostream << "Could not find suitable filename for screenshot" << std::endl; - } else { - irr::video::IImage* const image = - driver->createImage(video::ECF_R8G8B8, raw_image->getDimension()); - - if (image) { - raw_image->copyTo(image); - - std::ostringstream sstr; - if (driver->writeImageToFile(image, filename.c_str(), quality)) { - sstr << "Saved screenshot to '" << filename << "'"; - } else { - sstr << "Failed to save screenshot '" << filename << "'"; - } - pushToChatQueue(new ChatMessage(CHATMESSAGE_TYPE_SYSTEM, - narrow_to_wide(sstr.str()))); - infostream << sstr.str() << std::endl; - image->drop(); - } - } - - raw_image->drop(); -} - -bool Client::shouldShowMinimap() const -{ - return !m_minimap_disabled_by_server; -} - -void Client::pushToEventQueue(ClientEvent *event) -{ - m_client_event_queue.push(event); -} - -void Client::showMinimap(const bool show) -{ - m_game_ui->showMinimap(show); -} - -// IGameDef interface -// Under envlock -IItemDefManager* Client::getItemDefManager() -{ - return m_itemdef; -} -const NodeDefManager* Client::getNodeDefManager() -{ - return m_nodedef; -} -ICraftDefManager* Client::getCraftDefManager() -{ - return NULL; - //return m_craftdef; -} -ITextureSource* Client::getTextureSource() -{ - return m_tsrc; -} -IShaderSource* Client::getShaderSource() -{ - return m_shsrc; -} - -u16 Client::allocateUnknownNodeId(const std::string &name) -{ - errorstream << "Client::allocateUnknownNodeId(): " - << "Client cannot allocate node IDs" << std::endl; - FATAL_ERROR("Client allocated unknown node"); - - return CONTENT_IGNORE; -} -ISoundManager* Client::getSoundManager() -{ - return m_sound; -} -MtEventManager* Client::getEventManager() -{ - return m_event; -} - -ParticleManager* Client::getParticleManager() -{ - return &m_particle_manager; -} - -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()) { - errorstream << "Client::getMesh(): Mesh not found: \"" << filename - << "\"" << std::endl; - return NULL; - } - const std::string &data = it->second; - - // 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::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 = RenderingEngine::get_scene_manager()->getMesh(rfile); - rfile->drop(); - mesh->grab(); - if (!cache) - 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()) { - 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) -{ - std::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"; -} - -/* - * 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); -} |