aboutsummaryrefslogtreecommitdiff
path: root/src/client.cpp
diff options
context:
space:
mode:
authorQuentin Bazin <quent42340@gmail.com>2018-11-28 20:01:49 +0100
committerSmallJoker <SmallJoker@users.noreply.github.com>2018-11-28 20:01:49 +0100
commit5f1cd555cd9d1c64426e173b30b5b792d211c835 (patch)
tree2c8508467d3bf28d549cce2d25144fa8ef42beae /src/client.cpp
parentddd9317b733857630499972179caebc236b4d991 (diff)
downloadminetest-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.cpp1970
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);
-}