diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index 20dd18044..b5bce81f0 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -195,8 +195,12 @@ core.register_entity(":__builtin:item", { return -- Don't do anything end - assert(moveresult, - "Collision info missing, this is caused by an out-of-date/buggy mod or game") + -- assert(moveresult, + -- "Collision info missing, this is caused by an out-of-date/buggy mod or game") + if not moveresult then + self.object:set_velocity({x=0, y=0, z=0}) + return + end if not moveresult.collides then -- future TODO: items should probably decelerate in air diff --git a/builtin/game/models/mc_compat_character.b3d b/builtin/game/models/mc_compat_character.b3d new file mode 100644 index 000000000..8147edc6d Binary files /dev/null and b/builtin/game/models/mc_compat_character.b3d differ diff --git a/src/activeobject.h b/src/activeobject.h index d97014c96..24576f358 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -44,15 +44,18 @@ enum ActiveObjectType { struct ActiveObjectMessage { - ActiveObjectMessage(u16 id_, bool reliable_=true, const std::string &data_ = "") : + ActiveObjectMessage(u16 id_, bool reliable_ = true, + const std::string &data_ = "", const std::string legacy_ = "") : id(id_), reliable(reliable_), - datastring(data_) + datastring(data_), + legacystring(legacy_) {} u16 id; bool reliable; std::string datastring; + std::string legacystring; }; enum ActiveObjectCommand { diff --git a/src/clientiface.cpp b/src/clientiface.cpp index e08df78f3..adf039232 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -750,8 +750,8 @@ void ClientInterface::sendToAllCompat(NetworkPacket *pkt, NetworkPacket *legacyp } else if (client->net_proto_version != 0) { pkt_to_send = legacypkt; } else { - warningstream << "Client with unhandled version to handle: '" - << client->net_proto_version << "'"; + // This will happen if a client is connecting when sendToAllCompat + // is called, this can safely be ignored. continue; } @@ -762,6 +762,34 @@ void ClientInterface::sendToAllCompat(NetworkPacket *pkt, NetworkPacket *legacyp } } +void ClientInterface::oldSendToAll(NetworkPacket *pkt) +{ + RecursiveMutexAutoLock clientslock(m_clients_mutex); + for (auto &client_it : m_clients) { + RemoteClient *client = client_it.second; + + if (client->net_proto_version != 0 && client->net_proto_version < 37) { + m_con->Send(client->peer_id, + clientCommandFactoryTable[pkt->getCommand()].channel, pkt, + clientCommandFactoryTable[pkt->getCommand()].reliable); + } + } +} + +void ClientInterface::newSendToAll(NetworkPacket *pkt) +{ + RecursiveMutexAutoLock clientslock(m_clients_mutex); + for (auto &client_it : m_clients) { + RemoteClient *client = client_it.second; + + if (client->net_proto_version > 36) { + m_con->Send(client->peer_id, + clientCommandFactoryTable[pkt->getCommand()].channel, pkt, + clientCommandFactoryTable[pkt->getCommand()].reliable); + } + } +} + RemoteClient* ClientInterface::getClientNoEx(session_t peer_id, ClientState state_min) { RecursiveMutexAutoLock clientslock(m_clients_mutex); diff --git a/src/clientiface.h b/src/clientiface.h index 3b272b26f..09897819b 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -458,6 +458,8 @@ public: /* send to all clients */ void sendToAll(NetworkPacket *pkt); void sendToAllCompat(NetworkPacket *pkt, NetworkPacket *legacypkt, u16 min_proto_ver); + void oldSendToAll(NetworkPacket *pkt); + void newSendToAll(NetworkPacket *pkt); /* delete a client */ void DeleteClient(session_t peer_id); diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 2f0badbe1..638bf0a45 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -126,6 +126,11 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const { // protocol_version >= 37 u8 version = 6; + + // protocol_version < 37 + if (protocol_version < 37) + version = 3; + writeU8(os, version); writeU8(os, type); os << serializeString(name); @@ -153,13 +158,25 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const os << serializeString(node_placement_prediction); - // Version from ContentFeatures::serialize to keep in sync - sound_place.serialize(os, CONTENTFEATURES_VERSION); - sound_place_failed.serialize(os, CONTENTFEATURES_VERSION); + if (version == 3) { + os << serializeString(sound_place.name); + writeF1000(os, sound_place.gain); + writeF1000(os, range); + os << serializeString(sound_place_failed.name); + writeF1000(os, sound_place_failed.gain); + } else { + // Version from ContentFeatures::serialize to keep in sync + 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); + + if (version == 3) + return; + os << serializeString(inventory_overlay); os << serializeString(wield_overlay); } diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 3b6f7a138..f0b6f4e77 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -355,7 +355,7 @@ static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes, } } -void MapBlock::serialize(std::ostream &os, u8 version, bool disk) +void MapBlock::serialize(std::ostream &os, u8 version, bool disk, std::string formspec_prepend) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapBlock format not supported"); @@ -411,7 +411,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk) Node metadata */ std::ostringstream oss(std::ios_base::binary); - m_node_metadata.serialize(oss, version, disk); + m_node_metadata.serialize(oss, version, disk, false, formspec_prepend); compressZlib(oss.str(), os); /* diff --git a/src/mapblock.h b/src/mapblock.h index ea05f85a3..b5272a31e 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -482,7 +482,8 @@ public: // These don't write or read version by itself // Set disk to true for on-disk format, false for over-the-network format // Precondition: version >= SER_FMT_VER_LOWEST_WRITE - void serialize(std::ostream &os, u8 version, bool disk); + void serialize(std::ostream &os, u8 version, bool disk, + std::string formspec_prepend=""); // If disk == true: In addition to doing other things, will add // unknown blocks from id-name mapping to wndef void deSerialize(std::istream &is, u8 version, bool disk); diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index 19c06113b..3e87809b4 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -44,6 +44,7 @@ public: u16 getCommand() { return m_command; } const u32 getRemainingBytes() const { return m_datasize - m_read_offset; } const char *getRemainingString() { return getString(m_read_offset); } + u16 getProtocolVersion() const { return m_protocol_version; } // Returns a c-string without copying. // A better name for this would be getRawString() diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index f4c39a992..2368e3c7c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -210,7 +210,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) // Server's supported network protocol range -#define SERVER_PROTOCOL_VERSION_MIN 37 +#define SERVER_PROTOCOL_VERSION_MIN 25 #define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION // Client's supported network protocol range diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 91e9c54c0..06620d6f4 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -89,7 +89,7 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = null_command_handler, // 0x3e null_command_handler, // 0x3f { "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40 - null_command_handler, // 0x41 + { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_Deprecated }, // 0x41 not used by the server since protocol version 23 null_command_handler, // 0x42 { "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43 null_command_handler, // 0x44 diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 5a58cbd51..06f253958 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -324,7 +324,7 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) sendDetachedInventories(peer_id, false); // Send player movement settings - SendMovement(peer_id); + SendMovement(peer_id, protocol_version); // Send time of day u16 time = m_env->getTimeOfDay(); @@ -454,7 +454,7 @@ void Server::handleCommand_GotBlocks(NetworkPacket* pkt) void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, NetworkPacket *pkt) { - if (pkt->getRemainingBytes() < 12 + 12 + 4 + 4 + 4 + 1 + 1) + if (pkt->getRemainingBytes() < 12 + 12 + 4 + 4) return; v3s32 ps, ss; @@ -474,10 +474,14 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, f32 fov = 0; u8 wanted_range = 0; - *pkt >> keyPressed; - *pkt >> f32fov; - fov = (f32)f32fov / 80.0f; - *pkt >> wanted_range; + if (pkt->getRemainingBytes() >= 4) + *pkt >> keyPressed; + if (pkt->getRemainingBytes() >= 1) { + *pkt >> f32fov; + fov = (f32)f32fov / 80.0f; + if (pkt->getRemainingBytes() >= 1) + *pkt >> wanted_range; + } v3f position((f32)ps.X / 100.0f, (f32)ps.Y / 100.0f, (f32)ps.Z / 100.0f); v3f speed((f32)ss.X / 100.0f, (f32)ss.Y / 100.0f, (f32)ss.Z / 100.0f); @@ -791,9 +795,6 @@ void Server::handleCommand_ChatMessage(NetworkPacket* pkt) void Server::handleCommand_Damage(NetworkPacket* pkt) { - u16 damage; - - *pkt >> damage; session_t peer_id = pkt->getPeerId(); RemotePlayer *player = m_env->getPlayer(peer_id); @@ -806,6 +807,17 @@ void Server::handleCommand_Damage(NetworkPacket* pkt) return; } + u16 damage; + + // Minetest 0.4 uses 8-bit integers for damage. + if (player->protocol_version >= 37) { + *pkt >> damage; + } else { + u8 raw_damage; + *pkt >> raw_damage; + damage = raw_damage; + } + PlayerSAO *playersao = player->getPlayerSAO(); if (playersao == NULL) { errorstream << @@ -1670,7 +1682,7 @@ void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) if (client->chosen_mech != AUTH_MECHANISM_SRP && client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD) { actionstream << "Server: got SRP _M packet, while auth" - << "is going on with mech " << client->chosen_mech << " from " + << "is going on with mech " << client->chosen_mech << " from " << addr_s << " (wantSudo=" << wantSudo << "). Denying." << std::endl; if (wantSudo) { DenySudoAccess(peer_id); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 80a2d5388..4acdc5508 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -74,7 +74,13 @@ void NodeBox::reset() void NodeBox::serialize(std::ostream &os, u16 protocol_version) const { // Protocol >= 36 - const u8 version = 6; + u8 version; + if (protocol_version >= 36) + version = 6; + else if (protocol_version >= 27) + version = 3; + else + version = 2; writeU8(os, version); switch (type) { @@ -84,28 +90,38 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const writeU16(os, fixed.size()); for (const aabb3f &nodebox : fixed) { - writeV3F32(os, nodebox.MinEdge); - writeV3F32(os, nodebox.MaxEdge); + writeV3F(os, nodebox.MinEdge, protocol_version); + writeV3F(os, nodebox.MaxEdge, protocol_version); } break; case NODEBOX_WALLMOUNTED: writeU8(os, type); - writeV3F32(os, wall_top.MinEdge); - writeV3F32(os, wall_top.MaxEdge); - writeV3F32(os, wall_bottom.MinEdge); - writeV3F32(os, wall_bottom.MaxEdge); - writeV3F32(os, wall_side.MinEdge); - writeV3F32(os, wall_side.MaxEdge); + writeV3F(os, wall_top.MinEdge, protocol_version); + writeV3F(os, wall_top.MaxEdge, protocol_version); + writeV3F(os, wall_bottom.MinEdge, protocol_version); + writeV3F(os, wall_bottom.MaxEdge, protocol_version); + writeV3F(os, wall_side.MinEdge, protocol_version); + writeV3F(os, wall_side.MaxEdge, protocol_version); break; case NODEBOX_CONNECTED: + if (version <= 2) { + // send old clients nodes that can't be walked through + // to prevent abuse + writeU8(os, NODEBOX_FIXED); + + writeU16(os, 1); + writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2)); + writeV3F1000(os, v3f(BS/2, BS/2, BS/2)); + break; + } writeU8(os, type); #define WRITEBOX(box) \ writeU16(os, (box).size()); \ for (const aabb3f &i: (box)) { \ - writeV3F32(os, i.MinEdge); \ - writeV3F32(os, i.MaxEdge); \ + writeV3F(os, i.MinEdge, protocol_version); \ + writeV3F(os, i.MaxEdge, protocol_version); \ }; WRITEBOX(fixed); @@ -115,14 +131,17 @@ void NodeBox::serialize(std::ostream &os, u16 protocol_version) const WRITEBOX(connect_left); WRITEBOX(connect_back); WRITEBOX(connect_right); - WRITEBOX(disconnected_top); - WRITEBOX(disconnected_bottom); - WRITEBOX(disconnected_front); - WRITEBOX(disconnected_left); - WRITEBOX(disconnected_back); - WRITEBOX(disconnected_right); - WRITEBOX(disconnected); - WRITEBOX(disconnected_sides); + + if (version > 5) { + WRITEBOX(disconnected_top); + WRITEBOX(disconnected_bottom); + WRITEBOX(disconnected_front); + WRITEBOX(disconnected_left); + WRITEBOX(disconnected_back); + WRITEBOX(disconnected_right); + WRITEBOX(disconnected); + WRITEBOX(disconnected_sides); + } break; default: writeU8(os, type); @@ -207,12 +226,41 @@ void NodeBox::deSerialize(std::istream &is) void TileDef::serialize(std::ostream &os, u16 protocol_version) const { - // protocol_version >= 36 - u8 version = 6; + u8 version; + if (protocol_version >= 37) + version = 6; + else if (protocol_version >= 30) + version = 4; + else if (protocol_version >= 29) + version = 3; + else if (protocol_version >= 26) + version = 2; + else + version = 1; + writeU8(os, version); os << serializeString(name); animation.serialize(os, version); + + // Compatibility with Minetest 0.4. + if (version < 6) { + writeU8(os, backface_culling); + if (version < 2) + return; + writeU8(os, tileable_horizontal); + writeU8(os, tileable_vertical); + if (version < 3) + return; + writeU8(os, has_color); + if (has_color) { + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + } + return; + } + bool has_scale = scale > 0; u16 flags = 0; if (backface_culling) @@ -424,7 +472,14 @@ void ContentFeatures::reset() void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const { - const u8 version = CONTENTFEATURES_VERSION; + if (protocol_version < 31) { + serializeOld(os, protocol_version); + return; + } + + u8 version = CONTENTFEATURES_VERSION; + if (protocol_version < 37) + version = 10; writeU8(os, version); // general @@ -440,7 +495,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const // visual writeU8(os, drawtype); os << serializeString(mesh); - writeF32(os, visual_scale); + writeF(os, visual_scale, protocol_version); writeU8(os, 6); for (const TileDef &td : tiledef) td.serialize(os, protocol_version); @@ -1125,6 +1180,105 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc } #endif +//// Serialization of old ContentFeatures formats +void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const +{ + u8 compatible_param_type_2 = param_type_2; + if ((protocol_version < 28) + && (compatible_param_type_2 == CPT2_MESHOPTIONS)) + compatible_param_type_2 = CPT2_NONE; + else if (protocol_version < 30) { + if (compatible_param_type_2 == CPT2_COLOR) + compatible_param_type_2 = CPT2_NONE; + else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR) + compatible_param_type_2 = CPT2_FACEDIR; + else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED) + compatible_param_type_2 = CPT2_WALLMOUNTED; + } + + float compatible_visual_scale = visual_scale; + if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) + compatible_visual_scale = sqrt(visual_scale); + + TileDef compatible_tiles[6]; + for (u8 i = 0; i < 6; i++) { + compatible_tiles[i] = tiledef[i]; + if (tiledef_overlay[i].name != "") { + std::stringstream s; + s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name + << ")"; + compatible_tiles[i].name = s.str(); + } + } + + // Protocol >= 24 + if (protocol_version < 31) { + const u8 version = protocol_version < 27 ? 7 : 8; + writeU8(os, version); + + os << serializeString(name); + writeU16(os, groups.size()); + for (ItemGroupList::const_iterator i = groups.begin(); + i != groups.end(); ++i) { + os << serializeString(i->first); + writeS16(os, i->second); + } + writeU8(os, drawtype); + writeF1000(os, compatible_visual_scale); + writeU8(os, 6); + for (u32 i = 0; i < 6; i++) + compatible_tiles[i].serialize(os, protocol_version); + writeU8(os, CF_SPECIAL_COUNT); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) + tiledef_special[i].serialize(os, protocol_version); + writeU8(os, alpha); + writeU8(os, post_effect_color.getAlpha()); + writeU8(os, post_effect_color.getRed()); + writeU8(os, post_effect_color.getGreen()); + writeU8(os, post_effect_color.getBlue()); + writeU8(os, param_type); + writeU8(os, compatible_param_type_2); + writeU8(os, is_ground_content); + writeU8(os, light_propagates); + writeU8(os, sunlight_propagates); + writeU8(os, walkable); + writeU8(os, pointable); + writeU8(os, diggable); + writeU8(os, climbable); + writeU8(os, buildable_to); + os << serializeString(""); // legacy: used to be metadata_name + writeU8(os, liquid_type); + os << serializeString(liquid_alternative_flowing); + os << serializeString(liquid_alternative_source); + writeU8(os, liquid_viscosity); + writeU8(os, liquid_renewable); + writeU8(os, light_source); + writeU32(os, damage_per_second); + node_box.serialize(os, protocol_version); + selection_box.serialize(os, protocol_version); + writeU8(os, legacy_facedir_simple); + writeU8(os, legacy_wallmounted); + sound_footstep.serialize(os, version); + sound_dig.serialize(os, version); + sound_dug.serialize(os, version); + writeU8(os, rightclickable); + writeU8(os, drowning); + writeU8(os, leveled); + writeU8(os, liquid_range); + writeU8(os, waving); + os << serializeString(mesh); + collision_box.serialize(os, protocol_version); + writeU8(os, floodable); + writeU16(os, connects_to_ids.size()); + for (u16 connects_to_id : connects_to_ids) + writeU16(os, connects_to_id); + writeU8(os, connect_sides); + } else { + throw SerializationError("ContentFeatures::serialize(): " + "Unsupported version requested"); + } +} + /* NodeDefManager */ diff --git a/src/nodedef.h b/src/nodedef.h index 525f6e4d3..711e8443a 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -414,6 +414,7 @@ struct ContentFeatures 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; /*! * Since vertex alpha is no longer supported, this method * adds opacity directly to the texture pixels. diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp index 801aa47fa..ef498c5a5 100644 --- a/src/nodemetadata.cpp +++ b/src/nodemetadata.cpp @@ -40,7 +40,8 @@ NodeMetadata::~NodeMetadata() delete m_inventory; } -void NodeMetadata::serialize(std::ostream &os, u8 version, bool disk) const +void NodeMetadata::serialize(std::ostream &os, u8 version, bool disk, + std::string formspec_prepend) const { int num_vars = disk ? m_stringvars.size() : countNonPrivate(); writeU32(os, num_vars); @@ -50,7 +51,11 @@ void NodeMetadata::serialize(std::ostream &os, u8 version, bool disk) const continue; os << serializeString(sv.first); - os << serializeLongString(sv.second); + if (!formspec_prepend.empty() && sv.first == "formspec" && + sv.second.find("no_prepend[]") == std::string::npos) + os << serializeLongString(sv.second + formspec_prepend); + else + os << serializeLongString(sv.second); if (version >= 2) writeU8(os, (priv) ? 1 : 0); } @@ -113,7 +118,7 @@ int NodeMetadata::countNonPrivate() const */ void NodeMetadataList::serialize(std::ostream &os, u8 blockver, bool disk, - bool absolute_pos) const + bool absolute_pos, std::string formspec_prepend) const { /* Version 0 is a placeholder for "nothing to see here; go away." @@ -146,7 +151,7 @@ void NodeMetadataList::serialize(std::ostream &os, u8 blockver, bool disk, u16 p16 = (p.Z * MAP_BLOCKSIZE + p.Y) * MAP_BLOCKSIZE + p.X; writeU16(os, p16); } - data->serialize(os, version, disk); + data->serialize(os, version, disk, formspec_prepend); } } diff --git a/src/nodemetadata.h b/src/nodemetadata.h index 17e47013f..04c3cad90 100644 --- a/src/nodemetadata.h +++ b/src/nodemetadata.h @@ -40,7 +40,8 @@ public: NodeMetadata(IItemDefManager *item_def_mgr); ~NodeMetadata(); - void serialize(std::ostream &os, u8 version, bool disk=true) const; + void serialize(std::ostream &os, u8 version, bool disk=true, + std::string formspec_prepend="") const; void deSerialize(std::istream &is, u8 version); void clear(); @@ -82,7 +83,7 @@ public: ~NodeMetadataList(); void serialize(std::ostream &os, u8 blockver, bool disk = true, - bool absolute_pos = false) const; + bool absolute_pos = false, std::string formspec_prepend = "") const; void deSerialize(std::istream &is, IItemDefManager *item_def_mgr, bool absolute_pos = false); diff --git a/src/object_properties.cpp b/src/object_properties.cpp index af3c7f26a..2c6a74a6f 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -73,28 +73,62 @@ std::string ObjectProperties::dump() return os.str(); } -void ObjectProperties::serialize(std::ostream &os) const +void ObjectProperties::serialize(std::ostream &os, u16 protocol_version) const { - writeU8(os, 4); // PROTOCOL_VERSION >= 37 + if (protocol_version > 36) + writeU8(os, 4); // PROTOCOL_VERSION >= 37 + else + writeU8(os, 1); writeU16(os, hp_max); writeU8(os, physical); - writeF32(os, 0.f); // Removed property (weight) - writeV3F32(os, collisionbox.MinEdge); - writeV3F32(os, collisionbox.MaxEdge); - writeV3F32(os, selectionbox.MinEdge); - writeV3F32(os, selectionbox.MaxEdge); - writeU8(os, pointable); - os << serializeString(visual); - writeV3F32(os, visual_size); - writeU16(os, textures.size()); - for (const std::string &texture : textures) { - os << serializeString(texture); + writeF(os, 0.f, protocol_version); // Removed property (weight) + if (protocol_version > 36) { + writeV3F32(os, collisionbox.MinEdge); + writeV3F32(os, collisionbox.MaxEdge); + writeV3F32(os, selectionbox.MinEdge); + writeV3F32(os, selectionbox.MaxEdge); + writeU8(os, pointable); + } else if (pointable) { + writeV3F1000(os, selectionbox.MinEdge); + writeV3F1000(os, selectionbox.MaxEdge); + } else { + // A hack to emulate unpointable objects + for (u8 i = 0; i < 6; i++) + writeF1000(os, 0); } + + // The "wielditem" type isn't exactly the same as "item", however this + // is the most similar compatible option + if (visual == "item" && protocol_version < 37) + os << serializeString("wielditem"); + else + os << serializeString(visual); + + if (protocol_version > 36) { + writeV3F32(os, visual_size); + } else { + writeF1000(os, visual_size.X); + writeF1000(os, visual_size.Y); + } + + // MT 0.4.15 and below don't have the wield_item property and expect + // wield_item to be in textures[0]. + if (protocol_version < 37 && (visual == "item" || visual == "wielditem") && + !wield_item.empty()) { + writeU16(os, 1); + os << serializeString(wield_item); + } else { + writeU16(os, textures.size()); + for (const std::string &texture : textures) { + os << serializeString(texture); + } + } + writeV2S16(os, spritediv); writeV2S16(os, initial_sprite_basepos); writeU8(os, is_visible); writeU8(os, makes_footstep_sound); - writeF32(os, automatic_rotate); + writeF(os, automatic_rotate, protocol_version); // Added in protocol version 14 os << serializeString(mesh); writeU16(os, colors.size()); @@ -102,16 +136,21 @@ void ObjectProperties::serialize(std::ostream &os) const writeARGB8(os, color); } writeU8(os, collideWithObjects); - writeF32(os, stepheight); + writeF(os, stepheight, protocol_version); writeU8(os, automatic_face_movement_dir); - writeF32(os, automatic_face_movement_dir_offset); + writeF(os, automatic_face_movement_dir_offset, protocol_version); writeU8(os, backface_culling); os << serializeString(nametag); writeARGB8(os, nametag_color); - writeF32(os, automatic_face_movement_max_rotation_per_sec); + writeF(os, automatic_face_movement_max_rotation_per_sec, protocol_version); os << serializeString(infotext); os << serializeString(wield_item); writeS8(os, glow); + + // Everything after this can use writeF32(). + if (protocol_version < 37) + return; + writeU16(os, breath_max); writeF32(os, eye_height); writeF32(os, zoom_fov); diff --git a/src/object_properties.h b/src/object_properties.h index e68b45e2c..dab826bb4 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -65,6 +65,6 @@ struct ObjectProperties ObjectProperties(); std::string dump(); - void serialize(std::ostream &os) const; + void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); }; diff --git a/src/server.cpp b/src/server.cpp index 0c1012cc4..8a00f65be 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -771,7 +771,11 @@ void Server::AsyncRunStep(bool initial_step) // u16 id // std::string data buffer.append(idbuf, sizeof(idbuf)); - buffer.append(serializeString(aom.datastring)); + if (client->net_proto_version >= 37 || + aom.legacystring.empty()) + buffer.append(serializeString(aom.datastring)); + else + buffer.append(serializeString(aom.legacystring)); } } /* @@ -1147,6 +1151,9 @@ void Server::ProcessData(NetworkPacket *pkt) return; } + RemoteClient *client = getClient(peer_id); + if (client) + pkt->setProtocolVersion(client->net_proto_version); handleCommand(pkt); } catch (SendFailedException &e) { errorstream << "Server::ProcessData(): SendFailedException: " @@ -1154,7 +1161,7 @@ void Server::ProcessData(NetworkPacket *pkt) << std::endl; } catch (PacketError &e) { actionstream << "Server::ProcessData(): PacketError: " - << "what=" << e.what() + << "what=" << e.what() << ", command=" << pkt->getCommand() // TODO: REMOVE COMMAND= << std::endl; } } @@ -1296,11 +1303,11 @@ void Server::Send(session_t peer_id, NetworkPacket *pkt) clientCommandFactoryTable[pkt->getCommand()].reliable); } -void Server::SendMovement(session_t peer_id) +void Server::SendMovement(session_t peer_id, u16 protocol_version) { std::ostringstream os(std::ios_base::binary); - NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id); + NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id, protocol_version); pkt << g_settings->getFloat("movement_acceleration_default"); pkt << g_settings->getFloat("movement_acceleration_air"); @@ -1335,7 +1342,13 @@ void Server::SendPlayerHPOrDie(PlayerSAO *playersao, const PlayerHPChangeReason void Server::SendHP(session_t peer_id, u16 hp) { NetworkPacket pkt(TOCLIENT_HP, 1, peer_id); - pkt << hp; + // Minetest 0.4 uses 8-bit integers for HPP. + if (m_clients.getProtocolVersion(peer_id) >= 37) { + pkt << hp; + } else { + u8 raw_hp = hp & 0xFF; + pkt << raw_hp; + } Send(&pkt); } @@ -1454,6 +1467,9 @@ void Server::SendInventory(PlayerSAO *sao, bool incremental) void Server::SendChatMessage(session_t peer_id, const ChatMessage &message) { + NetworkPacket legacypkt(TOCLIENT_CHAT_MESSAGE_OLD, 0, peer_id); + legacypkt << message.message; + NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id); u8 version = 1; u8 type = message.type; @@ -1464,9 +1480,12 @@ void Server::SendChatMessage(session_t peer_id, const ChatMessage &message) if (!player) return; - Send(&pkt); + if (player->protocol_version < 35) + Send(&legacypkt); + else + Send(&pkt); } else { - m_clients.sendToAll(&pkt); + m_clients.sendToAllCompat(&pkt, &legacypkt, 35); } } @@ -1484,7 +1503,12 @@ void Server::SendShowFormspecMessage(session_t peer_id, const std::string &forms pkt.putLongString(""); } else { m_formspec_state_data[peer_id] = formname; - pkt.putLongString(formspec); + RemotePlayer *player = m_env->getPlayer(peer_id); + if (player && player->protocol_version < 37 && + formspec.find("no_prepend[]") == std::string::npos) + pkt.putLongString(formspec + player->formspec_prepend); + else + pkt.putLongString(formspec); } pkt << formname; @@ -1522,7 +1546,7 @@ void Server::SendSpawnParticle(session_t peer_id, u16 protocol_version, } assert(protocol_version != 0); - NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); + NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id, protocol_version); { // NetworkPacket and iostreams are incompatible... @@ -1569,7 +1593,7 @@ void Server::SendAddParticleSpawner(session_t peer_id, u16 protocol_version, } assert(protocol_version != 0); - NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id); + NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 100, peer_id, protocol_version); pkt << p.amount << p.time << p.minpos << p.maxpos << p.minvel << p.maxvel << p.minacc << p.maxacc << p.minexptime << p.maxexptime @@ -1604,7 +1628,7 @@ void Server::SendDeleteParticleSpawner(session_t peer_id, u32 id) void Server::SendHUDAdd(session_t peer_id, u32 id, HudElement *form) { - NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id); + NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id, m_clients.getProtocolVersion(peer_id)); pkt << id << (u8) form->type << form->pos << form->name << form->scale << form->text << form->number << form->item << form->dir @@ -1623,7 +1647,7 @@ void Server::SendHUDRemove(session_t peer_id, u32 id) void Server::SendHUDChange(session_t peer_id, u32 id, HudElementStat stat, void *value) { - NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id); + NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id, m_clients.getProtocolVersion(peer_id)); pkt << id << (u8) stat; switch (stat) { @@ -1735,7 +1759,8 @@ void Server::SendSetStars(session_t peer_id, const StarParams ¶ms) void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms) { - NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id); + NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id, + m_clients.getProtocolVersion(peer_id)); pkt << params.density << params.color_bright << params.color_ambient << params.height << params.thickness << params.speed; Send(&pkt); @@ -1754,15 +1779,20 @@ void Server::SendOverrideDayNightRatio(session_t peer_id, bool do_override, void Server::SendTimeOfDay(session_t peer_id, u16 time, f32 time_speed) { - NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id); - pkt << time << time_speed; + if (peer_id != PEER_ID_INEXISTENT) { + NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id, + m_clients.getProtocolVersion(peer_id)); + pkt << time << time_speed; - if (peer_id == PEER_ID_INEXISTENT) { - m_clients.sendToAll(&pkt); - } - else { Send(&pkt); + return; } + + NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id, 37); + NetworkPacket legacypkt(TOCLIENT_TIME_OF_DAY, 0, peer_id, 32); + pkt << time << time_speed; + legacypkt << time << time_speed; + m_clients.sendToAllCompat(&pkt, &legacypkt, 37); } void Server::SendPlayerHP(session_t peer_id) @@ -1792,7 +1822,7 @@ void Server::SendMovePlayer(session_t peer_id) PlayerSAO *sao = player->getPlayerSAO(); assert(sao); - NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id); + NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id, player->protocol_version); pkt << sao->getBasePosition() << sao->getLookPitch() << sao->getRotation().Y; { @@ -1821,7 +1851,7 @@ void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames f32 animation_speed) { NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0, - peer_id); + peer_id, m_clients.getProtocolVersion(peer_id)); pkt << animation_frames[0] << animation_frames[1] << animation_frames[2] << animation_frames[3] << animation_speed; @@ -1831,7 +1861,7 @@ void Server::SendLocalPlayerAnimations(session_t peer_id, v2s32 animation_frames void Server::SendEyeOffset(session_t peer_id, v3f first, v3f third) { - NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id); + NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id, m_clients.getProtocolVersion(peer_id)); pkt << first << third; Send(&pkt); } @@ -1864,7 +1894,12 @@ void Server::SendPlayerInventoryFormspec(session_t peer_id) return; NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id); - pkt.putLongString(player->inventory_formspec); + if (player->protocol_version < 37 && player->inventory_formspec.find( + "no_prepend[]") == std::string::npos) + pkt.putLongString(player->inventory_formspec + + player->formspec_prepend); + else + pkt.putLongString(player->inventory_formspec); Send(&pkt); } @@ -1875,6 +1910,10 @@ void Server::SendPlayerFormspecPrepend(session_t peer_id) assert(player); if (player->getPeerId() == PEER_ID_INEXISTENT) return; + if (player->protocol_version < 37) { + SendPlayerInventoryFormspec(peer_id); + return; + } NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id); pkt << player->formspec_prepend; @@ -1995,6 +2034,10 @@ void Server::SendActiveObjectMessages(session_t peer_id, const std::string &data void Server::SendCSMRestrictionFlags(session_t peer_id) { + const u16 protocol_version = m_clients.getProtocolVersion(peer_id); + if (protocol_version < 35 && protocol_version != 0) + return; + NetworkPacket pkt(TOCLIENT_CSM_RESTRICTION_FLAGS, sizeof(m_csm_restriction_flags) + sizeof(m_csm_restriction_noderange), peer_id); pkt << m_csm_restriction_flags << m_csm_restriction_noderange; @@ -2086,17 +2129,29 @@ s32 Server::playSound(const SimpleSoundSpec &spec, float gain = params.gain * spec.gain; NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0); + NetworkPacket legacypkt(TOCLIENT_PLAY_SOUND, 0, PEER_ID_INEXISTENT, 32); pkt << id << spec.name << gain << (u8) params.type << pos << params.object << params.loop << params.fade << params.pitch << ephemeral; + legacypkt << id << spec.name << gain + << (u8) params.type << pos << params.object + << params.loop << params.fade; bool as_reliable = !ephemeral; + bool play_sound = gain > 0; for (const u16 dst_client : dst_clients) { + const u16 protocol_version = m_clients.getProtocolVersion(dst_client); + if (!play_sound && protocol_version < 32) + continue; if (psound) psound->clients.insert(dst_client); - m_clients.send(dst_client, 0, &pkt, as_reliable); + + if (protocol_version >= 37) + m_clients.send(dst_client, 0, &pkt, as_reliable); + else + m_clients.send(dst_client, 0, &legacypkt, as_reliable); } return id; } @@ -2257,6 +2312,13 @@ void Server::sendMetadataChanged(const std::list &meta_updates, float far if (!client) continue; + if (client->net_proto_version < 37) { + for (const v3s16 &pos : meta_updates) { + client->SetBlockNotSent(getNodeBlockPos(pos)); + } + continue; + } + ServerActiveObject *player = m_env->getActiveObject(i); v3f player_pos = player ? player->getBasePosition() : v3f(); @@ -2303,7 +2365,12 @@ void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver, */ std::ostringstream os(std::ios_base::binary); - block->serialize(os, ver, false); + + RemotePlayer *player = m_env->getPlayer(peer_id); + if (player && player->protocol_version < 37) + block->serialize(os, ver, false, player->formspec_prepend); + else + block->serialize(os, ver, false); block->serializeNetworkSpecific(os); std::string s = os.str(); @@ -2483,7 +2550,10 @@ void Server::fillMediaCache() std::vector paths; m_modmgr->getModsMediaPaths(paths); fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures"); - fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server"); + fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + + DIR_DELIM + "server"); + fs::GetRecursiveDirs(paths, porting::path_share + DIR_DELIM + "builtin" + + DIR_DELIM + "game" + DIR_DELIM + "models"); // Collect media file information from paths into cache for (const std::string &mediapath : paths) { @@ -2645,7 +2715,9 @@ void Server::sendRequestedMedia(session_t peer_id, void Server::sendDetachedInventory(Inventory *inventory, const std::string &name, session_t peer_id) { NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); + NetworkPacket legacy_pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id); pkt << name; + legacy_pkt << name; if (!inventory) { pkt << false; // Remove inventory @@ -2660,12 +2732,25 @@ void Server::sendDetachedInventory(Inventory *inventory, const std::string &name const std::string &os_str = os.str(); pkt << static_cast(os_str.size()); // HACK: to keep compatibility with 5.0.0 clients pkt.putRawString(os_str); + legacy_pkt.putRawString(os_str); } - if (peer_id == PEER_ID_INEXISTENT) - m_clients.sendToAll(&pkt); - else - Send(&pkt); + if (peer_id == PEER_ID_INEXISTENT) { + m_clients.newSendToAll(&pkt); + if (inventory) + m_clients.oldSendToAll(&legacy_pkt); + } else { + RemoteClient *client = getClientNoEx(peer_id, CS_Created); + if (!client) { + warningstream << "Could not get client in sendDetachedInventory!" + << std::endl; + } + + if (!client || client->net_proto_version >= 37) + Send(&pkt); + else if (inventory) + Send(&legacy_pkt); + } } void Server::sendDetachedInventories(session_t peer_id, bool incremental) @@ -2774,7 +2859,7 @@ void Server::acceptAuth(session_t peer_id, bool forSudoMode) if (!forSudoMode) { RemoteClient* client = getClient(peer_id, CS_Invalid); - NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id); + NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id, client->net_proto_version); // Right now, the auth mechs don't change between login and sudo mode. u32 sudo_auth_mechs = client->allowed_auth_mechs; diff --git a/src/server.h b/src/server.h index 52ca1ca3f..40b083272 100644 --- a/src/server.h +++ b/src/server.h @@ -374,7 +374,7 @@ private: void init(); - void SendMovement(session_t peer_id); + void SendMovement(session_t peer_id, u16 protocol_version); void SendHP(session_t peer_id, u16 hp); void SendBreath(session_t peer_id, u16 breath); void SendAccessDenied(session_t peer_id, AccessDeniedCode reason, diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 7b7b7b6c0..e99f56851 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -117,9 +117,10 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) if(!m_properties_sent) { m_properties_sent = true; - std::string str = getPropertyPacket(); + std::string str = getPropertyPacket(37); + std::string legacy_str = getPropertyPacket(32); // create message and add to list - m_messages_out.emplace(getId(), true, str); + m_messages_out.emplace(getId(), true, str, legacy_str); } // If attached, check that our parent is still there. If it isn't, detach. @@ -228,19 +229,26 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) os << serializeString(""); // name writeU8(os, 0); // is_player writeU16(os, getId()); //id - writeV3F32(os, m_base_position); - writeV3F32(os, m_rotation); + writeV3F(os, m_base_position, protocol_version); + if (protocol_version >= 37) + writeV3F32(os, m_rotation); + else + writeF1000(os, m_rotation.Y); writeU16(os, m_hp); std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(getPropertyPacket( + protocol_version)); // message 1 msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + msg_os << serializeLongString(generateUpdateAnimationCommand( + protocol_version)); // 3 for (const auto &bone_pos : m_bone_position) { msg_os << serializeLongString(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size + bone_pos.first, bone_pos.second.X, bone_pos.second.Y, + protocol_version)); // m_bone_position.size } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 + msg_os << serializeLongString(generateUpdateAttachmentCommand( + protocol_version)); // 4 int message_count = 4 + m_bone_position.size(); @@ -472,9 +480,9 @@ std::string LuaEntitySAO::getName() return m_init_name; } -std::string LuaEntitySAO::getPropertyPacket() +std::string LuaEntitySAO::getPropertyPacket(const u16 protocol_version) { - return generateSetPropertiesCommand(m_prop); + return generateSetPropertiesCommand(m_prop, protocol_version); } void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) @@ -500,10 +508,23 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) m_rotation, do_interpolate, is_movement_end, - update_interval + update_interval, + 37 ); + + std::string legacy_str = generateUpdatePositionCommand( + m_base_position, + m_velocity, + m_acceleration, + m_rotation, + do_interpolate, + is_movement_end, + update_interval, + 32 + ); + // create message and add to list - m_messages_out.emplace(getId(), false, str); + m_messages_out.emplace(getId(), false, str, legacy_str); } bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const diff --git a/src/server/luaentity_sao.h b/src/server/luaentity_sao.h index 3cae2a636..708639941 100644 --- a/src/server/luaentity_sao.h +++ b/src/server/luaentity_sao.h @@ -71,7 +71,7 @@ public: bool collideWithObjects() const; private: - std::string getPropertyPacket(); + std::string getPropertyPacket(const u16 protocol_version); void sendPosition(bool do_interpolate, bool is_movement_end); std::string generateSetTextureModCommand() const; static std::string generateSetSpriteCommand(v2s16 p, u16 num_frames, diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 2f0fa397f..912b4f27e 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -112,20 +112,37 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) os << serializeString(m_player->getName()); // name writeU8(os, 1); // is_player writeS16(os, getId()); // id - writeV3F32(os, m_base_position); - writeV3F32(os, m_rotation); - writeU16(os, getHP()); + if (protocol_version >= 37) { + writeV3F32(os, m_base_position); + writeV3F32(os, m_rotation); + writeU16(os, getHP()); + } else { + writeV3F1000(os, m_base_position + v3f(0, BS, 0)); + writeF1000(os, m_rotation.Y); + + // HP is sent as a signed integer + const u16 hp = getHP(); + if (hp > S16_MAX) + writeS16(os, S16_MAX); + else + writeS16(os, static_cast(hp)); + } std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(getPropertyPacket( + protocol_version)); // message 1 msg_os << serializeLongString(generateUpdateArmorGroupsCommand()); // 2 - msg_os << serializeLongString(generateUpdateAnimationCommand()); // 3 + msg_os << serializeLongString(generateUpdateAnimationCommand( + protocol_version)); // 3 for (const auto &bone_pos : m_bone_position) { msg_os << serializeLongString(generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); // m_bone_position.size + bone_pos.first, bone_pos.second.X, bone_pos.second.Y, + protocol_version)); // m_bone_position.size } - msg_os << serializeLongString(generateUpdateAttachmentCommand()); // 4 - msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand()); // 5 + msg_os << serializeLongString(generateUpdateAttachmentCommand( + protocol_version)); // 4 + msg_os << serializeLongString(generateUpdatePhysicsOverrideCommand( + protocol_version)); // 5 int message_count = 5 + m_bone_position.size(); @@ -221,9 +238,10 @@ void PlayerSAO::step(float dtime, bool send_recommended) if (!m_properties_sent) { m_properties_sent = true; - std::string str = getPropertyPacket(); + std::string str = getPropertyPacket(37); + std::string legacy_str = getPropertyPacket(32); // create message and add to list - m_messages_out.emplace(getId(), true, str); + m_messages_out.emplace(getId(), true, str, legacy_str); m_env->getScriptIface()->player_event(this, "properties_changed"); } @@ -286,30 +304,45 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_rotation, true, false, - update_interval + update_interval, + 37 ); + + std::string legacy_str = generateUpdatePositionCommand( + pos + v3f(0.0f, BS, 0.0f), + v3f(0.0f, 0.0f, 0.0f), + v3f(0.0f, 0.0f, 0.0f), + m_rotation, + true, + false, + update_interval, + 32 + ); + // create message and add to list - m_messages_out.emplace(getId(), false, str); + m_messages_out.emplace(getId(), false, str, legacy_str); } if (!m_physics_override_sent) { m_physics_override_sent = true; // create message and add to list - m_messages_out.emplace(getId(), true, generateUpdatePhysicsOverrideCommand()); + m_messages_out.emplace(getId(), true, + generateUpdatePhysicsOverrideCommand(37), + generateUpdatePhysicsOverrideCommand(32)); } sendOutdatedData(); } -std::string PlayerSAO::generateUpdatePhysicsOverrideCommand() const +std::string PlayerSAO::generateUpdatePhysicsOverrideCommand(const u16 protocol_version) const { std::ostringstream os(std::ios::binary); // command writeU8(os, AO_CMD_SET_PHYSICS_OVERRIDE); // parameters - writeF32(os, m_physics_override_speed); - writeF32(os, m_physics_override_jump); - writeF32(os, m_physics_override_gravity); + writeF(os, m_physics_override_speed, protocol_version); + writeF(os, m_physics_override_jump, protocol_version); + writeF(os, m_physics_override_gravity, protocol_version); // these are sent inverted so we get true when the server sends nothing writeU8(os, !m_physics_override_sneak); writeU8(os, !m_physics_override_sneak_glitch); @@ -537,10 +570,26 @@ void PlayerSAO::unlinkPlayerSessionAndSave() m_env->removePlayer(m_player); } -std::string PlayerSAO::getPropertyPacket() +std::string PlayerSAO::getPropertyPacket(const u16 protocol_version) { m_prop.is_visible = (true); - return generateSetPropertiesCommand(m_prop); + + ObjectProperties prop = m_prop; + if (protocol_version < 37 && (m_prop.mesh == "3d_armor_character.b3d" || + m_prop.mesh == "character.b3d" || + m_prop.mesh == "skinsdb_3d_armor_character_5.b3d")) { + prop.mesh = "mc_compat_character.b3d"; + for (u16 i = prop.textures.size(); i < 5; i++) { + prop.textures.emplace_back("blank.png"); + } + } + + // Remove a one-node offset from a copy of the object properties for MT 0.4 + if (protocol_version < 37) { + prop.selectionbox.MinEdge.Y -= 1.0f; + prop.selectionbox.MaxEdge.Y -= 1.0f; + } + return generateSetPropertiesCommand(prop, protocol_version); } void PlayerSAO::setMaxSpeedOverride(const v3f &vel) diff --git a/src/server/player_sao.h b/src/server/player_sao.h index fae7fd079..2d351fdf1 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -180,9 +180,10 @@ public: inline Metadata &getMeta() { return m_meta; } private: - std::string getPropertyPacket(); + std::string getPropertyPacket(const u16 protocol_version); void unlinkPlayerSessionAndSave(); - std::string generateUpdatePhysicsOverrideCommand() const; + std::string generateUpdatePhysicsOverrideCommand( + const u16 protocol_version) const; RemotePlayer *m_player = nullptr; session_t m_peer_id = 0; diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index 2890448cc..2700896ee 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -99,24 +99,34 @@ void UnitSAO::sendOutdatedData() if (!m_animation_sent) { m_animation_sent = true; m_animation_speed_sent = true; - m_messages_out.emplace(getId(), true, generateUpdateAnimationCommand()); + m_messages_out.emplace(getId(), true, + generateUpdateAnimationCommand(37), + generateUpdateAnimationCommand(32)); } else if (!m_animation_speed_sent) { // Animation speed is also sent when 'm_animation_sent == false' m_animation_speed_sent = true; - m_messages_out.emplace(getId(), true, generateUpdateAnimationSpeedCommand()); + m_messages_out.emplace(getId(), true, + generateUpdateAnimationSpeedCommand(), + // MT 0.4 has no update animation speed command + generateUpdateAnimationCommand(32)); } if (!m_bone_position_sent) { m_bone_position_sent = true; for (const auto &bone_pos : m_bone_position) { - m_messages_out.emplace(getId(), true, generateUpdateBonePositionCommand( - bone_pos.first, bone_pos.second.X, bone_pos.second.Y)); + std::string str = generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y, 37); + std::string legacy_str = generateUpdateBonePositionCommand( + bone_pos.first, bone_pos.second.X, bone_pos.second.Y, 32); + m_messages_out.emplace(getId(), true, str, legacy_str); } } if (!m_attachment_sent) { m_attachment_sent = true; - m_messages_out.emplace(getId(), true, generateUpdateAttachmentCommand()); + m_messages_out.emplace(getId(), true, + generateUpdateAttachmentCommand(37), + generateUpdateAttachmentCommand(32)); } } // clang-format on @@ -235,7 +245,7 @@ void UnitSAO::notifyObjectPropertiesModified() m_properties_sent = false; } -std::string UnitSAO::generateUpdateAttachmentCommand() const +std::string UnitSAO::generateUpdateAttachmentCommand(const u16 protocol_version) const { std::ostringstream os(std::ios::binary); // command @@ -243,21 +253,37 @@ std::string UnitSAO::generateUpdateAttachmentCommand() const // parameters writeS16(os, m_attachment_parent_id); os << serializeString(m_attachment_bone); - writeV3F32(os, m_attachment_position); - writeV3F32(os, m_attachment_rotation); + + // Add/remove offsets to compensate for MT 0.4 + if (protocol_version >= 37) { + writeV3F32(os, m_attachment_position); + } else { + v3f compat_attachment_position = m_attachment_position; + if (getType() == ACTIVEOBJECT_TYPE_PLAYER) { + compat_attachment_position.Y += BS; + } else { + ServerActiveObject *p = + m_env->getActiveObject(m_attachment_parent_id); + if (p && p->getType() == ACTIVEOBJECT_TYPE_PLAYER) + compat_attachment_position.Y -= BS; + } + writeV3F1000(os, compat_attachment_position); + } + + writeV3F(os, m_attachment_rotation, protocol_version); return os.str(); } -std::string UnitSAO::generateUpdateBonePositionCommand( - const std::string &bone, const v3f &position, const v3f &rotation) +std::string UnitSAO::generateUpdateBonePositionCommand(const std::string &bone, + const v3f &position, const v3f &rotation, const u16 protocol_version) { std::ostringstream os(std::ios::binary); // command writeU8(os, AO_CMD_SET_BONE_POSITION); // parameters os << serializeString(bone); - writeV3F32(os, position); - writeV3F32(os, rotation); + writeV3F(os, position, protocol_version); + writeV3F(os, rotation, protocol_version); return os.str(); } @@ -271,15 +297,15 @@ std::string UnitSAO::generateUpdateAnimationSpeedCommand() const return os.str(); } -std::string UnitSAO::generateUpdateAnimationCommand() const +std::string UnitSAO::generateUpdateAnimationCommand(const u16 protocol_version) const { std::ostringstream os(std::ios::binary); // command writeU8(os, AO_CMD_SET_ANIMATION); // parameters - writeV2F32(os, m_animation_range); - writeF32(os, m_animation_speed); - writeF32(os, m_animation_blend); + writeV2F(os, m_animation_range, protocol_version); + writeF(os, m_animation_speed, protocol_version); + writeF(os, m_animation_blend, protocol_version); // these are sent inverted so we get true when the server sends nothing writeU8(os, !m_animation_loop); return os.str(); @@ -299,33 +325,38 @@ std::string UnitSAO::generateUpdateArmorGroupsCommand() const std::string UnitSAO::generateUpdatePositionCommand(const v3f &position, const v3f &velocity, const v3f &acceleration, const v3f &rotation, - bool do_interpolate, bool is_movement_end, f32 update_interval) + bool do_interpolate, bool is_movement_end, f32 update_interval, + const u16 protocol_version) { std::ostringstream os(std::ios::binary); // command writeU8(os, AO_CMD_UPDATE_POSITION); // pos - writeV3F32(os, position); + writeV3F(os, position, protocol_version); // velocity - writeV3F32(os, velocity); + writeV3F(os, velocity, protocol_version); // acceleration - writeV3F32(os, acceleration); + writeV3F(os, acceleration, protocol_version); // rotation - writeV3F32(os, rotation); + if (protocol_version >= 37) + writeV3F32(os, rotation); + else + writeF1000(os, rotation.Y); // do_interpolate writeU8(os, do_interpolate); // is_end_position (for interpolation) writeU8(os, is_movement_end); // update_interval (for interpolation) - writeF32(os, update_interval); + writeF(os, update_interval, protocol_version); return os.str(); } -std::string UnitSAO::generateSetPropertiesCommand(const ObjectProperties &prop) const +std::string UnitSAO::generateSetPropertiesCommand( + const ObjectProperties &prop, const u16 protocol_version) const { std::ostringstream os(std::ios::binary); writeU8(os, AO_CMD_SET_PROPERTIES); - prop.serialize(os); + prop.serialize(os, protocol_version); return os.str(); } @@ -339,7 +370,19 @@ std::string UnitSAO::generatePunchCommand(u16 result_hp) const return os.str(); } +std::string UnitSAO::generateLegacyPunchCommand(u16 result_hp) const +{ + std::ostringstream os(std::ios::binary); + // command + writeU8(os, AO_CMD_PUNCHED); + // result_hp + writeU16(os, result_hp); + return os.str(); +} + void UnitSAO::sendPunchCommand() { - m_messages_out.emplace(getId(), true, generatePunchCommand(getHP())); + const u16 result_hp = getHP(); + m_messages_out.emplace(getId(), true, generatePunchCommand(result_hp), + generateLegacyPunchCommand(result_hp)); } diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index 76d409ba6..6e1f2138c 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include "constants.h" #include "object_properties.h" #include "serveractiveobject.h" @@ -79,16 +80,19 @@ public: void sendOutdatedData(); // Update packets - std::string generateUpdateAttachmentCommand() const; + std::string generateUpdateAttachmentCommand(const u16 protocol_version) const; std::string generateUpdateAnimationSpeedCommand() const; - std::string generateUpdateAnimationCommand() const; + std::string generateUpdateAnimationCommand(const u16 protocol_version) const; std::string generateUpdateArmorGroupsCommand() const; static std::string generateUpdatePositionCommand(const v3f &position, const v3f &velocity, const v3f &acceleration, const v3f &rotation, - bool do_interpolate, bool is_movement_end, f32 update_interval); - std::string generateSetPropertiesCommand(const ObjectProperties &prop) const; + bool do_interpolate, bool is_movement_end, f32 update_interval, + const u16 protocol_version); + std::string generateSetPropertiesCommand( + const ObjectProperties &prop, const u16 protocol_version) const; static std::string generateUpdateBonePositionCommand(const std::string &bone, - const v3f &position, const v3f &rotation); + const v3f &position, const v3f &rotation, + const u16 protocol_version); void sendPunchCommand(); protected: @@ -112,6 +116,7 @@ private: void onDetach(int parent_id); std::string generatePunchCommand(u16 result_hp) const; + std::string generateLegacyPunchCommand(u16 result_hp) const; // Armor groups bool m_armor_groups_sent = false; diff --git a/src/sound.h b/src/sound.h index 090b240c4..bda0780b0 100644 --- a/src/sound.h +++ b/src/sound.h @@ -40,6 +40,12 @@ struct SimpleSoundSpec void serialize(std::ostream &os, u8 cf_version) const { os << serializeString(name); + if (cf_version < 13) { + writeF1000(os, gain); + if (cf_version > 10) + writeF1000(os, pitch); + return; + } writeF32(os, gain); writeF32(os, pitch); writeF32(os, fade); diff --git a/src/tileanimation.cpp b/src/tileanimation.cpp index a78cb8cb3..a44d8be35 100644 --- a/src/tileanimation.cpp +++ b/src/tileanimation.cpp @@ -21,15 +21,27 @@ with this program; if not, write to the Free Software Foundation, Inc., void TileAnimationParams::serialize(std::ostream &os, u8 tiledef_version) const { + if (tiledef_version < 3 && type != TAT_VERTICAL_FRAMES) { + writeU8(os, TAT_NONE); + writeU16(os, 1); + writeU16(os, 1); + writeF1000(os, 1.0f); + return; + } + writeU8(os, type); + + // Approximate protocol version + const u16 protocol_version = tiledef_version >= 6 ? 37 : 32; + if (type == TAT_VERTICAL_FRAMES) { writeU16(os, vertical_frames.aspect_w); writeU16(os, vertical_frames.aspect_h); - writeF32(os, vertical_frames.length); + writeF(os, vertical_frames.length, protocol_version); } else if (type == TAT_SHEET_2D) { writeU8(os, sheet_2d.frames_w); writeU8(os, sheet_2d.frames_h); - writeF32(os, sheet_2d.frame_length); + writeF(os, sheet_2d.frame_length, protocol_version); } } diff --git a/src/tool.cpp b/src/tool.cpp index af0434bd5..e956345a5 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -58,9 +58,11 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const { if (protocol_version >= 38) writeU8(os, 5); - else + else if (protocol_version == 37) writeU8(os, 4); // proto == 37 - writeF32(os, full_punch_interval); + else + writeU8(os, 2); // proto >= 18 + writeF(os, full_punch_interval, protocol_version); writeS16(os, max_drop_level); writeU32(os, groupcaps.size()); for (const auto &groupcap : groupcaps) { @@ -72,7 +74,7 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const writeU32(os, cap->times.size()); for (const auto &time : cap->times) { writeS16(os, time.first); - writeF32(os, time.second); + writeF(os, time.second, protocol_version); } }