#include "client/client.h"
#include "util/base64.h"
+#include "client/camera.h"
#include "chatmessage.h"
#include "client/clientmedia.h"
#include "log.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"
void Client::handleCommand_Deprecated(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 = "";
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 {
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)
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)
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);
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 = deSerializeLongString(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);
}
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;
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);
}
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;
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(deSerializeString(is));
+ u16 count = readU16(is);
+
+ for (size_t i = 0; i < count; i++)
+ skybox.textures.emplace_back(deSerializeString(is));
+
+ skybox.clouds = true;
+ try {
+ skybox.clouds = readU8(is);
+ } catch (...) {}
+
+ // Use default skybox settings:
+ SkyboxDefaults sky_defaults;
+ SunParams sun = sky_defaults.getSunDefaults();
+ MoonParams moon = sky_defaults.getMoonDefaults();
+ StarParams stars = sky_defaults.getStarDefaults();
+
+ // Fix for "regular" skies, as color isn't kept:
+ if (skybox.type == "regular") {
+ skybox.sky_color = sky_defaults.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_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_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_STARS;
+ event->star_params = new StarParams(stars);
+
m_client_event_queue.push(event);
}
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
*/