Tooltips: Unify the tooltip[] and list[] description tooltip display functions (...
[oweals/minetest.git] / src / client.cpp
index 94c808a57823683e346fc4f09a951294c7335b59..a5228132d629c5eae1927fc5f01cda0e36c0e66b 100644 (file)
@@ -45,7 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "database-sqlite3.h"
 #include "serialization.h"
 #include "guiscalingfilter.h"
-#include "script/clientscripting.h"
+#include "script/scripting_client.h"
 #include "game.h"
 
 extern gui::IGUIEnvironment* guienv;
@@ -58,6 +58,7 @@ Client::Client(
                IrrlichtDevice *device,
                const char *playername,
                const std::string &password,
+               const std::string &address_name,
                MapDrawControl &control,
                IWritableTextureSource *tsrc,
                IWritableShaderSource *shsrc,
@@ -89,8 +90,10 @@ Client::Client(
        ),
        m_particle_manager(&m_env),
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
+       m_address_name(address_name),
        m_device(device),
        m_camera(NULL),
+       m_minimap(NULL),
        m_minimap_disabled_by_server(false),
        m_server_ser_ver(SER_FMT_VER_INVALID),
        m_proto_ver(0),
@@ -101,6 +104,8 @@ Client::Client(
        m_animation_time(0),
        m_crack_level(-1),
        m_crack_pos(0,0,0),
+       m_last_chat_message_sent(time(NULL)),
+       m_chat_message_allowance(5.0f),
        m_map_seed(0),
        m_password(password),
        m_chosen_auth_mech(AUTH_MECHANISM_NONE),
@@ -125,7 +130,9 @@ Client::Client(
        // Add local player
        m_env.setLocalPlayer(new LocalPlayer(this, playername));
 
-       m_minimap = new Minimap(device, this);
+       if (g_settings->getBool("enable_minimap")) {
+               m_minimap = new Minimap(device, this);
+       }
        m_cache_save_interval = g_settings->getU16("server_map_save_interval");
 
        m_modding_enabled = g_settings->getBool("enable_client_modding");
@@ -166,7 +173,7 @@ void Client::initMods()
                if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
                        throw ModError("Error loading mod \"" + mod.name +
                                "\": Mod name does not follow naming conventions: "
-                                       "Only chararacters [a-z0-9_] are allowed.");
+                                       "Only characters [a-z0-9_] are allowed.");
                }
                std::string script_path = mod.path + DIR_DELIM + "init.lua";
                infostream << "  [" << padStringRight(mod.name, 12) << "] [\""
@@ -253,13 +260,11 @@ Client::~Client()
        delete m_minimap;
 }
 
-void Client::connect(Address address,
-               const std::string &address_name,
-               bool is_local_server)
+void Client::connect(Address address, bool is_local_server)
 {
        DSTACK(FUNCTION_NAME);
 
-       initLocalMapSaving(address, address_name, is_local_server);
+       initLocalMapSaving(address, m_address_name, is_local_server);
 
        m_con.SetTimeoutMs(0);
        m_con.Connect(address);
@@ -397,6 +402,14 @@ void Client::step(float dtime)
                }
        }
 
+       /*
+               Send pending messages on out chat queue
+       */
+       if (!m_out_chat_queue.empty() && canSendChatMessage()) {
+               sendChatMessage(m_out_chat_queue.front());
+               m_out_chat_queue.pop();
+       }
+
        /*
                Handle environment
        */
@@ -407,20 +420,19 @@ void Client::step(float dtime)
 
        // Step environment
        m_env.step(dtime);
+       m_sound->step(dtime);
 
        /*
                Get events
        */
-       for(;;) {
-               ClientEnvEvent event = m_env.getClientEvent();
-               if(event.type == CEE_NONE) {
-                       break;
-               }
-               else if(event.type == CEE_PLAYER_DAMAGE) {
-                       if(m_ignore_damage_timer <= 0) {
-                               u8 damage = event.player_damage.amount;
+       while (m_env.hasClientEnvEvents()) {
+               ClientEnvEvent envEvent = m_env.getClientEnvEvent();
 
-                               if(event.player_damage.send_to_server)
+               if (envEvent.type == CEE_PLAYER_DAMAGE) {
+                       if (m_ignore_damage_timer <= 0) {
+                               u8 damage = envEvent.player_damage.amount;
+
+                               if (envEvent.player_damage.send_to_server)
                                        sendDamage(damage);
 
                                // Add to ClientEvent queue
@@ -431,8 +443,8 @@ void Client::step(float dtime)
                        }
                }
                // Protocol v29 or greater obsoleted this event
-               else if (event.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {
-                       u16 breath = event.player_breath.amount;
+               else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) {
+                       u16 breath = envEvent.player_breath.amount;
                        sendBreath(breath);
                }
        }
@@ -503,7 +515,7 @@ void Client::step(float dtime)
                                delete r.mesh;
                        }
 
-                       if (do_mapper_update)
+                       if (m_minimap && do_mapper_update)
                                m_minimap->addBlock(r.p, minimap_mapblock);
 
                        if (r.ack_block_to_server) {
@@ -526,7 +538,6 @@ void Client::step(float dtime)
        if (m_media_downloader && m_media_downloader->isStarted()) {
                m_media_downloader->step(this);
                if (m_media_downloader->isDone()) {
-                       received_media();
                        delete m_media_downloader;
                        m_media_downloader = NULL;
                }
@@ -747,14 +758,6 @@ void Client::request_media(const std::vector<std::string> &file_requests)
                        << file_requests.size() << " files. packet size)" << std::endl;
 }
 
-void Client::received_media()
-{
-       NetworkPacket pkt(TOSERVER_RECEIVED_MEDIA, 0);
-       Send(&pkt);
-       infostream << "Client: Notifying server that we received all media"
-                       << std::endl;
-}
-
 void Client::initLocalMapSaving(const Address &address,
                const std::string &hostname,
                bool is_local_server)
@@ -778,7 +781,7 @@ void Client::initLocalMapSaving(const Address &address,
 void Client::ReceiveAll()
 {
        DSTACK(FUNCTION_NAME);
-       u32 start_ms = porting::getTimeMs();
+       u64 start_ms = porting::getTimeMs();
        for(;;)
        {
                // Limit time even if there would be huge amounts of data to
@@ -1165,13 +1168,50 @@ void Client::sendInventoryAction(InventoryAction *a)
        Send(&pkt);
 }
 
+bool Client::canSendChatMessage() const
+{
+       u32 now = time(NULL);
+       float time_passed = now - m_last_chat_message_sent;
+
+       float virt_chat_message_allowance = m_chat_message_allowance + time_passed *
+                       (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
+
+       if (virt_chat_message_allowance < 1.0f)
+               return false;
+
+       return true;
+}
+
 void Client::sendChatMessage(const std::wstring &message)
 {
-       NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
+       const s16 max_queue_size = g_settings->getS16("max_out_chat_queue_size");
+       if (canSendChatMessage()) {
+               u32 now = time(NULL);
+               float time_passed = now - m_last_chat_message_sent;
+               m_last_chat_message_sent = time(NULL);
 
-       pkt << message;
+               m_chat_message_allowance += time_passed * (CLIENT_CHAT_MESSAGE_LIMIT_PER_10S / 8.0f);
+               if (m_chat_message_allowance > CLIENT_CHAT_MESSAGE_LIMIT_PER_10S)
+                       m_chat_message_allowance = CLIENT_CHAT_MESSAGE_LIMIT_PER_10S;
 
-       Send(&pkt);
+               m_chat_message_allowance -= 1.0f;
+
+               NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
+
+               pkt << message;
+
+               Send(&pkt);
+       } else if (m_out_chat_queue.size() < (u16) max_queue_size || max_queue_size == -1) {
+               m_out_chat_queue.push(message);
+       } else {
+               infostream << "Could not queue chat message because maximum out chat queue size ("
+                               << max_queue_size << ") is reached." << std::endl;
+       }
+}
+
+void Client::clearOutChatQueue()
+{
+       m_out_chat_queue = std::queue<std::wstring>();
 }
 
 void Client::sendChangePassword(const std::string &oldpassword,
@@ -1605,14 +1645,11 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur
 
 ClientEvent Client::getClientEvent()
 {
-       ClientEvent event;
-       if (m_client_event_queue.empty()) {
-               event.type = CE_NONE;
-       }
-       else {
-               event = m_client_event_queue.front();
-               m_client_event_queue.pop();
-       }
+       FATAL_ERROR_IF(m_client_event_queue.empty(),
+                       "Cannot getClientEvent, queue is empty.");
+
+       ClientEvent event = m_client_event_queue.front();
+       m_client_event_queue.pop();
        return event;
 }
 
@@ -1627,7 +1664,7 @@ float Client::mediaReceiveProgress()
 typedef struct TextureUpdateArgs {
        IrrlichtDevice *device;
        gui::IGUIEnvironment *guienv;
-       u32 last_time_ms;
+       u64 last_time_ms;
        u16 last_percent;
        const wchar_t* text_base;
        ITextureSource *tsrc;
@@ -1640,10 +1677,10 @@ void texture_update_progress(void *args, u32 progress, u32 max_progress)
 
                // update the loading menu -- if neccessary
                bool do_draw = false;
-               u32 time_ms = targs->last_time_ms;
+               u64 time_ms = targs->last_time_ms;
                if (cur_percent != targs->last_percent) {
                        targs->last_percent = cur_percent;
-                       time_ms = getTimeMs();
+                       time_ms = porting::getTimeMs();
                        // only draw when the user will notice something:
                        do_draw = (time_ms - targs->last_time_ms > 100);
                }
@@ -1701,7 +1738,7 @@ void Client::afterContentReceived(IrrlichtDevice *device)
        TextureUpdateArgs tu_args;
        tu_args.device = device;
        tu_args.guienv = guienv;
-       tu_args.last_time_ms = getTimeMs();
+       tu_args.last_time_ms = porting::getTimeMs();
        tu_args.last_percent = 0;
        tu_args.text_base =  wgettext("Initializing nodes");
        tu_args.tsrc = m_tsrc;
@@ -1733,7 +1770,7 @@ float Client::getRTT()
 
 float Client::getCurRate()
 {
-       return ( m_con.getLocalStat(con::CUR_INC_RATE) +
+       return (m_con.getLocalStat(con::CUR_INC_RATE) +
                        m_con.getLocalStat(con::CUR_DL_RATE));
 }
 
@@ -1934,4 +1971,3 @@ std::string Client::getModStoragePath() const
 {
        return porting::path_user + DIR_DELIM + "client" + DIR_DELIM + "mod_storage";
 }
-