From 205893620cf66af03c1224d5de6f6bcc29ff2ec3 Mon Sep 17 00:00:00 2001 From: luk3yx Date: Sun, 29 Dec 2019 20:49:02 +1300 Subject: [PATCH] Re-add support for protocol versions 25-33. --- src/client/client.cpp | 51 +++++- src/client/client.h | 2 + src/client/clientenvironment.cpp | 77 +++++++++ src/client/clientenvironment.h | 10 +- src/client/content_cao.cpp | 100 +++++++---- src/client/game.cpp | 5 + src/client/localplayer.cpp | 8 + src/client/localplayer.h | 2 +- src/itemdef.cpp | 47 ++++-- src/network/clientopcodes.cpp | 2 +- src/network/clientpackethandler.cpp | 82 +++++++-- src/network/connection.h | 14 +- src/network/connectionthreads.cpp | 20 +++ src/network/networkpacket.cpp | 18 +- src/network/networkpacket.h | 7 + src/network/networkprotocol.h | 2 +- src/nodedef.cpp | 253 +++++++++++++++++++++++++--- src/nodedef.h | 1 + src/object_properties.cpp | 105 +++++++----- src/particles.cpp | 14 +- src/sound.h | 13 +- src/tileanimation.cpp | 30 +++- src/tileanimation.h | 1 + src/tool.cpp | 14 +- src/util/serialize.h | 53 +++++- 25 files changed, 772 insertions(+), 159 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 3eaf81d25..f9068083f 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -482,6 +482,10 @@ void Client::step(float dtime) event->type = CE_PLAYER_DAMAGE; event->player_damage.amount = damage; m_client_event_queue.push(event); + } else if (envEvent.type == CEE_PLAYER_BREATH && m_proto_ver < 29) { + // Protocol v29 or greater obsoleted this event + u16 breath = envEvent.player_breath.amount; + sendBreath(breath); } } @@ -861,6 +865,7 @@ void Client::ReceiveAll() inline void Client::handleCommand(NetworkPacket* pkt) { + pkt->setProtocolVersion(m_proto_ver); const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()]; (this->*opHandle.handler)(pkt); } @@ -979,7 +984,7 @@ void Client::interact(InteractAction action, const PointedThing& pointed) [9 + plen] player position information */ - NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0); + NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0, 0, m_proto_ver); pkt << (u8)action; pkt << myplayer->getWieldIndex(); @@ -1260,8 +1265,27 @@ void Client::sendChangePassword(const std::string &oldpassword, void Client::sendDamage(u16 damage) { - NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u16)); - pkt << damage; + // Minetest 0.4 uses uint8s instead of uint16s in TOSERVER_DAMAGE. + if (m_proto_ver >= 37) { + NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u16)); + pkt << damage; + Send(&pkt); + } else { + u8 raw_damage = damage & 0xFF; + NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8)); + pkt << raw_damage; + Send(&pkt); + } +} + +void Client::sendBreath(u16 breath) +{ + // Protocol v29 (Minetest 0.4.16) made this obsolete + if (m_proto_ver >= 29) + return; + + NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16)); + pkt << breath; Send(&pkt); } @@ -1319,7 +1343,8 @@ void Client::sendPlayerPos() player->last_camera_fov = camera_fov; player->last_wanted_range = wanted_range; - NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1); + NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4 + 1 + 1, 0, + m_proto_ver); writePlayerPos(player, &map, &pkt); @@ -1584,6 +1609,24 @@ void Client::typeChatMessage(const std::wstring &message) // Send to others sendChatMessage(message); + + // Show locally + if (message[0] == L'/') { + if (!m_mods_loaded) { + ChatMessage *chatMessage = new ChatMessage(L"issued command: " + + message); + m_chat_queue.push(chatMessage); + } + } else if (m_proto_ver < 29) { + // Backwards compatibility + LocalPlayer *player = m_env.getLocalPlayer(); + if (!player) + return; + std::wstring name = narrow_to_wide(player->getName()); + ChatMessage *chatMessage = new ChatMessage(CHATMESSAGE_TYPE_NORMAL, + message, name); + m_chat_queue.push(chatMessage); + } } void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) diff --git a/src/client/client.h b/src/client/client.h index 52de3e046..3ab6e3958 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -179,6 +179,7 @@ public: void handleCommand_BlockData(NetworkPacket* pkt); void handleCommand_Inventory(NetworkPacket* pkt); void handleCommand_TimeOfDay(NetworkPacket* pkt); + void handleCommand_ChatMessageOld(NetworkPacket *pkt); void handleCommand_ChatMessage(NetworkPacket *pkt); void handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt); void handleCommand_ActiveObjectMessages(NetworkPacket* pkt); @@ -240,6 +241,7 @@ public: void sendChangePassword(const std::string &oldpassword, const std::string &newpassword); void sendDamage(u16 damage); + void sendBreath(u16 breath); void sendRespawn(); void sendReady(); diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index d276bc482..6452d1beb 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -298,6 +298,75 @@ void ClientEnvironment::step(float dtime) if (m_client->modsLoaded()) m_script->environment_step(dtime); + // Protocol v29 make this behaviour obsolete + if (getGameDef()->getProtoVersion() < 29) { + if (m_lava_hurt_interval.step(dtime, 1.0)) { + v3f pf = lplayer->getPosition(); + + // Feet, middle and head + v3s16 p1 = floatToInt(pf + v3f(0, BS * 0.1, 0), BS); + MapNode n1 = m_map->getNode(p1); + v3s16 p2 = floatToInt(pf + v3f(0, BS * 0.8, 0), BS); + MapNode n2 = m_map->getNode(p2); + v3s16 p3 = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n3 = m_map->getNode(p3); + + u32 damage_per_second = 0; + damage_per_second = MYMAX(damage_per_second, + m_client->ndef()->get(n1).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_client->ndef()->get(n2).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_client->ndef()->get(n3).damage_per_second); + + if (damage_per_second != 0) + damageLocalPlayer(damage_per_second, true); + } + + /* + Drowning + */ + if (m_drowning_interval.step(dtime, 2.0)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNode(p); + ContentFeatures c = m_client->ndef()->get(n); + u8 drowning_damage = c.drowning; + if (drowning_damage > 0 && lplayer->hp > 0) { + u16 breath = lplayer->getBreath(); + if (breath > 10) + breath = 11; + if (breath > 0) + breath -= 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + + if (lplayer->getBreath() == 0 && drowning_damage > 0) + damageLocalPlayer(drowning_damage, true); + } + if (m_breathing_interval.step(dtime, 0.5)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNode(p); + ContentFeatures c = m_client->ndef()->get(n); + if (!lplayer->hp) { + lplayer->setBreath(11); + } else if (c.drowning == 0) { + u16 breath = lplayer->getBreath(); + if (breath <= 10) { + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + } + } + } + // Update lighting on local player (used for wield item) u32 day_night_ratio = getDayNightRatio(); { @@ -498,6 +567,14 @@ void ClientEnvironment::damageLocalPlayer(u16 damage, bool handle_hp) m_client_event_queue.push(event); } +void ClientEnvironment::updateLocalPlayerBreath(u16 breath) +{ + ClientEnvEvent event; + event.type = CEE_PLAYER_BREATH; + event.player_breath.amount = breath; + m_client_event_queue.push(event); +} + /* Client likes to call these */ diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index f8f37cfd0..43429ac52 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -43,7 +43,8 @@ class LocalPlayer; enum ClientEnvEventType { CEE_NONE, - CEE_PLAYER_DAMAGE + CEE_PLAYER_DAMAGE, + CEE_PLAYER_BREATH }; struct ClientEnvEvent @@ -56,6 +57,9 @@ struct ClientEnvEvent u16 amount; bool send_to_server; } player_damage; + struct{ + u16 amount; + } player_breath; }; }; @@ -113,6 +117,7 @@ public: */ void damageLocalPlayer(u16 damage, bool handle_hp=true); + void updateLocalPlayerBreath(u16 breath); /* Client likes to call these @@ -151,6 +156,9 @@ private: std::vector m_simple_objects; std::queue m_client_event_queue; IntervalLimiter m_active_object_light_update_interval; + IntervalLimiter m_lava_hurt_interval; + IntervalLimiter m_drowning_interval; + IntervalLimiter m_breathing_interval; std::list m_player_names; v3s16 m_camera_offset; }; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index fc4a2c0a4..88292368f 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -354,6 +354,8 @@ void GenericCAO::initialize(const std::string &data) m_is_visible = false; player->setCAO(this); } + if (m_client->getProtoVersion() < 33) + m_env->addPlayerName(m_name.c_str()); } m_enable_shaders = g_settings->getBool("enable_shaders"); @@ -362,21 +364,25 @@ void GenericCAO::initialize(const std::string &data) void GenericCAO::processInitData(const std::string &data) { std::istringstream is(data, std::ios::binary); + const u8 version = readU8(is); + const u16 protocol_version = m_client->getProtoVersion(); - if (version < 1) { - errorstream << "GenericCAO: Unsupported init data version" - << std::endl; - return; - } - - // PROTOCOL_VERSION >= 37 + // PROTOCOL_VERSION >= 14 m_name = deSerializeString(is); m_is_player = readU8(is); - m_id = readU16(is); - m_position = readV3F32(is); - m_rotation = readV3F32(is); - m_hp = readU16(is); + if (protocol_version >= 37) { + m_id = readU16(is); + m_position = readV3F32(is); + m_rotation = readV3F32(is); + m_hp = readU16(is); + } else { + if (version >= 1) + m_id = readS16(is); + m_position = readV3F1000(is); + m_rotation = v3f(0.0, readF1000(is), 0.0); + m_hp = readS16(is); + } const u8 num_messages = readU8(is); @@ -393,6 +399,8 @@ void GenericCAO::processInitData(const std::string &data) GenericCAO::~GenericCAO() { + if (m_is_player && m_client->getProtoVersion() < 33) + m_env->removePlayerName(m_name.c_str()); removeFromScene(true); } @@ -653,8 +661,9 @@ void GenericCAO::addToScene(ITextureSource *tsrc) video::S3DVertex( dx, dy, 0, 0,0,1, c, 0,0), video::S3DVertex(-dx, dy, 0, 0,0,1, c, 1,0), }; - if (m_is_player) { + if (m_is_player && m_client->getProtoVersion() >= 36) { // Move minimal Y position to 0 (feet position) + // This should not be done on Minetest 0.4 servers for (video::S3DVertex &vertex : vertices) vertex.Pos.Y += dy; } @@ -684,8 +693,9 @@ void GenericCAO::addToScene(ITextureSource *tsrc) video::S3DVertex(-dx, dy, 0, 0,0,-1, c, 0,0), video::S3DVertex( dx, dy, 0, 0,0,-1, c, 1,0), }; - if (m_is_player) { + if (m_is_player && m_client->getProtoVersion() >= 36) { // Move minimal Y position to 0 (feet position) + // This should not be done on Minetest 0.4 servers for (video::S3DVertex &vertex : vertices) vertex.Pos.Y += dy; } @@ -754,7 +764,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc) setSceneNodeMaterial(m_animated_meshnode); m_animated_meshnode->setMaterialFlag(video::EMF_BACK_FACE_CULLING, - m_prop.backface_culling); + !m_is_player && m_prop.backface_culling); } else errorstream<<"GenericCAO::addToScene(): Could not load mesh "<getLocalPlayer(); m_position = player->getPosition(); + if (m_client->getProtoVersion() < 36) + m_position += v3f(0,BS,0); pos_translator.val_current = m_position; m_rotation.Y = wrapDegrees_0_360(player->getYaw()); rot_translator.val_current = m_rotation; @@ -1556,6 +1568,7 @@ void GenericCAO::processMessage(const std::string &data) std::istringstream is(data, std::ios::binary); // command u8 cmd = readU8(is); + const u16 protocol_version = m_client->getProtoVersion(); if (cmd == AO_CMD_SET_PROPERTIES) { ObjectProperties newprops; newprops.deSerialize(is); @@ -1581,10 +1594,14 @@ void GenericCAO::processMessage(const std::string &data) if (m_is_local_player) { LocalPlayer *player = m_env->getLocalPlayer(); player->makes_footstep_sound = m_prop.makes_footstep_sound; - aabb3f collision_box = m_prop.collisionbox; - collision_box.MinEdge *= BS; - collision_box.MaxEdge *= BS; - player->setCollisionbox(collision_box); + // Only set the collision box on Minetest 5.0.0+ to ensure + // compatibility with 0.4. + if (protocol_version >= 36) { + aabb3f collision_box = m_prop.collisionbox; + collision_box.MinEdge *= BS; + collision_box.MaxEdge *= BS; + player->setCollisionbox(collision_box); + } player->setEyeHeight(m_prop.eye_height); player->setZoomFOV(m_prop.zoom_fov); } @@ -1607,15 +1624,19 @@ void GenericCAO::processMessage(const std::string &data) } else if (cmd == AO_CMD_UPDATE_POSITION) { // Not sent by the server if this object is an attachment. // We might however get here if the server notices the object being detached before the client. - m_position = readV3F32(is); - m_velocity = readV3F32(is); - m_acceleration = readV3F32(is); - m_rotation = readV3F32(is); + m_position = readV3F(is, protocol_version); + m_velocity = readV3F(is, protocol_version); + m_acceleration = readV3F(is, protocol_version); + if (protocol_version >= 37) + m_rotation = readV3F32(is); + else + m_rotation = v3f(0.0, readF1000(is), 0.0); + m_rotation = wrapDegrees_0_360_v3f(m_rotation); bool do_interpolate = readU8(is); bool is_end_position = readU8(is); - float update_interval = readF32(is); + float update_interval = readF(is, protocol_version); // Place us a bit higher if we're physical, to not sink into // the ground due to sucky collision detection... @@ -1646,7 +1667,7 @@ void GenericCAO::processMessage(const std::string &data) } else if (cmd == AO_CMD_SET_SPRITE) { v2s16 p = readV2S16(is); int num_frames = readU16(is); - float framelength = readF32(is); + float framelength = readF(is, protocol_version); bool select_horiz_by_yawpitch = readU8(is); m_tx_basepos = p; @@ -1656,9 +1677,9 @@ void GenericCAO::processMessage(const std::string &data) updateTexturePos(); } else if (cmd == AO_CMD_SET_PHYSICS_OVERRIDE) { - float override_speed = readF32(is); - float override_jump = readF32(is); - float override_gravity = readF32(is); + float override_speed = readF(is, protocol_version); + float override_jump = readF(is, protocol_version); + float override_gravity = readF(is, protocol_version); // these are sent inverted so we get true when the server sends nothing bool sneak = !readU8(is); bool sneak_glitch = !readU8(is); @@ -1677,11 +1698,11 @@ void GenericCAO::processMessage(const std::string &data) } } else if (cmd == AO_CMD_SET_ANIMATION) { // TODO: change frames send as v2s32 value - v2f range = readV2F32(is); + v2f range = readV2F(is, protocol_version); if (!m_is_local_player) { m_animation_range = v2s32((s32)range.X, (s32)range.Y); - m_animation_speed = readF32(is); - m_animation_blend = readF32(is); + m_animation_speed = readF(is, protocol_version); + m_animation_blend = readF(is, protocol_version); // these are sent inverted so we get true when the server sends nothing m_animation_loop = !readU8(is); updateAnimation(); @@ -1690,8 +1711,8 @@ void GenericCAO::processMessage(const std::string &data) if(player->last_animation == NO_ANIM) { m_animation_range = v2s32((s32)range.X, (s32)range.Y); - m_animation_speed = readF32(is); - m_animation_blend = readF32(is); + m_animation_speed = readF(is, protocol_version); + m_animation_blend = readF(is, protocol_version); // these are sent inverted so we get true when the server sends nothing m_animation_loop = !readU8(is); } @@ -1710,20 +1731,20 @@ void GenericCAO::processMessage(const std::string &data) } } } else if (cmd == AO_CMD_SET_ANIMATION_SPEED) { - m_animation_speed = readF32(is); + m_animation_speed = readF(is, protocol_version); updateAnimationSpeed(); } else if (cmd == AO_CMD_SET_BONE_POSITION) { std::string bone = deSerializeString(is); - v3f position = readV3F32(is); - v3f rotation = readV3F32(is); + v3f position = readV3F(is, protocol_version); + v3f rotation = readV3F(is, protocol_version); m_bone_position[bone] = core::vector2d(position, rotation); // updateBonePosition(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); std::string bone = deSerializeString(is); - v3f position = readV3F32(is); - v3f rotation = readV3F32(is); + v3f position = readV3F(is, protocol_version); + v3f rotation = readV3F(is, protocol_version); setAttachment(parent_id, bone, position, rotation); @@ -1732,6 +1753,11 @@ void GenericCAO::processMessage(const std::string &data) m_is_visible = !m_attached_to_local; } else if (cmd == AO_CMD_PUNCHED) { u16 result_hp = readU16(is); + if (protocol_version < 37) { + // This is not a bug, the above readU16() is intentionally executed + // on older protocols as there used to be a damage value sent. + result_hp = readS16(is); + } // Use this instead of the send damage to not interfere with prediction s32 damage = (s32)m_hp - (s32)result_hp; diff --git a/src/client/game.cpp b/src/client/game.cpp index dcfdd9bc9..f6f8f8294 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2580,6 +2580,11 @@ void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam) void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam) { + // Don't do anything if proto_ver < 36 and the player is dead. + // This reverts the change introduced in dcd1a15 for old servers. + if (client->getProtoVersion() < 36 && client->getHP() == 0) + return; + if (client->modsLoaded()) client->getScript()->on_damage_taken(event->player_damage.amount); diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index e4a9179a7..c863cd0cc 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -715,6 +715,14 @@ ClientActiveObject *LocalPlayer::getParent() const return m_cao ? m_cao->getParent() : nullptr; } +float LocalPlayer::getZoomFOV() const +{ + // OH nO, The ZOOm FoV IS nOt COnFIguRABLE On oLDER ServErS. + if (m_client && m_client->getProtoVersion() < 36) + return m_client->checkPrivilege("zoom") ? 15.0f : 0.0f; + return m_zoom_fov; +} + bool LocalPlayer::isDead() const { FATAL_ERROR_IF(!getCAO(), "LocalPlayer's CAO isn't initialized"); diff --git a/src/client/localplayer.h b/src/client/localplayer.h index e6b741c33..4d9f1fa38 100644 --- a/src/client/localplayer.h +++ b/src/client/localplayer.h @@ -146,7 +146,7 @@ public: const aabb3f& getCollisionbox() const { return m_collisionbox; } - float getZoomFOV() const { return m_zoom_fov; } + float getZoomFOV() const; void setZoomFOV(float zoom_fov) { m_zoom_fov = zoom_fov; } bool getAutojump() const { return m_autojump; } diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 8a45cf61a..2f0badbe1 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -132,7 +132,7 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const os << serializeString(description); os << serializeString(inventory_image); os << serializeString(wield_image); - writeV3F32(os, wield_scale); + writeV3F(os, wield_scale, protocol_version); writeS16(os, stack_max); writeU8(os, usable); writeU8(os, liquids_pointable); @@ -157,7 +157,7 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const sound_place.serialize(os, CONTENTFEATURES_VERSION); sound_place_failed.serialize(os, CONTENTFEATURES_VERSION); - writeF32(os, range); + writeF(os, range, protocol_version); os << serializeString(palette_image); writeARGB8(os, color); os << serializeString(inventory_overlay); @@ -171,7 +171,7 @@ void ItemDefinition::deSerialize(std::istream &is) // Deserialize int version = readU8(is); - if (version < 6) + if (version < 1 || (version > 3 && version < 6)) throw SerializationError("unsupported ItemDefinition version"); type = (enum ItemType)readU8(is); @@ -179,7 +179,10 @@ void ItemDefinition::deSerialize(std::istream &is) description = deSerializeString(is); inventory_image = deSerializeString(is); wield_image = deSerializeString(is); - wield_scale = readV3F32(is); + if (version >= 6) + wield_scale = readV3F32(is); + else + wield_scale = readV3F1000(is); stack_max = readS16(is); usable = readU8(is); liquids_pointable = readU8(is); @@ -199,22 +202,38 @@ void ItemDefinition::deSerialize(std::istream &is) groups[name] = value; } + if (version < 2) { + // We can't be sure that node_placement_prediction is sent in version 1. + try { + node_placement_prediction = deSerializeString(is); + } catch (SerializationError &e) {}; + sound_place.name = "default_place_node"; + sound_place.gain = 0.5; + return; + } + node_placement_prediction = deSerializeString(is); // Version from ContentFeatures::serialize to keep in sync - sound_place.deSerialize(is, CONTENTFEATURES_VERSION); - sound_place_failed.deSerialize(is, CONTENTFEATURES_VERSION); - - range = readF32(is); - palette_image = deSerializeString(is); - color = readARGB8(is); - inventory_overlay = deSerializeString(is); - wield_overlay = deSerializeString(is); + const u8 cf_version = version >= 6 ? CONTENTFEATURES_VERSION : 9; + sound_place.deSerialize(is, cf_version); // If you add anything here, insert it primarily inside the try-catch // block to not need to increase the version. - //try { - //} catch(SerializationError &e) {}; + try { + if (version >= 6) { + sound_place_failed.deSerialize(is, cf_version); + range = readF32(is); + } else { + if (version >= 3) + range = readF1000(is); + sound_place_failed.deSerialize(is, cf_version); + } + palette_image = deSerializeString(is); + color = readARGB8(is); + inventory_overlay = deSerializeString(is); + wield_overlay = deSerializeString(is); + } catch(SerializationError &e) {}; } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index e0d2cc726..a358699bc 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -72,7 +72,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = null_command_handler, null_command_handler, { "TOCLIENT_CHAT_MESSAGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x2F - null_command_handler, // 0x30 + { "TOCLIENT_CHAT_MESSAGE_OLD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessageOld }, // 0x30 { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectRemoveAdd }, // 0x31 { "TOCLIENT_ACTIVE_OBJECT_MESSAGES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectMessages }, // 0x32 { "TOCLIENT_HP", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33 diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 2e6cd0da7..fd770fcf2 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -394,6 +394,35 @@ void Client::handleCommand_TimeOfDay(NetworkPacket* pkt) // << " dr=" << dr << std::endl; } +void Client::handleCommand_ChatMessageOld(NetworkPacket* pkt) +{ + /* + u16 command + u16 length + wstring message + */ + u16 len, read_wchar; + + *pkt >> len; + + std::wstring message; + for (u32 i = 0; i < len; i++) { + *pkt >> read_wchar; + message += (wchar_t)read_wchar; + } + + ChatMessage *chatMessage = new ChatMessage(message); + + // @TODO send this to CSM using ChatMessage object + 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; + } else { + pushToChatQueue(chatMessage); + } +} + void Client::handleCommand_ChatMessage(NetworkPacket *pkt) { /* @@ -557,7 +586,13 @@ void Client::handleCommand_HP(NetworkPacket *pkt) u16 oldhp = player->hp; u16 hp; - *pkt >> hp; + if (m_proto_ver >= 37) { + *pkt >> hp; + } else { + u8 raw_hp; + *pkt >> raw_hp; + hp = raw_hp; + } player->hp = hp; @@ -907,6 +942,27 @@ void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt) void Client::handleCommand_DetachedInventory(NetworkPacket* pkt) { + // TODO: Unify this legacy code with the new code. + if (m_proto_ver < 37) { + std::string datastring(pkt->getString(0), pkt->getSize()); + std::istringstream is(datastring, std::ios_base::binary); + + std::string name = deSerializeString(is); + + infostream << "Client: Detached inventory update: \"" << name + << "\"" << std::endl; + + Inventory *inv = NULL; + if (m_detached_inventories.count(name) > 0) + inv = m_detached_inventories[name]; + else { + inv = new Inventory(m_itemdef); + m_detached_inventories[name] = inv; + } + inv->deSerialize(is); + return; + } + std::string name; bool keep_inv = true; *pkt >> name >> keep_inv; @@ -979,17 +1035,17 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) 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.time = readF(is, m_proto_ver); + p.minpos = readV3F(is, m_proto_ver); + p.maxpos = readV3F(is, m_proto_ver); + p.minvel = readV3F(is, m_proto_ver); + p.maxvel = readV3F(is, m_proto_ver); + p.minacc = readV3F(is, m_proto_ver); + p.maxacc = readV3F(is, m_proto_ver); + p.minexptime = readF(is, m_proto_ver); + p.maxexptime = readF(is, m_proto_ver); + p.minsize = readF(is, m_proto_ver); + p.maxsize = readF(is, m_proto_ver); p.collisiondetection = readU8(is); p.texture = deSerializeLongString(is); @@ -1000,7 +1056,7 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) attached_id = readU16(is); - p.animation.deSerialize(is, m_proto_ver); + p.animation.deSerializeWithProtoVer(is, m_proto_ver); p.glow = readU8(is); p.object_collision = readU8(is); diff --git a/src/network/connection.h b/src/network/connection.h index cc74880fe..3dda11fc4 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -178,6 +178,7 @@ controltype and data description: #define CONTROLTYPE_SET_PEER_ID 1 #define CONTROLTYPE_PING 2 #define CONTROLTYPE_DISCO 3 +#define CONTROLTYPE_ENABLE_BIG_SEND_WINDOW 4 /* ORIGINAL: This is a plain packet with no control and no error @@ -315,7 +316,8 @@ enum ConnectionCommandType{ CONNCMD_SEND, CONNCMD_SEND_TO_ALL, CONCMD_ACK, - CONCMD_CREATE_PEER + CONCMD_CREATE_PEER, + CONCMD_DISABLE_LEGACY }; struct ConnectionCommand @@ -382,6 +384,16 @@ struct ConnectionCommand reliable = true; raw = true; } + + void disableLegacy(session_t peer_id_, const SharedBuffer &data_) + { + type = CONCMD_DISABLE_LEGACY; + peer_id = peer_id_; + data = data_; + channelnum = 0; + reliable = true; + raw = true; + } }; /* maximum window size to use, 0xFFFF is theoretical maximum. don't think about diff --git a/src/network/connectionthreads.cpp b/src/network/connectionthreads.cpp index 6495b82b6..d2a6857b3 100644 --- a/src/network/connectionthreads.cpp +++ b/src/network/connectionthreads.cpp @@ -428,6 +428,15 @@ void ConnectionSendThread::processReliableCommand(ConnectionCommand &c) } return; + case CONCMD_DISABLE_LEGACY: + LOG(dout_con << m_connection->getDesc() + << "UDP processing reliable CONCMD_DISABLE_LEGACY" << std::endl); + if (!rawSendAsPacket(c.peer_id, c.channelnum, c.data, c.reliable)) { + /* put to queue if we couldn't send it immediately */ + sendReliable(c); + } + return; + case CONNCMD_SERVE: case CONNCMD_CONNECT: case CONNCMD_DISCONNECT: @@ -1204,6 +1213,17 @@ SharedBuffer ConnectionReceiveThread::handlePacketType_Control(Channel *chan m_connection->SetPeerID(peer_id_new); } + // Request the server disables "legacy peer mode" (0.4.X servers + // throttle outgoing packets if this does not happen). + // Minetest 5.X servers *should* ignore this. + ConnectionCommand cmd; + + SharedBuffer reply(2); + writeU8(&reply[0], PACKET_TYPE_CONTROL); + writeU8(&reply[1], CONTROLTYPE_ENABLE_BIG_SEND_WINDOW); + cmd.disableLegacy(PEER_ID_SERVER, reply); + m_connection->putCommand(cmd); + throw ProcessedSilentlyException("Got a SET_PEER_ID"); } else if (controltype == CONTROLTYPE_PING) { // Just ignore it, the incoming data already reset diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index 4d13ebcac..99ceb9bfc 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -23,6 +23,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "networkprotocol.h" +NetworkPacket::NetworkPacket(u16 command, u32 datasize, session_t peer_id, + u16 protocol_version): +m_datasize(datasize), m_command(command), m_peer_id(peer_id), + m_protocol_version(protocol_version) +{ + if (m_protocol_version == 0) + m_protocol_version = 37; + m_data.resize(m_datasize); +} + NetworkPacket::NetworkPacket(u16 command, u32 datasize, session_t peer_id): m_datasize(datasize), m_command(command), m_peer_id(peer_id) { @@ -294,7 +304,7 @@ NetworkPacket& NetworkPacket::operator<<(float src) { checkDataSize(4); - writeF32(&m_data[m_read_offset], src); + writeF(&m_data[m_read_offset], src, m_protocol_version); m_read_offset += 4; return *this; @@ -379,7 +389,7 @@ NetworkPacket& NetworkPacket::operator>>(float& dst) { checkReadOffset(m_read_offset, 4); - dst = readF32(&m_data[m_read_offset]); + dst = readF(&m_data[m_read_offset], m_protocol_version); m_read_offset += 4; return *this; @@ -389,7 +399,7 @@ NetworkPacket& NetworkPacket::operator>>(v2f& dst) { checkReadOffset(m_read_offset, 8); - dst = readV2F32(&m_data[m_read_offset]); + dst = readV2F(&m_data[m_read_offset], m_protocol_version); m_read_offset += 8; return *this; @@ -399,7 +409,7 @@ NetworkPacket& NetworkPacket::operator>>(v3f& dst) { checkReadOffset(m_read_offset, 12); - dst = readV3F32(&m_data[m_read_offset]); + dst = readV3F(&m_data[m_read_offset], m_protocol_version); m_read_offset += 12; return *this; diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index f9df243a5..19c06113b 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -28,6 +28,7 @@ class NetworkPacket { public: + NetworkPacket(u16 command, u32 datasize, session_t peer_id, u16 protocol_version); NetworkPacket(u16 command, u32 datasize, session_t peer_id); NetworkPacket(u16 command, u32 datasize); NetworkPacket() = default; @@ -118,6 +119,11 @@ public: // Temp, we remove SharedBuffer when migration finished SharedBuffer oldForgePacket(); + inline void setProtocolVersion(const u16 protocol_version) + { + m_protocol_version = protocol_version; + } + private: void checkReadOffset(u32 from_offset, u32 field_size); @@ -134,4 +140,5 @@ private: u32 m_read_offset = 0; u16 m_command = 0; session_t m_peer_id = 0; + u16 m_protocol_version = 37; }; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 391b3eb75..f4c39a992 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -216,7 +216,7 @@ with this program; if not, write to the Free Software Foundation, Inc., // Client's supported network protocol range // The minimal version depends on whether // send_pre_v25_init is enabled or not -#define CLIENT_PROTOCOL_VERSION_MIN 37 +#define CLIENT_PROTOCOL_VERSION_MIN 25 #define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION // Constant that differentiates the protocol from random data and other protocols diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 0730f2073..80a2d5388 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -133,32 +133,34 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const void NodeBox::deSerialize(std::istream &is) { int version = readU8(is); - if (version < 6) + if (version < 1 || (version > 3 && version < 6)) throw SerializationError("unsupported NodeBox version"); reset(); type = (enum NodeBoxType)readU8(is); + // An approximate protocol version. + const u16 protocol_version = version > 3 ? 37 : 32; if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED) { u16 fixed_count = readU16(is); while(fixed_count--) { aabb3f box; - box.MinEdge = readV3F32(is); - box.MaxEdge = readV3F32(is); + box.MinEdge = readV3F(is, protocol_version); + box.MaxEdge = readV3F(is, protocol_version); fixed.push_back(box); } } else if(type == NODEBOX_WALLMOUNTED) { - wall_top.MinEdge = readV3F32(is); - wall_top.MaxEdge = readV3F32(is); - wall_bottom.MinEdge = readV3F32(is); - wall_bottom.MaxEdge = readV3F32(is); - wall_side.MinEdge = readV3F32(is); - wall_side.MaxEdge = readV3F32(is); + wall_top.MinEdge = readV3F(is, protocol_version); + wall_top.MaxEdge = readV3F(is, protocol_version); + wall_bottom.MinEdge = readV3F(is, protocol_version); + wall_bottom.MaxEdge = readV3F(is, protocol_version); + wall_side.MinEdge = readV3F(is, protocol_version); + wall_side.MaxEdge = readV3F(is, protocol_version); } else if (type == NODEBOX_CONNECTED) { @@ -166,8 +168,8 @@ void NodeBox::deSerialize(std::istream &is) count = readU16(is); \ (box).reserve(count); \ while (count--) { \ - v3f min = readV3F32(is); \ - v3f max = readV3F32(is); \ + v3f min = readV3F(is, protocol_version); \ + v3f max = readV3F(is, protocol_version); \ (box).emplace_back(min, max); }; } u16 count; @@ -179,6 +181,8 @@ void NodeBox::deSerialize(std::istream &is) READBOXES(connect_left); READBOXES(connect_back); READBOXES(connect_right); + if (version <= 3) + return; READBOXES(disconnected_top); READBOXES(disconnected_bottom); READBOXES(disconnected_front); @@ -239,17 +243,37 @@ void TileDef::deSerialize(std::istream &is, u8 contentfeatures_version, NodeDrawType drawtype) { int version = readU8(is); - if (version < 6) - throw SerializationError("unsupported TileDef version"); name = deSerializeString(is); animation.deSerialize(is, version); - u16 flags = readU16(is); - backface_culling = flags & TILE_FLAG_BACKFACE_CULLING; - tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL; - tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL; - has_color = flags & TILE_FLAG_HAS_COLOR; - bool has_scale = flags & TILE_FLAG_HAS_SCALE; - bool has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE; + + bool has_scale = false; + bool has_align_style = false; + if (version >= 6) { + u16 flags = readU16(is); + backface_culling = flags & TILE_FLAG_BACKFACE_CULLING; + tileable_horizontal = flags & TILE_FLAG_TILEABLE_HORIZONTAL; + tileable_vertical = flags & TILE_FLAG_TILEABLE_VERTICAL; + has_color = flags & TILE_FLAG_HAS_COLOR; + has_scale = flags & TILE_FLAG_HAS_SCALE; + has_align_style = flags & TILE_FLAG_HAS_ALIGN_STYLE; + } else { + if (version >= 1) + backface_culling = readU8(is); + if (version >= 2) { + tileable_horizontal = readU8(is); + tileable_vertical = readU8(is); + } + if (version >= 4) + has_color = readU8(is); + + if ((contentfeatures_version < 8) && + ((drawtype == NDT_MESH) || + (drawtype == NDT_FIRELIKE) || + (drawtype == NDT_LIQUID) || + (drawtype == NDT_PLANTLIKE))) + backface_culling = false; + } + if (has_color) { color.setRed(readU8(is)); color.setGreen(readU8(is)); @@ -499,12 +523,192 @@ void ContentFeatures::correctAlpha(TileDef *tiles, int length) } } +void ContentFeatures::deSerializeOld(std::istream &is, int version) +{ + if (version == 5) // In PROTOCOL_VERSION 13 + { + name = deSerializeString(is); + groups.clear(); + u32 groups_size = readU16(is); + for(u32 i=0; i 12) + visual_scale = readF32(is); + else + visual_scale = readF1000(is); if (readU8(is) != 6) throw SerializationError("unsupported tile count"); for (TileDef &td : tiledef) diff --git a/src/nodedef.h b/src/nodedef.h index 84d2ac32f..525f6e4d3 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -413,6 +413,7 @@ struct ContentFeatures void reset(); void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); + void deSerializeOld(std::istream &is, int version); /*! * Since vertex alpha is no longer supported, this method * adds opacity directly to the texture pixels. diff --git a/src/object_properties.cpp b/src/object_properties.cpp index d53b6dee0..af3c7f26a 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -126,51 +126,72 @@ void ObjectProperties::serialize(std::ostream &os) const void ObjectProperties::deSerialize(std::istream &is) { int version = readU8(is); - if (version != 4) + if (version != 1 && version != 4) throw SerializationError("unsupported ObjectProperties version"); - hp_max = readU16(is); - physical = readU8(is); - readU32(is); // removed property (weight) - collisionbox.MinEdge = readV3F32(is); - collisionbox.MaxEdge = readV3F32(is); - selectionbox.MinEdge = readV3F32(is); - selectionbox.MaxEdge = readV3F32(is); - pointable = readU8(is); - visual = deSerializeString(is); - visual_size = readV3F32(is); - textures.clear(); - u32 texture_count = readU16(is); - for (u32 i = 0; i < texture_count; i++){ - textures.push_back(deSerializeString(is)); - } - spritediv = readV2S16(is); - initial_sprite_basepos = readV2S16(is); - is_visible = readU8(is); - makes_footstep_sound = readU8(is); - automatic_rotate = readF32(is); - mesh = deSerializeString(is); - colors.clear(); - u32 color_count = readU16(is); - for (u32 i = 0; i < color_count; i++){ - colors.push_back(readARGB8(is)); - } - collideWithObjects = readU8(is); - stepheight = readF32(is); - automatic_face_movement_dir = readU8(is); - automatic_face_movement_dir_offset = readF32(is); - backface_culling = readU8(is); - nametag = deSerializeString(is); - nametag_color = readARGB8(is); - automatic_face_movement_max_rotation_per_sec = readF32(is); - infotext = deSerializeString(is); - wield_item = deSerializeString(is); - glow = readS8(is); - breath_max = readU16(is); - eye_height = readF32(is); - zoom_fov = readF32(is); - use_texture_alpha = readU8(is); + // Another approximate protocol version. + const u16 protocol_version = version == 1 ? 32 : 37; + try { + hp_max = readU16(is); + physical = readU8(is); + readU32(is); // removed property (weight) + collisionbox.MinEdge = readV3F(is, protocol_version); + collisionbox.MaxEdge = readV3F(is, protocol_version); + if (version >= 4) { + selectionbox.MinEdge = readV3F32(is); + selectionbox.MaxEdge = readV3F32(is); + pointable = readU8(is); + } else { + selectionbox.MinEdge = collisionbox.MinEdge; + selectionbox.MaxEdge = collisionbox.MaxEdge; + pointable = true; + } + visual = deSerializeString(is); + if (version == 1) { + v2f size = readV2F1000(is); + visual_size = v3f(size.X, size.Y, size.X); + } else { + visual_size = readV3F32(is); + } + textures.clear(); + u32 texture_count = readU16(is); + for (u32 i = 0; i < texture_count; i++){ + textures.push_back(deSerializeString(is)); + } + spritediv = readV2S16(is); + initial_sprite_basepos = readV2S16(is); + is_visible = readU8(is); + makes_footstep_sound = readU8(is); + automatic_rotate = readF(is, protocol_version); + mesh = deSerializeString(is); + colors.clear(); + u32 color_count = readU16(is); + for (u32 i = 0; i < color_count; i++){ + colors.push_back(readARGB8(is)); + } + collideWithObjects = readU8(is); + stepheight = readF(is, protocol_version); + automatic_face_movement_dir = readU8(is); + automatic_face_movement_dir_offset = readF(is, protocol_version); + backface_culling = readU8(is); + nametag = deSerializeString(is); + nametag_color = readARGB8(is); + automatic_face_movement_max_rotation_per_sec = readF(is, + protocol_version); + infotext = deSerializeString(is); + wield_item = deSerializeString(is); + + // The "glow" property exists in MultiCraft 1. + glow = readS8(is); + if (version == 1) + return; + + breath_max = readU16(is); + eye_height = readF32(is); + zoom_fov = readF32(is); + use_texture_alpha = readU8(is); + damage_texture_modifier = deSerializeString(is); u8 tmp = readU8(is); if (is.eof()) diff --git a/src/particles.cpp b/src/particles.cpp index a29a3438e..c8508c739 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -41,16 +41,18 @@ void ParticleParameters::serialize(std::ostream &os, u16 protocol_ver) const void ParticleParameters::deSerialize(std::istream &is, u16 protocol_ver) { - pos = readV3F32(is); - vel = readV3F32(is); - acc = readV3F32(is); - expirationtime = readF32(is); - size = readF32(is); + pos = readV3F(is, protocol_ver); + vel = readV3F(is, protocol_ver); + acc = readV3F(is, protocol_ver); + expirationtime = readF(is, protocol_ver); + size = readF(is, protocol_ver); collisiondetection = readU8(is); texture = deSerializeLongString(is); vertical = readU8(is); collision_removal = readU8(is); - animation.deSerialize(is, 6); /* NOT the protocol ver */ + animation.deSerializeWithProtoVer(is, protocol_ver); + + // TODO: Ensure this doesn't error glow = readU8(is); object_collision = readU8(is); // This is kinda awful diff --git a/src/sound.h b/src/sound.h index 5ab13e478..090b240c4 100644 --- a/src/sound.h +++ b/src/sound.h @@ -50,9 +50,16 @@ struct SimpleSoundSpec void deSerialize(std::istream &is, u8 cf_version) { name = deSerializeString(is); - gain = readF32(is); - pitch = readF32(is); - fade = readF32(is); + if (cf_version > 12) { + gain = readF32(is); + pitch = readF32(is); + fade = readF32(is); + } else { + // Minetest 0.4 compatibility + gain = readF1000(is); + pitch = 1.0f; + fade = 0.0f; + } } std::string name; diff --git a/src/tileanimation.cpp b/src/tileanimation.cpp index 79a7ad0a7..a78cb8cb3 100644 --- a/src/tileanimation.cpp +++ b/src/tileanimation.cpp @@ -37,17 +37,41 @@ void TileAnimationParams::deSerialize(std::istream &is, u8 tiledef_version) { type = (TileAnimationType) readU8(is); - if (type == TAT_VERTICAL_FRAMES) { + FATAL_ERROR_IF(tiledef_version >= 25, "TileAnimationParams::deSerialize " + "called with what is probably a protocol version."); + + // Approximate protocol version + u16 protocol_version = tiledef_version >= 6 ? 37 : 32; + + if (type == TAT_VERTICAL_FRAMES || tiledef_version <= 2) { vertical_frames.aspect_w = readU16(is); vertical_frames.aspect_h = readU16(is); - vertical_frames.length = readF32(is); + vertical_frames.length = readF(is, protocol_version); } else if (type == TAT_SHEET_2D) { sheet_2d.frames_w = readU8(is); sheet_2d.frames_h = readU8(is); - sheet_2d.frame_length = readF32(is); + sheet_2d.frame_length = readF(is, protocol_version); } } +void TileAnimationParams::deSerializeWithProtoVer(std::istream &is, + u16 protocol_version) +{ + u8 tiledef_version; + if (protocol_version >= 37) + tiledef_version = 6; + else if (protocol_version >= 30) + tiledef_version = 4; + else if (protocol_version >= 29) + tiledef_version = 3; + else if (protocol_version >= 26) + tiledef_version = 2; + else + tiledef_version = 1; + + deSerialize(is, tiledef_version); +} + void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms, v2u32 *frame_size) const { diff --git a/src/tileanimation.h b/src/tileanimation.h index 1bfd4f1cb..d6a5a4eec 100644 --- a/src/tileanimation.h +++ b/src/tileanimation.h @@ -52,6 +52,7 @@ struct TileAnimationParams void serialize(std::ostream &os, u8 tiledef_version) const; void deSerialize(std::istream &is, u8 tiledef_version); + void deSerializeWithProtoVer(std::istream &is, u16 protocol_version); void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms, v2u32 *frame_size) const; void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const; diff --git a/src/tool.cpp b/src/tool.cpp index f97b27e00..af0434bd5 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -90,10 +90,13 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const void ToolCapabilities::deSerialize(std::istream &is) { int version = readU8(is); - if (version < 4) + if (version != 1 && version != 2 && version < 4) throw SerializationError("unsupported ToolCapabilities version"); - full_punch_interval = readF32(is); + if (version > 3) + full_punch_interval = readF32(is); + else + full_punch_interval = readF1000(is); max_drop_level = readS16(is); groupcaps.clear(); u32 groupcaps_size = readU32(is); @@ -105,7 +108,11 @@ void ToolCapabilities::deSerialize(std::istream &is) u32 times_size = readU32(is); for(u32 i = 0; i < times_size; i++) { int level = readS16(is); - float time = readF32(is); + float time; + if (version > 3) + time = readF32(is); + else + time = readF1000(is); cap.times[level] = time; } groupcaps[name] = cap; @@ -306,4 +313,3 @@ f32 getToolRange(const ItemDefinition &def_selected, const ItemDefinition &def_h return max_d; } - diff --git a/src/util/serialize.h b/src/util/serialize.h index 87e32467f..187a09305 100644 --- a/src/util/serialize.h +++ b/src/util/serialize.h @@ -250,6 +250,14 @@ inline v3s32 readV3S32(const u8 *data) return p; } +inline v2f readV2F1000(const u8 *data) +{ + v2f p; + p.X = readF1000(&data[0]); + p.Y = readF1000(&data[4]); + return p; +} + inline v3f readV3F1000(const u8 *data) { v3f p; @@ -290,7 +298,7 @@ inline void writeS8(u8 *data, s8 i) inline void writeS16(u8 *data, s16 i) { - writeU16(data, (u16)i); + writeU16(data, (u16)i); } inline void writeS32(u8 *data, s32 i) @@ -357,6 +365,12 @@ inline void writeV3S32(u8 *data, v3s32 p) writeS32(&data[8], p.Z); } +inline void writeV2F1000(u8 *data, v2f p) +{ + writeF1000(&data[0], p.X); + writeF1000(&data[4], p.Y); +} + inline void writeV3F1000(u8 *data, v3f p) { writeF1000(&data[0], p.X); @@ -411,6 +425,7 @@ MAKE_STREAM_READ_FXN(v2s16, V2S16, 4); MAKE_STREAM_READ_FXN(v3s16, V3S16, 6); MAKE_STREAM_READ_FXN(v2s32, V2S32, 8); MAKE_STREAM_READ_FXN(v3s32, V3S32, 12); +MAKE_STREAM_READ_FXN(v2f, V2F1000, 8); MAKE_STREAM_READ_FXN(v3f, V3F1000, 12); MAKE_STREAM_READ_FXN(v2f, V2F32, 8); MAKE_STREAM_READ_FXN(v3f, V3F32, 12); @@ -430,11 +445,47 @@ MAKE_STREAM_WRITE_FXN(v2s16, V2S16, 4); MAKE_STREAM_WRITE_FXN(v3s16, V3S16, 6); MAKE_STREAM_WRITE_FXN(v2s32, V2S32, 8); MAKE_STREAM_WRITE_FXN(v3s32, V3S32, 12); +MAKE_STREAM_WRITE_FXN(v2f, V2F1000, 8); MAKE_STREAM_WRITE_FXN(v3f, V3F1000, 12); MAKE_STREAM_WRITE_FXN(v2f, V2F32, 8); MAKE_STREAM_WRITE_FXN(v3f, V3F32, 12); MAKE_STREAM_WRITE_FXN(video::SColor, ARGB8, 4); +// Make float functions +#define MAKE_FLOAT_FXNS(T, N) \ + inline T read ## N(u8 *data, const u16 protocol_version) \ + { \ + if (protocol_version > 36) \ + return read ## N ## 32(data); \ + else \ + return read ## N ## 1000(data); \ + } \ + inline T read ## N(std::istream &is, const u16 protocol_version) \ + { \ + if (protocol_version > 36) \ + return read ## N ## 32(is); \ + else \ + return read ## N ## 1000(is); \ + } \ + inline void write ## N(u8 *data, T val, const u16 protocol_version) \ + { \ + if (protocol_version > 36) \ + write ## N ## 32(data, val); \ + else \ + write ## N ## 1000(data, val); \ + } \ + inline void write ## N(std::ostream &os, T val, const u16 protocol_version) \ + { \ + if (protocol_version > 36) \ + write ## N ## 32(os, val); \ + else \ + write ## N ## 1000(os, val); \ + } + +MAKE_FLOAT_FXNS(f32, F); +MAKE_FLOAT_FXNS(v2f, V2F); +MAKE_FLOAT_FXNS(v3f, V3F); + //// //// More serialization stuff ////