From 4a9327f19547b7727fb87de19a64832fae7dd486 Mon Sep 17 00:00:00 2001 From: mckaygerhard Date: Mon, 25 Sep 2023 10:03:14 -0400 Subject: [PATCH] Revert "Drop support for connecting to MT 0.4 servers" for our finalminetest * This reverts commit f2ca6a6ca5e9dfe7bda3630a1ae41a94f7d02125. * now all clients can connect to older servers or server --- builtin/mainmenu/dlg_outdated_server.lua | 71 +++++++ builtin/mainmenu/init.lua | 1 + builtin/mainmenu/tab_online.lua | 17 ++ src/client/client.cpp | 55 ++++- 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 | 39 +++- src/network/clientopcodes.cpp | 2 +- src/network/clientpackethandler.cpp | 82 ++++++-- src/network/connection.h | 14 +- src/network/connectionthreads.cpp | 20 ++ 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 | 31 ++- src/tileanimation.h | 1 + src/tool.cpp | 13 +- 25 files changed, 787 insertions(+), 151 deletions(-) create mode 100644 builtin/mainmenu/dlg_outdated_server.lua diff --git a/builtin/mainmenu/dlg_outdated_server.lua b/builtin/mainmenu/dlg_outdated_server.lua new file mode 100644 index 000000000..1a84937ef --- /dev/null +++ b/builtin/mainmenu/dlg_outdated_server.lua @@ -0,0 +1,71 @@ +--MultiCraft +--Copyright (C) 2022 MultiCraft Development Team +-- +--This program is free software; you can redistribute it and/or modify +--it under the terms of the GNU Lesser General Public License as published by +--the Free Software Foundation; either version 3.0 of the License, or +--(at your option) any later version. +-- +--This program is distributed in the hope that it will be useful, +--but WITHOUT ANY WARRANTY; without even the implied warranty of +--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +--GNU Lesser General Public License for more details. +-- +--You should have received a copy of the GNU Lesser General Public License along +--with this program; if not, write to the Free Software Foundation, Inc., +--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +local function outdated_server_formspec(this) + return ([[ + style_type[image_button;content_offset=0] + image[4.9,0.3;2.5,2.5;%sattention.png] + style[msg;content_offset=0] + image_button[1,2.5;10,0.8;;msg;%s;false;false] + image_button[1,3.2;10,0.8;;msg;%s;false;false] + %s + button[2,4.5;4,0.8;cancel;%s] + %s + button[6,4.5;4,0.8;continue;%s] + ]]):format( + defaulttexturedir_esc, + fgettext("The server you are trying to connect to is outdated!"), + fgettext("Support for older servers may be removed at any time."), + btn_style("cancel"), + fgettext("Cancel"), + btn_style("continue", "yellow"), + fgettext("Join anyway") + ) +end + +local function outdated_server_buttonhandler(this, fields) + if fields.cancel then + this:delete() + return true + end + + if fields.continue then + serverlistmgr.add_favorite(this.server) + + gamedata.servername = this.server.name + gamedata.serverdescription = this.server.description + + core.settings:set_bool("auto_connect", false) + core.settings:set("connect_time", os.time()) + core.settings:set("maintab_LAST", "online") + core.settings:set("address", gamedata.address) + core.settings:set("remote_port", gamedata.port) + + core.start() + end +end + + +function create_outdated_server_dlg(server) + local retval = dialog_create("outdated_server_dlg", + outdated_server_formspec, + outdated_server_buttonhandler, + nil, true) + retval.server = server + + return retval +end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index bb4545ce6..188e57aba 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -47,6 +47,7 @@ dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua") dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua") dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") +dofile(menupath .. DIR_DELIM .. "dlg_outdated_server.lua") if not mobile then dofile(menupath .. DIR_DELIM .. "dlg_settings_advanced.lua") diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index e397c2104..297292ffb 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -165,6 +165,17 @@ local function get_formspec(tabview, name, tabdata) return retval end +local function is_favorite(server) + local favs = serverlistmgr.get_favorites() + for fav_id = 1, #favs do + if server.address == favs[fav_id].address and + server.port == favs[fav_id].port then + return true + end + end + return false +end + -------------------------------------------------------------------------------- local function main_button_handler(tabview, fields, name, tabdata) local serverlist = menudata.search_result or serverlistmgr.servers @@ -374,6 +385,12 @@ local function main_button_handler(tabview, fields, name, tabdata) if not is_server_protocol_compat_or_error( fav.proto_min, fav.proto_max) then return true + elseif fav.proto_max and fav.proto_max < 37 and not is_favorite(fav) then + local dlg = create_outdated_server_dlg(fav) + dlg:set_parent(tabview) + tabview:hide() + dlg:show() + return true end serverlistmgr.add_favorite(fav) diff --git a/src/client/client.cpp b/src/client/client.cpp index 2d6588f33..264a9f276 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -467,6 +467,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); } } @@ -850,6 +854,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); } @@ -968,7 +973,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(); @@ -1019,14 +1024,14 @@ AuthMechanism Client::choseAuthMech(const u32 mechs) void Client::sendInit(const std::string &playerName) { - NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size()) + 1); + NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size())); // we don't support network compression yet u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE; pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes; pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX; - pkt << playerName << (u8) 1; + pkt << playerName; Send(&pkt); } @@ -1263,8 +1268,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); } @@ -1328,7 +1352,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); @@ -1593,6 +1618,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 = utf8_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 03dec4bc9..4a5d5f2a3 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); @@ -241,6 +242,7 @@ public: void sendChangePassword(const std::string &oldpassword, const std::string &newpassword, const bool close_form = false); 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 9ebe442b5..80f4ba5ea 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -271,6 +271,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); + const 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); + const 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(); { @@ -449,6 +518,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 c7cd02871..056f1609d 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -357,6 +357,8 @@ void GenericCAO::initialize(const std::string &data) m_prop.show_on_minimap = false; } + if (m_client->getProtoVersion() < 33) + m_env->addPlayerName(m_name.c_str()); } m_enable_shaders = g_settings->getBool("enable_shaders"); @@ -365,21 +367,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 = deSerializeString16(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); @@ -396,6 +402,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); } @@ -675,8 +683,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; } @@ -706,8 +715,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; } @@ -776,7 +786,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; @@ -1626,6 +1638,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.show_on_minimap = m_is_player; // default @@ -1653,10 +1666,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); } @@ -1682,15 +1699,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... @@ -1721,7 +1742,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; @@ -1731,9 +1752,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); @@ -1752,11 +1773,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(); @@ -1765,8 +1786,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); } @@ -1785,25 +1806,30 @@ 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 = deSerializeString16(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 = deSerializeString16(is); - v3f position = readV3F32(is); - v3f rotation = readV3F32(is); + v3f position = readV3F(is, protocol_version); + v3f rotation = readV3F(is, protocol_version); bool force_visible = readU8(is); // Returns false for EOF setAttachment(parent_id, bone, position, rotation, force_visible); } 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 341151dc0..baccb1dcc 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2648,6 +2648,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 f3029c6c2..85f217189 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -743,6 +743,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 cc403a98f..c3f927362 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 cf2a7c8df..a7911e53e 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -192,7 +192,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); @@ -200,7 +200,10 @@ void ItemDefinition::deSerialize(std::istream &is) description = deSerializeString16(is); inventory_image = deSerializeString16(is); wield_image = deSerializeString16(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); @@ -220,21 +223,37 @@ 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 = deSerializeString16(is); + } catch (SerializationError &e) {}; + sound_place.name = "default_place_node"; + sound_place.gain = 0.5; + return; + } + node_placement_prediction = deSerializeString16(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 = deSerializeString16(is); - color = readARGB8(is); - inventory_overlay = deSerializeString16(is); - wield_overlay = deSerializeString16(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 { + 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 = deSerializeString16(is); + color = readARGB8(is); + inventory_overlay = deSerializeString16(is); + wield_overlay = deSerializeString16(is); short_description = deSerializeString16(is); } catch(SerializationError &e) {}; } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 6c3a097af..61897a207 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 f620a7de2..fee04f7d3 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -397,6 +397,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) { /* @@ -560,7 +589,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; @@ -916,6 +951,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 = deSerializeString16(is); + + infostream << "Client: Detached inventory update: \"" << name + << "\"" << std::endl; + + Inventory *inv = nullptr; + 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; @@ -988,17 +1044,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 = deSerializeString32(is); @@ -1009,7 +1065,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 2cf3b767f..e6330affd 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -177,6 +177,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 @@ -313,7 +314,8 @@ enum ConnectionCommandType{ CONNCMD_SEND, CONNCMD_SEND_TO_ALL, CONCMD_ACK, - CONCMD_CREATE_PEER + CONCMD_CREATE_PEER, + CONCMD_DISABLE_LEGACY }; struct ConnectionCommand @@ -368,6 +370,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 9e775774d..01187e1b5 100644 --- a/src/network/connectionthreads.cpp +++ b/src/network/connectionthreads.cpp @@ -405,6 +405,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: @@ -1185,6 +1194,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/networkprotocol.h b/src/network/networkprotocol.h index c20c91fb7..4cb21a5db 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -218,7 +218,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 f1100e2fa..019a96014 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -152,32 +152,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) { @@ -185,8 +187,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; @@ -198,6 +200,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); @@ -287,17 +291,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 = deSerializeString16(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)); @@ -572,12 +596,192 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, alpha); } +void ContentFeatures::deSerializeOld(std::istream &is, int version) +{ + if (version == 5) // In PROTOCOL_VERSION 13 + { + name = deSerializeString16(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 d0d88a338..7558fa8c7 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -423,6 +423,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); void serializeOld(std::ostream &os, u16 protocol_version) const; /* diff --git a/src/object_properties.cpp b/src/object_properties.cpp index f1cb455b6..b59e2f50a 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -222,51 +222,72 @@ void ObjectProperties::serialize(std::ostream &os, u16 protocol_version) 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 = deSerializeString16(is); - visual_size = readV3F32(is); - textures.clear(); - u32 texture_count = readU16(is); - for (u32 i = 0; i < texture_count; i++){ - textures.push_back(deSerializeString16(is)); - } - spritediv = readV2S16(is); - initial_sprite_basepos = readV2S16(is); - is_visible = readU8(is); - makes_footstep_sound = readU8(is); - automatic_rotate = readF32(is); - mesh = deSerializeString16(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 = deSerializeString16(is); - nametag_color = readARGB8(is); - automatic_face_movement_max_rotation_per_sec = readF32(is); - infotext = deSerializeString16(is); - wield_item = deSerializeString16(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 = deSerializeString16(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(deSerializeString16(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 = deSerializeString16(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 = deSerializeString16(is); + nametag_color = readARGB8(is); + automatic_face_movement_max_rotation_per_sec = readF(is, + protocol_version); + infotext = deSerializeString16(is); + wield_item = deSerializeString16(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 = deSerializeString16(is); u8 tmp = readU8(is); if (is.eof()) diff --git a/src/particles.cpp b/src/particles.cpp index abed728f1..9fa1ab71f 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 = deSerializeString32(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 dcf2afa96..02f191077 100644 --- a/src/sound.h +++ b/src/sound.h @@ -56,9 +56,16 @@ struct SimpleSoundSpec void deSerialize(std::istream &is, u8 cf_version) { name = deSerializeString16(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 89a04a388..bba45205a 100644 --- a/src/tileanimation.cpp +++ b/src/tileanimation.cpp @@ -49,17 +49,42 @@ 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 = fabs(readF32(is)); + // Don't hang if a negative length is sent + vertical_frames.length = fabs(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 = fabs(readF32(is)); + sheet_2d.frame_length = fabs(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 a8f3a8434..5796635ba 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -92,10 +92,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); @@ -107,7 +110,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;