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"
#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)
{
// 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 ||
// Authenticate using that method, or abort if there wasn't any method found
if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
- if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP
- && !m_simple_singleplayer_mode) {
+ if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
+ !m_simple_singleplayer_mode &&
+ !getServerAddress().isLocalhost() &&
+ g_settings->getBool("enable_register_confirmation")) {
promptConfirmRegistration(chosen_auth_mechanism);
} else {
startAuth(chosen_auth_mechanism);
<< 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_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);
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;
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 {
// 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";
- }
}
}
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);
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);
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)
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;
}
*/
+ LocalPlayer *player = m_env.getLocalPlayer();
+ bool try_reattach = player && player->isWaitingForReattach();
+
try {
u8 type;
u16 removed_count, added_count, id;
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)
if (!is.good())
break;
- std::string message = deSerializeString(is);
+ std::string message = deSerializeString16(is);
// Pass on to the environment
m_env.processActiveObjectMessage(id, message);
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
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 << ")"
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;
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);
}
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;
+ }
}
}
// 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;
}
// 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;
}
[25 + len] bool loop
[26 + len] f32 fade
[30 + len] f32 pitch
+ [34 + len] bool ephemeral
*/
s32 server_id;
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) {
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:
}
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;
}
inv = inv_it->second;
}
- std::string contents;
- *pkt >> contents;
+ u16 ignore;
+ *pkt >> ignore; // this used to be the length of the following string, ignore it
+
+ std::string contents(pkt->getRemainingString(), pkt->getRemainingBytes());
std::istringstream is(contents, std::ios::binary);
inv->deSerialize(is);
}
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);
- u32 client_id = m_particle_manager.getSpawnerId();
- m_particles_server_to_client[server_id] = client_id;
-
- ClientEvent *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 = client_id;
- event->add_particlespawner.animation = animation;
- event->add_particlespawner.glow = glow;
+ auto event = new ClientEvent();
+ 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);
}
u32 server_id;
*pkt >> server_id;
- u32 client_id;
- auto i = m_particles_server_to_client.find(server_id);
- if (i != m_particles_server_to_client.end())
- client_id = i->second;
- else
- return;
-
ClientEvent *event = new ClientEvent();
event->type = CE_DELETE_PARTICLESPAWNER;
- event->delete_particlespawner.id = client_id;
+ event->delete_particlespawner.id = server_id;
m_client_event_queue.push(event);
}
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;
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);
}
*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)
*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)
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)
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_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_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_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);
}
*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)
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
*/
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;
+}