]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/network/clientpackethandler.cpp
Merge branch 'master' of https://github.com/minetest/minetest
[dragonfireclient.git] / src / network / clientpackethandler.cpp
index 889002a82d21c79c522c3a537b1b5e0d3848fd74..29e3364db0e2d6ef4a50b365832076702820c0be 100644 (file)
@@ -17,9 +17,11 @@ 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 "client/client.h"
 
 #include "util/base64.h"
+#include "client/camera.h"
 #include "chatmessage.h"
 #include "client/clientmedia.h"
 #include "log.h"
@@ -32,14 +34,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "server.h"
 #include "util/strfnd.h"
 #include "client/clientevent.h"
+#include "client/content_cao.h"
 #include "client/sound.h"
 #include "network/clientopcodes.h"
 #include "network/connection.h"
 #include "script/scripting_client.h"
 #include "util/serialize.h"
 #include "util/srp.h"
+#include "util/sha1.h"
 #include "tileanimation.h"
 #include "gettext.h"
+#include "skyparams.h"
+#include <memory>
 
 void Client::handleCommand_Deprecated(NetworkPacket* pkt)
 {
@@ -85,7 +91,7 @@ void Client::handleCommand_Hello(NetworkPacket* pkt)
        // This is only neccessary though when we actually want to add casing support
 
        if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
-               // we recieved a TOCLIENT_HELLO while auth was already going on
+               // we received a TOCLIENT_HELLO while auth was already going on
                errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
                        << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
                if (m_chosen_auth_mech == AUTH_MECHANISM_SRP ||
@@ -134,6 +140,9 @@ void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
                                        << m_recommended_send_interval<<std::endl;
 
        // Reply to server
+       /*~ DO NOT TRANSLATE THIS LITERALLY!
+       This is a special string which needs to contain the translation's
+       language code (e.g. "de" for German). */
        std::string lang = gettext("LANG_CODE");
        if (lang == "LANG_CODE")
                lang = "";
@@ -150,7 +159,7 @@ void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
 
        m_password = m_new_password;
 
-       verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
+       verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
 
        // send packet to actually set the password
        startAuth(AUTH_MECHANISM_FIRST_SRP);
@@ -176,7 +185,7 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
        m_access_denied_reason = "Unknown";
 
        if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
-               // 13/03/15 Legacy code from 0.4.12 and lesser but is still used
+               // Legacy code from 0.4.12 and older but is still used
                // in some places of the server code
                if (pkt->getSize() >= 2) {
                        std::wstring wide_reason;
@@ -189,19 +198,22 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
        if (pkt->getSize() < 1)
                return;
 
-       u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
+       u8 denyCode;
        *pkt >> denyCode;
+
        if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
                        denyCode == SERVER_ACCESSDENIED_CRASH) {
                *pkt >> m_access_denied_reason;
-               if (m_access_denied_reason.empty()) {
+               if (m_access_denied_reason.empty())
                        m_access_denied_reason = accessDeniedStrings[denyCode];
-               }
                u8 reconnect;
                *pkt >> reconnect;
                m_access_denied_reconnect = reconnect & 1;
        } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
                *pkt >> m_access_denied_reason;
+       } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) {
+               m_access_denied_reason = accessDeniedStrings[denyCode];
+               m_access_denied_reconnect = true;
        } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
                m_access_denied_reason = accessDeniedStrings[denyCode];
        } else {
@@ -210,9 +222,8 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
                // Until then (which may be never), this is outside
                // of the defined protocol.
                *pkt >> m_access_denied_reason;
-               if (m_access_denied_reason.empty()) {
+               if (m_access_denied_reason.empty())
                        m_access_denied_reason = "Unknown";
-               }
        }
 }
 
@@ -252,7 +263,7 @@ void Client::handleCommand_NodemetaChanged(NetworkPacket *pkt)
                return;
 
        std::istringstream is(pkt->readLongString(), std::ios::binary);
-       std::stringstream sstr;
+       std::stringstream sstr(std::ios::binary | std::ios::in | std::ios::out);
        decompressZlib(is, sstr);
 
        NodeMetadataList meta_updates_list(false);
@@ -333,7 +344,7 @@ void Client::handleCommand_Inventory(NetworkPacket* pkt)
 
        player->inventory.deSerialize(is);
 
-       m_inventory_updated = true;
+       m_update_wielded_item = true;
 
        delete m_inventory_from_server;
        m_inventory_from_server = new Inventory(player->inventory);
@@ -382,10 +393,10 @@ void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
        m_env.setTimeOfDaySpeed(time_speed);
        m_time_of_day_set = true;
 
-       u32 dr = m_env.getDayNightRatio();
-       infostream << "Client: time_of_day=" << time_of_day
-                       << " time_speed=" << time_speed
-                       << " dr=" << dr << std::endl;
+       //u32 dr = m_env.getDayNightRatio();
+       //infostream << "Client: time_of_day=" << time_of_day
+       //              << " time_speed=" << time_speed
+       //              << " dr=" << dr << std::endl;
 }
 
 void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
@@ -408,12 +419,14 @@ void Client::handleCommand_ChatMessage(NetworkPacket *pkt)
                return;
        }
 
-       *pkt >> chatMessage->sender >> chatMessage->message >> chatMessage->timestamp;
+       u64 timestamp;
+       *pkt >> chatMessage->sender >> chatMessage->message >> timestamp;
+       chatMessage->timestamp = static_cast<std::time_t>(timestamp);
 
        chatMessage->type = (ChatMessageType) message_type;
 
        // @TODO send this to CSM using ChatMessage object
-       if (moddingEnabled() && m_script->on_receiving_message(
+       if (modsLoaded() && m_script->on_receiving_message(
                        wide_to_utf8(chatMessage->message))) {
                // Message was consumed by CSM and should not be handled by client
                delete chatMessage;
@@ -438,6 +451,9 @@ void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
                }
        */
 
+       LocalPlayer *player = m_env.getLocalPlayer();
+       bool try_reattach = player && player->isWaitingForReattach();
+
        try {
                u8 type;
                u16 removed_count, added_count, id;
@@ -456,11 +472,17 @@ void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
                for (u16 i = 0; i < added_count; i++) {
                        *pkt >> id >> type;
                        m_env.addActiveObject(id, type, pkt->readLongString());
+                       if (try_reattach)
+                               player->tryReattach(id);
                }
        } catch (PacketError &e) {
                infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
                                << ". The packet is unreliable, ignoring" << std::endl;
        }
+
+       // m_activeobjects_received is false before the first
+       // TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD packet is received
+       m_activeobjects_received = true;
 }
 
 void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
@@ -482,7 +504,7 @@ void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
                        if (!is.good())
                                break;
 
-                       std::string message = deSerializeString(is);
+                       std::string message = deSerializeString16(is);
 
                        // Pass on to the environment
                        m_env.processActiveObjectMessage(id, message);
@@ -517,22 +539,40 @@ void Client::handleCommand_Movement(NetworkPacket* pkt)
        player->movement_gravity                = g * BS;
 }
 
-void Client::handleCommand_HP(NetworkPacket* pkt)
+void Client::handleCommand_Fov(NetworkPacket *pkt)
 {
+       f32 fov;
+       bool is_multiplier = false;
+       f32 transition_time = 0.0f;
+
+       *pkt >> fov >> is_multiplier;
+
+       // Wrap transition_time extraction within a
+       // try-catch to preserve backwards compat
+       try {
+               *pkt >> transition_time;
+       } catch (PacketError &e) {};
 
+       LocalPlayer *player = m_env.getLocalPlayer();
+       assert(player);
+       player->setFov({ fov, is_multiplier, transition_time });
+       m_camera->notifyFovChange();
+}
+
+void Client::handleCommand_HP(NetworkPacket *pkt)
+{
        LocalPlayer *player = m_env.getLocalPlayer();
        assert(player != NULL);
 
-       u16 oldhp   = player->hp;
+       u16 oldhp = player->hp;
 
        u16 hp;
        *pkt >> hp;
 
        player->hp = hp;
 
-       if (moddingEnabled()) {
+       if (modsLoaded())
                m_script->on_hp_modification(hp);
-       }
 
        if (hp < oldhp) {
                // Add to ClientEvent queue
@@ -560,12 +600,15 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
        LocalPlayer *player = m_env.getLocalPlayer();
        assert(player != NULL);
 
+       if ((player->getCAO() && player->getCAO()->getParentId()) || player->isWaitingForReattach())
+               return;
+
        v3f pos;
        f32 pitch, yaw;
 
        *pkt >> pos >> pitch >> yaw;
 
-       player->setPosition(pos);
+       player->setLegitPosition(pos);
 
        infostream << "Client got TOCLIENT_MOVE_PLAYER"
                        << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
@@ -579,6 +622,10 @@ void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
                it would just force the pitch and yaw values to whatever
                the camera points to.
        */
+
+       if (g_settings->getBool("no_force_rotate"))
+               return;
+
        ClientEvent *event = new ClientEvent();
        event->type = CE_PLAYER_FORCE_MOVE;
        event->player_force_move.pitch = pitch;
@@ -637,21 +684,19 @@ void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
                m_media_downloader->addFile(name, sha1_raw);
        }
 
-       try {
+       {
                std::string str;
-
                *pkt >> str;
 
                Strfnd sf(str);
-               while(!sf.at_end()) {
+               while (!sf.at_end()) {
                        std::string baseurl = trim(sf.next(","));
-                       if (!baseurl.empty())
+                       if (!baseurl.empty()) {
+                               m_remote_media_servers.emplace_back(baseurl);
                                m_media_downloader->addRemoteServer(baseurl);
+                       }
                }
        }
-       catch(SerializationError& e) {
-               // not supported by server or turned off
-       }
 
        m_media_downloader->step(this);
 }
@@ -683,31 +728,38 @@ void Client::handleCommand_Media(NetworkPacket* pkt)
        if (num_files == 0)
                return;
 
-       if (!m_media_downloader || !m_media_downloader->isStarted()) {
-               const char *problem = m_media_downloader ?
-                       "media has not been requested" :
-                       "all media has been received already";
-               errorstream << "Client: Received media but "
-                       << problem << "! "
-                       << " bunch " << bunch_i << "/" << num_bunches
-                       << " files=" << num_files
-                       << " size=" << pkt->getSize() << std::endl;
-               return;
-       }
+       bool init_phase = m_media_downloader && m_media_downloader->isStarted();
 
-       // Mesh update thread must be stopped while
-       // updating content definitions
-       sanity_check(!m_mesh_update_thread.isRunning());
+       if (init_phase) {
+               // Mesh update thread must be stopped while
+               // updating content definitions
+               sanity_check(!m_mesh_update_thread.isRunning());
+       }
 
-       for (u32 i=0; i < num_files; i++) {
-               std::string name;
+       for (u32 i = 0; i < num_files; i++) {
+               std::string name, data;
 
                *pkt >> name;
+               data = pkt->readLongString();
 
-               std::string data = pkt->readLongString();
-
-               m_media_downloader->conventionalTransferDone(
-                               name, data, this);
+               bool ok = false;
+               if (init_phase) {
+                       ok = m_media_downloader->conventionalTransferDone(name, data, this);
+               } else {
+                       // Check pending dynamic transfers, one of them must be it
+                       for (const auto &it : m_pending_media_downloads) {
+                               if (it.second->conventionalTransferDone(name, data, this)) {
+                                       ok = true;
+                                       break;
+                               }
+                       }
+               }
+               if (!ok) {
+                       errorstream << "Client: Received media \"" << name
+                               << "\" but no downloads pending. " << num_bunches << " bunches, "
+                               << num_files << " in this one. (init_phase=" << init_phase
+                               << ")" << std::endl;
+               }
        }
 }
 
@@ -722,12 +774,11 @@ void Client::handleCommand_NodeDef(NetworkPacket* pkt)
 
        // Decompress node definitions
        std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
-       std::ostringstream tmp_os;
+       std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
        decompressZlib(tmp_is, tmp_os);
 
        // Deserialize node definitions
-       std::istringstream tmp_is2(tmp_os.str());
-       m_nodedef->deSerialize(tmp_is2);
+       m_nodedef->deSerialize(tmp_os);
        m_nodedef_received = true;
 }
 
@@ -742,12 +793,11 @@ void Client::handleCommand_ItemDef(NetworkPacket* pkt)
 
        // Decompress item definitions
        std::istringstream tmp_is(pkt->readLongString(), std::ios::binary);
-       std::ostringstream tmp_os;
+       std::stringstream tmp_os(std::ios::binary | std::ios::in | std::ios::out);
        decompressZlib(tmp_is, tmp_os);
 
        // Deserialize node definitions
-       std::istringstream tmp_is2(tmp_os.str());
-       m_itemdef->deSerialize(tmp_is2);
+       m_itemdef->deSerialize(tmp_os);
        m_itemdef_received = true;
 }
 
@@ -764,6 +814,7 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
                [25 + len] bool loop
                [26 + len] f32 fade
                [30 + len] f32 pitch
+               [34 + len] bool ephemeral
        */
 
        s32 server_id;
@@ -776,14 +827,21 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
        bool loop;
        float fade = 0.0f;
        float pitch = 1.0f;
+       bool ephemeral = false;
 
        *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
 
        try {
                *pkt >> fade;
                *pkt >> pitch;
+               *pkt >> ephemeral;
        } catch (PacketError &e) {};
 
+       SimpleSoundSpec sound_spec(name, gain, fade, pitch);
+
+       if (m_mods_loaded && m_script->on_play_sound(sound_spec))
+               return;
+
        // Start playing
        int client_id = -1;
        switch(type) {
@@ -799,7 +857,6 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
                        if (cao)
                                pos = cao->getPosition();
                        client_id = m_sound->playSoundAt(name, loop, gain, pos, pitch);
-                       // TODO: Set up sound to move with object
                        break;
                }
                default:
@@ -807,8 +864,11 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
        }
 
        if (client_id != -1) {
-               m_sounds_server_to_client[server_id] = client_id;
-               m_sounds_client_to_server[client_id] = server_id;
+               // for ephemeral sounds, server_id is not meaningful
+               if (!ephemeral) {
+                       m_sounds_server_to_client[server_id] = client_id;
+                       m_sounds_client_to_server[client_id] = server_id;
+               }
                if (object_id != 0)
                        m_sounds_to_objects[client_id] = object_id;
        }
@@ -898,7 +958,7 @@ void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
        u16 ignore;
        *pkt >> ignore; // this used to be the length of the following string, ignore it
 
-       std::string contents = pkt->getRemainingString();
+       std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
        std::istringstream is(contents, std::ios::binary);
        inv->deSerialize(is);
 }
@@ -924,114 +984,69 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
        std::string datastring(pkt->getString(0), pkt->getSize());
        std::istringstream is(datastring, std::ios_base::binary);
 
-       v3f pos                 = readV3F32(is);
-       v3f vel                 = readV3F32(is);
-       v3f acc                 = readV3F32(is);
-       float expirationtime    = readF32(is);
-       float size              = readF32(is);
-       bool collisiondetection = readU8(is);
-       std::string texture     = deSerializeLongString(is);
-
-       bool vertical          = false;
-       bool collision_removal = false;
-       TileAnimationParams animation;
-       animation.type         = TAT_NONE;
-       u8 glow                = 0;
-       bool object_collision  = false;
-       try {
-               vertical = readU8(is);
-               collision_removal = readU8(is);
-               animation.deSerialize(is, m_proto_ver);
-               glow = readU8(is);
-               object_collision = readU8(is);
-       } catch (...) {}
+       ParticleParameters p;
+       p.deSerialize(is, m_proto_ver);
 
        ClientEvent *event = new ClientEvent();
-       event->type                              = CE_SPAWN_PARTICLE;
-       event->spawn_particle.pos                = new v3f (pos);
-       event->spawn_particle.vel                = new v3f (vel);
-       event->spawn_particle.acc                = new v3f (acc);
-       event->spawn_particle.expirationtime     = expirationtime;
-       event->spawn_particle.size               = size;
-       event->spawn_particle.collisiondetection = collisiondetection;
-       event->spawn_particle.collision_removal  = collision_removal;
-       event->spawn_particle.object_collision   = object_collision;
-       event->spawn_particle.vertical           = vertical;
-       event->spawn_particle.texture            = new std::string(texture);
-       event->spawn_particle.animation          = animation;
-       event->spawn_particle.glow               = glow;
+       event->type           = CE_SPAWN_PARTICLE;
+       event->spawn_particle = new ParticleParameters(p);
+
+       if (m_mods_loaded && m_script->on_spawn_particle(*event->spawn_particle))
+               return;
 
        m_client_event_queue.push(event);
 }
 
 void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
 {
-       u16 amount;
-       float spawntime;
-       v3f minpos;
-       v3f maxpos;
-       v3f minvel;
-       v3f maxvel;
-       v3f minacc;
-       v3f maxacc;
-       float minexptime;
-       float maxexptime;
-       float minsize;
-       float maxsize;
-       bool collisiondetection;
-       u32 server_id;
-
-       *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
-               >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
-               >> maxsize >> collisiondetection;
-
-       std::string texture = pkt->readLongString();
-
-       *pkt >> server_id;
-
-       bool vertical          = false;
-       bool collision_removal = false;
-       u16 attached_id        = 0;
-       TileAnimationParams animation;
-       animation.type         = TAT_NONE;
-       u8 glow                = 0;
-       bool object_collision  = false;
-       try {
-               *pkt >> vertical;
-               *pkt >> collision_removal;
-               *pkt >> attached_id;
+       std::string datastring(pkt->getString(0), pkt->getSize());
+       std::istringstream is(datastring, std::ios_base::binary);
 
-               // This is horrible but required (why are there two ways to deserialize pkts?)
-               std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes());
-               std::istringstream is(datastring, std::ios_base::binary);
-               animation.deSerialize(is, m_proto_ver);
-               glow = readU8(is);
-               object_collision = readU8(is);
-       } catch (...) {}
+       ParticleSpawnerParameters p;
+       u32 server_id;
+       u16 attached_id = 0;
+
+       p.amount             = readU16(is);
+       p.time               = readF32(is);
+       p.minpos             = readV3F32(is);
+       p.maxpos             = readV3F32(is);
+       p.minvel             = readV3F32(is);
+       p.maxvel             = readV3F32(is);
+       p.minacc             = readV3F32(is);
+       p.maxacc             = readV3F32(is);
+       p.minexptime         = readF32(is);
+       p.maxexptime         = readF32(is);
+       p.minsize            = readF32(is);
+       p.maxsize            = readF32(is);
+       p.collisiondetection = readU8(is);
+       p.texture            = deSerializeString32(is);
+
+       server_id = readU32(is);
+
+       p.vertical = readU8(is);
+       p.collision_removal = readU8(is);
+
+       attached_id = readU16(is);
+
+       p.animation.deSerialize(is, m_proto_ver);
+       p.glow = readU8(is);
+       p.object_collision = readU8(is);
+
+       // This is kinda awful
+       do {
+               u16 tmp_param0 = readU16(is);
+               if (is.eof())
+                       break;
+               p.node.param0 = tmp_param0;
+               p.node.param2 = readU8(is);
+               p.node_tile   = readU8(is);
+       } while (0);
 
        auto event = new ClientEvent();
-       event->type                                   = CE_ADD_PARTICLESPAWNER;
-       event->add_particlespawner.amount             = amount;
-       event->add_particlespawner.spawntime          = spawntime;
-       event->add_particlespawner.minpos             = new v3f (minpos);
-       event->add_particlespawner.maxpos             = new v3f (maxpos);
-       event->add_particlespawner.minvel             = new v3f (minvel);
-       event->add_particlespawner.maxvel             = new v3f (maxvel);
-       event->add_particlespawner.minacc             = new v3f (minacc);
-       event->add_particlespawner.maxacc             = new v3f (maxacc);
-       event->add_particlespawner.minexptime         = minexptime;
-       event->add_particlespawner.maxexptime         = maxexptime;
-       event->add_particlespawner.minsize            = minsize;
-       event->add_particlespawner.maxsize            = maxsize;
-       event->add_particlespawner.collisiondetection = collisiondetection;
-       event->add_particlespawner.collision_removal  = collision_removal;
-       event->add_particlespawner.object_collision   = object_collision;
-       event->add_particlespawner.attached_id        = attached_id;
-       event->add_particlespawner.vertical           = vertical;
-       event->add_particlespawner.texture            = new std::string(texture);
-       event->add_particlespawner.id                 = server_id;
-       event->add_particlespawner.animation          = animation;
-       event->add_particlespawner.glow               = glow;
+       event->type                            = CE_ADD_PARTICLESPAWNER;
+       event->add_particlespawner.p           = new ParticleSpawnerParameters(p);
+       event->add_particlespawner.attached_id = attached_id;
+       event->add_particlespawner.id          = server_id;
 
        m_client_event_queue.push(event);
 }
@@ -1051,9 +1066,6 @@ void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
 
 void Client::handleCommand_HudAdd(NetworkPacket* pkt)
 {
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
-
        u32 server_id;
        u8 type;
        v2f pos;
@@ -1067,33 +1079,39 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt)
        v2f offset;
        v3f world_pos;
        v2s32 size;
+       s16 z_index = 0;
+       std::string text2;
+       u32 style = 0;
 
        *pkt >> server_id >> type >> pos >> name >> scale >> text >> number >> item
                >> dir >> align >> offset;
        try {
                *pkt >> world_pos;
-       }
-       catch(SerializationError &e) {};
-
-       try {
                *pkt >> size;
-       } catch(SerializationError &e) {};
+               *pkt >> z_index;
+               *pkt >> text2;
+               *pkt >> style;
+       } catch(PacketError &e) {};
 
        ClientEvent *event = new ClientEvent();
-       event->type             = CE_HUDADD;
-       event->hudadd.server_id = server_id;
-       event->hudadd.type      = type;
-       event->hudadd.pos       = new v2f(pos);
-       event->hudadd.name      = new std::string(name);
-       event->hudadd.scale     = new v2f(scale);
-       event->hudadd.text      = new std::string(text);
-       event->hudadd.number    = number;
-       event->hudadd.item      = item;
-       event->hudadd.dir       = dir;
-       event->hudadd.align     = new v2f(align);
-       event->hudadd.offset    = new v2f(offset);
-       event->hudadd.world_pos = new v3f(world_pos);
-       event->hudadd.size      = new v2s32(size);
+       event->type              = CE_HUDADD;
+       event->hudadd            = new ClientEventHudAdd();
+       event->hudadd->server_id = server_id;
+       event->hudadd->type      = type;
+       event->hudadd->pos       = pos;
+       event->hudadd->name      = name;
+       event->hudadd->scale     = scale;
+       event->hudadd->text      = text;
+       event->hudadd->number    = number;
+       event->hudadd->item      = item;
+       event->hudadd->dir       = dir;
+       event->hudadd->align     = align;
+       event->hudadd->offset    = offset;
+       event->hudadd->world_pos = world_pos;
+       event->hudadd->size      = size;
+       event->hudadd->z_index   = z_index;
+       event->hudadd->text2     = text2;
+       event->hudadd->style     = style;
        m_client_event_queue.push(event);
 }
 
@@ -1103,16 +1121,10 @@ void Client::handleCommand_HudRemove(NetworkPacket* pkt)
 
        *pkt >> server_id;
 
-       auto i = m_hud_server_to_client.find(server_id);
-       if (i != m_hud_server_to_client.end()) {
-               int client_id = i->second;
-               m_hud_server_to_client.erase(i);
-
-               ClientEvent *event = new ClientEvent();
-               event->type     = CE_HUDRM;
-               event->hudrm.id = client_id;
-               m_client_event_queue.push(event);
-       }
+       ClientEvent *event = new ClientEvent();
+       event->type     = CE_HUDRM;
+       event->hudrm.id = server_id;
+       m_client_event_queue.push(event);
 }
 
 void Client::handleCommand_HudChange(NetworkPacket* pkt)
@@ -1127,31 +1139,41 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt)
 
        *pkt >> server_id >> stat;
 
-       if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
-               stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
-               *pkt >> v2fdata;
-       else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
-               *pkt >> sdata;
-       else if (stat == HUD_STAT_WORLD_POS)
-               *pkt >> v3fdata;
-       else if (stat == HUD_STAT_SIZE )
-               *pkt >> v2s32data;
-       else
-               *pkt >> intdata;
-
-       std::unordered_map<u32, u32>::const_iterator i = m_hud_server_to_client.find(server_id);
-       if (i != m_hud_server_to_client.end()) {
-               ClientEvent *event = new ClientEvent();
-               event->type              = CE_HUDCHANGE;
-               event->hudchange.id      = i->second;
-               event->hudchange.stat    = (HudElementStat)stat;
-               event->hudchange.v2fdata = new v2f(v2fdata);
-               event->hudchange.v3fdata = new v3f(v3fdata);
-               event->hudchange.sdata   = new std::string(sdata);
-               event->hudchange.data    = intdata;
-               event->hudchange.v2s32data = new v2s32(v2s32data);
-               m_client_event_queue.push(event);
+       // Keep in sync with:server.cpp -> SendHUDChange
+       switch ((HudElementStat)stat) {
+               case HUD_STAT_POS:
+               case HUD_STAT_SCALE:
+               case HUD_STAT_ALIGN:
+               case HUD_STAT_OFFSET:
+                       *pkt >> v2fdata;
+                       break;
+               case HUD_STAT_NAME:
+               case HUD_STAT_TEXT:
+               case HUD_STAT_TEXT2:
+                       *pkt >> sdata;
+                       break;
+               case HUD_STAT_WORLD_POS:
+                       *pkt >> v3fdata;
+                       break;
+               case HUD_STAT_SIZE:
+                       *pkt >> v2s32data;
+                       break;
+               default:
+                       *pkt >> intdata;
+                       break;
        }
+
+       ClientEvent *event = new ClientEvent();
+       event->type                 = CE_HUDCHANGE;
+       event->hudchange            = new ClientEventHudChange();
+       event->hudchange->id        = server_id;
+       event->hudchange->stat      = static_cast<HudElementStat>(stat);
+       event->hudchange->v2fdata   = v2fdata;
+       event->hudchange->v3fdata   = v3fdata;
+       event->hudchange->sdata     = sdata;
+       event->hudchange->data      = intdata;
+       event->hudchange->v2s32data = v2s32data;
+       m_client_event_queue.push(event);
 }
 
 void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
@@ -1169,18 +1191,33 @@ void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
        player->hud_flags &= ~mask;
        player->hud_flags |= flags;
 
+       if (g_settings->getBool("hud_flags_bypass"))
+               player->hud_flags = HUD_FLAG_HOTBAR_VISIBLE     | HUD_FLAG_HEALTHBAR_VISIBLE |
+                       HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
+                       HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE   |
+                       HUD_FLAG_MINIMAP_RADAR_VISIBLE;
+
        m_minimap_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
        bool m_minimap_radar_disabled_by_server = !(player->hud_flags & HUD_FLAG_MINIMAP_RADAR_VISIBLE);
 
+       // Not so satisying code to keep compatibility with old fixed mode system
+       // -->
+
        // Hide minimap if it has been disabled by the server
        if (m_minimap && m_minimap_disabled_by_server && was_minimap_visible)
                // defers a minimap update, therefore only call it if really
                // needed, by checking that minimap was visible before
-               m_minimap->setMinimapMode(MINIMAP_MODE_OFF);
-
-       // Switch to surface mode if radar disabled by server
-       if (m_minimap && m_minimap_radar_disabled_by_server && was_minimap_radar_visible)
-               m_minimap->setMinimapMode(MINIMAP_MODE_SURFACEx1);
+               m_minimap->setModeIndex(0);
+
+       // If radar has been disabled, try to find a non radar mode or fall back to 0
+       if (m_minimap && m_minimap_radar_disabled_by_server
+                       && was_minimap_radar_visible) {
+               while (m_minimap->getModeIndex() > 0 &&
+                               m_minimap->getModeDef().type == MINIMAP_TYPE_RADAR)
+                       m_minimap->nextMode();
+       }
+       // <--
+       // End of 'not so satifying code'
 }
 
 void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
@@ -1198,49 +1235,138 @@ void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
                        player->hud_hotbar_itemcount = hotbar_itemcount;
        }
        else if (param == HUD_PARAM_HOTBAR_IMAGE) {
-               // If value not empty verify image exists in texture source
-               if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
-                       errorstream << "Server sent wrong Hud hotbar image (sent value: '"
-                               << value << "')" << std::endl;
-                       return;
-               }
                player->hotbar_image = value;
        }
        else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
-               // If value not empty verify image exists in texture source
-               if (!value.empty() && !getTextureSource()->isKnownSourceImage(value)) {
-                       errorstream << "Server sent wrong Hud hotbar selected image (sent value: '"
-                                       << value << "')" << std::endl;
-                       return;
-               }
                player->hotbar_selected_image = value;
        }
 }
 
 void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
 {
-       std::string datastring(pkt->getString(0), pkt->getSize());
-       std::istringstream is(datastring, std::ios_base::binary);
+       if (m_proto_ver < 39) {
+               // Handle Protocol 38 and below servers with old set_sky,
+               // ensuring the classic look is kept.
+               std::string datastring(pkt->getString(0), pkt->getSize());
+               std::istringstream is(datastring, std::ios_base::binary);
 
-       video::SColor *bgcolor           = new video::SColor(readARGB8(is));
-       std::string *type                = new std::string(deSerializeString(is));
-       u16 count                        = readU16(is);
-       std::vector<std::string> *params = new std::vector<std::string>;
+               SkyboxParams skybox;
+               skybox.bgcolor = video::SColor(readARGB8(is));
+               skybox.type = std::string(deSerializeString16(is));
+               u16 count = readU16(is);
+
+               for (size_t i = 0; i < count; i++)
+                       skybox.textures.emplace_back(deSerializeString16(is));
+
+               skybox.clouds = true;
+               try {
+                       skybox.clouds = readU8(is);
+               } catch (...) {}
+
+               // Use default skybox settings:
+               SunParams sun = SkyboxDefaults::getSunDefaults();
+               MoonParams moon = SkyboxDefaults::getMoonDefaults();
+               StarParams stars = SkyboxDefaults::getStarDefaults();
+
+               // Fix for "regular" skies, as color isn't kept:
+               if (skybox.type == "regular") {
+                       skybox.sky_color = SkyboxDefaults::getSkyColorDefaults();
+                       skybox.fog_tint_type = "default";
+                       skybox.fog_moon_tint = video::SColor(255, 255, 255, 255);
+                       skybox.fog_sun_tint = video::SColor(255, 255, 255, 255);
+               } else {
+                       sun.visible = false;
+                       sun.sunrise_visible = false;
+                       moon.visible = false;
+                       stars.visible = false;
+               }
 
-       for (size_t i = 0; i < count; i++)
-               params->push_back(deSerializeString(is));
+               // Skybox, sun, moon and stars ClientEvents:
+               ClientEvent *sky_event = new ClientEvent();
+               sky_event->type = CE_SET_SKY;
+               sky_event->set_sky = new SkyboxParams(skybox);
+               m_client_event_queue.push(sky_event);
+
+               ClientEvent *sun_event = new ClientEvent();
+               sun_event->type = CE_SET_SUN;
+               sun_event->sun_params = new SunParams(sun);
+               m_client_event_queue.push(sun_event);
+
+               ClientEvent *moon_event = new ClientEvent();
+               moon_event->type = CE_SET_MOON;
+               moon_event->moon_params = new MoonParams(moon);
+               m_client_event_queue.push(moon_event);
+
+               ClientEvent *star_event = new ClientEvent();
+               star_event->type = CE_SET_STARS;
+               star_event->star_params = new StarParams(stars);
+               m_client_event_queue.push(star_event);
+       } else {
+               SkyboxParams skybox;
+               u16 texture_count;
+               std::string texture;
+
+               *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
+                       skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
+
+               if (skybox.type == "skybox") {
+                       *pkt >> texture_count;
+                       for (int i = 0; i < texture_count; i++) {
+                               *pkt >> texture;
+                               skybox.textures.emplace_back(texture);
+                       }
+               }
+               else if (skybox.type == "regular") {
+                       *pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
+                               >> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
+                               >> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
+                               >> skybox.sky_color.indoors;
+               }
 
-       bool clouds = true;
-       try {
-               clouds = readU8(is);
-       } catch (...) {}
+               ClientEvent *event = new ClientEvent();
+               event->type = CE_SET_SKY;
+               event->set_sky = new SkyboxParams(skybox);
+               m_client_event_queue.push(event);
+       }
+}
+
+void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
+{
+       SunParams sun;
+
+       *pkt >> sun.visible >> sun.texture>> sun.tonemap
+               >> sun.sunrise >> sun.sunrise_visible >> sun.scale;
 
        ClientEvent *event = new ClientEvent();
-       event->type            = CE_SET_SKY;
-       event->set_sky.bgcolor = bgcolor;
-       event->set_sky.type    = type;
-       event->set_sky.params  = params;
-       event->set_sky.clouds  = clouds;
+       event->type        = CE_SET_SUN;
+       event->sun_params  = new SunParams(sun);
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudSetMoon(NetworkPacket *pkt)
+{
+       MoonParams moon;
+
+       *pkt >> moon.visible >> moon.texture
+               >> moon.tonemap >> moon.scale;
+
+       ClientEvent *event = new ClientEvent();
+       event->type        = CE_SET_MOON;
+       event->moon_params = new MoonParams(moon);
+       m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudSetStars(NetworkPacket *pkt)
+{
+       StarParams stars;
+
+       *pkt >> stars.visible >> stars.count
+               >> stars.starcolor >> stars.scale;
+
+       ClientEvent *event = new ClientEvent();
+       event->type        = CE_SET_STARS;
+       event->star_params = new StarParams(stars);
+
        m_client_event_queue.push(event);
 }
 
@@ -1298,6 +1424,8 @@ void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
        *pkt >> player->local_animations[2];
        *pkt >> player->local_animations[3];
        *pkt >> player->local_animation_speed;
+
+       player->last_animation = -1;
 }
 
 void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
@@ -1381,6 +1509,90 @@ void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
        loadMods();
 }
 
+void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
+{
+       if (g_settings->getBool("antiknockback"))
+               return;
+       v3f added_vel;
+
+       *pkt >> added_vel;
+
+       LocalPlayer *player = m_env.getLocalPlayer();
+       assert(player != NULL);
+       player->addVelocity(added_vel);
+}
+
+void Client::handleCommand_MediaPush(NetworkPacket *pkt)
+{
+       std::string raw_hash, filename, filedata;
+       u32 token;
+       bool cached;
+
+       *pkt >> raw_hash >> filename >> cached;
+       if (m_proto_ver >= 40)
+               *pkt >> token;
+       else
+               filedata = pkt->readLongString();
+
+       if (raw_hash.size() != 20 || filename.empty() ||
+                       (m_proto_ver < 40 && filedata.empty()) ||
+                       !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
+               throw PacketError("Illegal filename, data or hash");
+       }
+
+       verbosestream << "Server pushes media file \"" << filename << "\" ";
+       if (filedata.empty())
+               verbosestream << "to be fetched ";
+       else
+               verbosestream << "with " << filedata.size() << " bytes ";
+       verbosestream << "(cached=" << cached << ")" << std::endl;
+
+       if (m_media_pushed_files.count(filename) != 0) {
+               // Ignore (but acknowledge). Previously this was for sync purposes,
+               // but even in new versions media cannot be replaced at runtime.
+               if (m_proto_ver >= 40)
+                       sendHaveMedia({ token });
+               return;
+       }
+
+       if (!filedata.empty()) {
+               // LEGACY CODEPATH
+               // Compute and check checksum of data
+               std::string computed_hash;
+               {
+                       SHA1 ctx;
+                       ctx.addBytes(filedata.c_str(), filedata.size());
+                       unsigned char *buf = ctx.getDigest();
+                       computed_hash.assign((char*) buf, 20);
+                       free(buf);
+               }
+               if (raw_hash != computed_hash) {
+                       verbosestream << "Hash of file data mismatches, ignoring." << std::endl;
+                       return;
+               }
+
+               // Actually load media
+               loadMedia(filedata, filename, true);
+               m_media_pushed_files.insert(filename);
+
+               // Cache file for the next time when this client joins the same server
+               if (cached)
+                       clientMediaUpdateCache(raw_hash, filedata);
+               return;
+       }
+
+       m_media_pushed_files.insert(filename);
+
+       // create a downloader for this file
+       auto downloader(std::make_shared<SingleMediaDownloader>(cached));
+       m_pending_media_downloads.emplace_back(token, downloader);
+       downloader->addFile(filename, raw_hash);
+       for (const auto &baseurl : m_remote_media_servers)
+               downloader->addRemoteServer(baseurl);
+
+       downloader->step(this);
+}
+
 /*
  * Mod channels
  */
@@ -1472,3 +1684,38 @@ void Client::handleCommand_ModChannelSignal(NetworkPacket *pkt)
        if (valid_signal)
                m_script->on_modchannel_signal(channel, signal);
 }
+
+void Client::handleCommand_MinimapModes(NetworkPacket *pkt)
+{
+       u16 count; // modes
+       u16 mode;  // wanted current mode index after change
+
+       *pkt >> count >> mode;
+
+       if (m_minimap)
+               m_minimap->clearModes();
+
+       for (size_t index = 0; index < count; index++) {
+               u16 type;
+               std::string label;
+               u16 size;
+               std::string texture;
+               u16 scale;
+
+               *pkt >> type >> label >> size >> texture >> scale;
+
+               if (m_minimap)
+                       m_minimap->addMode(MinimapType(type), size, label, texture, scale);
+       }
+
+       if (m_minimap)
+               m_minimap->setModeIndex(mode);
+}
+
+void Client::handleCommand_SetLighting(NetworkPacket *pkt)
+{
+       Lighting& lighting = m_env.getLocalPlayer()->getLighting();
+
+       if (pkt->getRemainingBytes() >= 4)
+               *pkt >> lighting.shadow_intensity;
+}