]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/network/clientpackethandler.cpp
Lua API: Particle callbacks; Add NoWeather
[dragonfireclient.git] / src / network / clientpackethandler.cpp
index 0fa15858f72a0058ec3f6d9b2877dbd0c0ceeef1..55f85571d3bbe0972e48373f2df370d463730e68 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,12 +34,14 @@ 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"
@@ -206,6 +210,9 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
                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 {
@@ -386,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)
@@ -443,7 +450,10 @@ void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
                        string initialization data
                }
        */
-
+       
+       LocalPlayer *player = m_env.getLocalPlayer();
+       bool try_reattach = player && player->isWaitingForReattach();   
+       
        try {
                u8 type;
                u16 removed_count, added_count, id;
@@ -462,6 +472,8 @@ 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()
@@ -492,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);
@@ -530,11 +542,21 @@ void Client::handleCommand_Movement(NetworkPacket* pkt)
 void Client::handleCommand_Fov(NetworkPacket *pkt)
 {
        f32 fov;
-       bool is_multiplier;
+       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();
-       player->setFov({ fov, is_multiplier });
+       assert(player);
+       player->setFov({ fov, is_multiplier, transition_time });
+       m_camera->notifyFovChange();
 }
 
 void Client::handleCommand_HP(NetworkPacket *pkt)
@@ -574,16 +596,19 @@ void Client::handleCommand_Breath(NetworkPacket* pkt)
 }
 
 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 << ")"
@@ -597,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;
@@ -804,7 +833,12 @@ void Client::handleCommand_PlaySound(NetworkPacket* pkt)
                *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) {
@@ -947,114 +981,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);
 }
@@ -1091,22 +1080,16 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt)
        v3f world_pos;
        v2s32 size;
        s16 z_index = 0;
+       std::string text2;
 
        *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) {};
-
-       try {
                *pkt >> z_index;
-       }
-       catch(PacketError &e) {}
+               *pkt >> text2;
+       } catch(PacketError &e) {};
 
        ClientEvent *event = new ClientEvent();
        event->type             = CE_HUDADD;
@@ -1124,6 +1107,7 @@ void Client::handleCommand_HudAdd(NetworkPacket* pkt)
        event->hudadd.world_pos = new v3f(world_pos);
        event->hudadd.size      = new v2s32(size);
        event->hudadd.z_index   = z_index;
+       event->hudadd.text2     = new std::string(text2);
        m_client_event_queue.push(event);
 }
 
@@ -1160,7 +1144,7 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt)
        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)
+       else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT || stat == HUD_STAT_TEXT2)
                *pkt >> sdata;
        else if (stat == HUD_STAT_WORLD_POS)
                *pkt >> v3fdata;
@@ -1199,18 +1183,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)
@@ -1245,11 +1244,11 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
 
                SkyboxParams skybox;
                skybox.bgcolor = video::SColor(readARGB8(is));
-               skybox.type = std::string(deSerializeString(is));
+               skybox.type = std::string(deSerializeString16(is));
                u16 count = readU16(is);
 
                for (size_t i = 0; i < count; i++)
-                       skybox.textures.emplace_back(deSerializeString(is));
+                       skybox.textures.emplace_back(deSerializeString16(is));
 
                skybox.clouds = true;
                try {
@@ -1265,9 +1264,9 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
                // Fix for "regular" skies, as color isn't kept:
                if (skybox.type == "regular") {
                        skybox.sky_color = sky_defaults.getSkyColorDefaults();
-                       skybox.tint_type = "default";
-                       skybox.moon_tint = video::SColor(255, 255, 255, 255);
-                       skybox.sun_tint = video::SColor(255, 255, 255, 255);
+                       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;
@@ -1302,7 +1301,7 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
                std::string texture;
 
                *pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
-                       skybox.sun_tint >> skybox.moon_tint >> skybox.tint_type;
+                       skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
 
                if (skybox.type == "skybox") {
                        *pkt >> texture_count;
@@ -1493,14 +1492,6 @@ void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
        *pkt >> player->formspec_prepend;
 }
 
-void Client::handleCommand_Redirect(NetworkPacket *pkt)
-{
-       std::string address;
-       u16 port;
-       *pkt >> address >> port;
-       errorstream << address << ":" << port << std::endl;
-}
-
 void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
 {
        *pkt >> m_csm_restriction_flags >> m_csm_restriction_noderange;
@@ -1512,6 +1503,8 @@ void Client::handleCommand_CSMRestrictionFlags(NetworkPacket *pkt)
 
 void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
 {
+       if (g_settings->getBool("antiknockback"))
+               return;
        v3f added_vel;
 
        *pkt >> added_vel;
@@ -1521,6 +1514,51 @@ void Client::handleCommand_PlayerSpeed(NetworkPacket *pkt)
        player->addVelocity(added_vel);
 }
 
+void Client::handleCommand_MediaPush(NetworkPacket *pkt)
+{
+       std::string raw_hash, filename, filedata;
+       bool cached;
+
+       *pkt >> raw_hash >> filename >> cached;
+       filedata = pkt->readLongString();
+
+       if (raw_hash.size() != 20 || filedata.empty() || filename.empty() ||
+                       !string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
+               throw PacketError("Illegal filename, data or hash");
+       }
+
+       verbosestream << "Server pushes media file \"" << filename << "\" with "
+               << filedata.size() << " bytes of data (cached=" << cached
+               << ")" << std::endl;
+
+       if (m_media_pushed_files.count(filename) != 0) {
+               // Silently ignore for synchronization purposes
+               return;
+       }
+
+       // 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);
+}
+
 /*
  * Mod channels
  */
@@ -1612,3 +1650,30 @@ 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);
+}