From 13579a2b6ba9d95c850dd7fffb5ef40ece8de8da Mon Sep 17 00:00:00 2001
From: Dale Glass <dale@daleglass.net>
Date: Wed, 17 Jul 2024 23:33:25 +0200
Subject: [PATCH] Add --getProtocolVersionData and --getProtocolVersionHash
 arguments

---
 interface/src/main.cpp                        | 38 +++++++++++
 .../networking/src/udt/PacketHeaders.cpp      | 16 +++++
 libraries/networking/src/udt/PacketHeaders.h  | 66 ++++++++++++++++++-
 3 files changed, 117 insertions(+), 3 deletions(-)

diff --git a/interface/src/main.cpp b/interface/src/main.cpp
index 4de09fca1f..c1854de446 100644
--- a/interface/src/main.cpp
+++ b/interface/src/main.cpp
@@ -275,6 +275,14 @@ int main(int argc, const char* argv[]) {
         "abortAfterInit",
         "Debug option. Aborts after initialization, right before the program starts running the event loop."
     );
+    QCommandLineOption getProtocolVersionHashOption(
+        "getProtocolVersionHash",
+        "Debug option. Returns the network protocol version MD5 hash."
+    );
+    QCommandLineOption getProtocolVersionDataOption(
+        "getProtocolVersionData",
+        "Debug option. Returns the network protocol detailed data in JSON."
+    );
 
     // "--qmljsdebugger", which appears in output from "--help-all".
     // Those below don't seem to be optional.
@@ -321,6 +329,8 @@ int main(int argc, const char* argv[]) {
     parser.addOption(abortAfterStartupOption);
     parser.addOption(abortAfterInitOption);
     parser.addOption(getPluginsOption);
+    parser.addOption(getProtocolVersionHashOption);
+    parser.addOption(getProtocolVersionDataOption);
 
 
     QString applicationPath;
@@ -455,6 +465,34 @@ int main(int argc, const char* argv[]) {
             return 1;
         }
     }
+    if (parser.isSet(getProtocolVersionHashOption)) {
+        std::cout << protocolVersionsSignatureHex().toStdString() << std::endl;
+        return 0;
+    }
+    if (parser.isSet(getProtocolVersionDataOption)) {
+        auto protocolMap = protocolVersionsSignatureMap();
+        QMetaEnum packetMetaEnum = QMetaEnum::fromType<PacketTypeEnum::Value>();
+
+        QJsonArray packetTypesList;
+        auto keyList = protocolMap.keys();
+        std::sort(keyList.begin(), keyList.end()); // Sort by numeric value
+
+        for(const auto packet : keyList) {
+            QJsonObject data;
+            int intValue = static_cast<int>(packet);
+            QString keyName = packetMetaEnum.valueToKey(intValue);
+
+            data["name"] = keyName;
+            data["value"] = intValue;
+            data["version"] = versionForPacketType(packet);
+
+            packetTypesList.append(data);
+        }
+
+        std::cout << QJsonDocument(packetTypesList).toJson().toStdString() << std::endl;
+        return 0;
+    }
+
 
     static const QString APPLICATION_CONFIG_FILENAME = "config.json";
     QDir applicationDir(applicationPath);
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index e561bfe21e..2aafc5501e 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -130,6 +130,10 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion) {
 
 static QByteArray protocolVersionSignature;
 static QString protocolVersionSignatureBase64;
+static QString protocolVersionSignatureHex;
+static QMap<PacketType, uint8_t> protocolVersionMap;
+
+
 static void ensureProtocolVersionsSignature() {
     static std::once_flag once;
     std::call_once(once, [&] {
@@ -139,12 +143,14 @@ static void ensureProtocolVersionsSignature() {
         stream << numberOfProtocols;
         for (uint8_t packetType = 0; packetType < numberOfProtocols; packetType++) {
             uint8_t packetTypeVersion = static_cast<uint8_t>(versionForPacketType(static_cast<PacketType>(packetType)));
+            protocolVersionMap[static_cast<PacketType>(packetType)] = packetTypeVersion;
             stream << packetTypeVersion;
         }
         QCryptographicHash hash(QCryptographicHash::Md5);
         hash.addData(buffer);
         protocolVersionSignature = hash.result();
         protocolVersionSignatureBase64 = protocolVersionSignature.toBase64();
+        protocolVersionSignatureHex = protocolVersionSignature.toHex(0);
     });
 }
 QByteArray protocolVersionsSignature() {
@@ -161,3 +167,13 @@ QString protocolVersionsSignatureBase64() {
     ensureProtocolVersionsSignature();
     return protocolVersionSignatureBase64;
 }
+
+QString protocolVersionsSignatureHex() {
+    ensureProtocolVersionsSignature();
+    return protocolVersionSignatureHex;
+}
+
+QMap<PacketType, uint8_t> protocolVersionsSignatureMap() {
+    ensureProtocolVersionsSignature();
+    return protocolVersionMap;
+}
\ No newline at end of file
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 3badfdf418..bc36d9444a 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -31,8 +31,15 @@ class PacketTypeEnum {
     Q_GADGET
     Q_ENUMS(Value)
 public:
-    // If adding a new packet packetType, you can replace one marked usable or add at the end.
-    // This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
+
+    /**
+     * @brief Packet type identifier
+     *
+     * Identifies the type of packet being sent.
+     *
+     * @note If adding a new packet packetType, you can replace one marked usable or add at the end.
+     * @note This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
+     */
     enum class Value : uint8_t {
         Unknown,
         DomainConnectRequestPending,
@@ -143,6 +150,8 @@ public:
         NUM_PACKET_TYPE
     };
 
+    Q_ENUM(Value)
+
     const static QHash<PacketTypeEnum::Value, PacketTypeEnum::Value> getReplicatedPacketMapping() {
         const static QHash<PacketTypeEnum::Value, PacketTypeEnum::Value> REPLICATED_PACKET_MAPPING {
             { PacketTypeEnum::Value::MicrophoneAudioNoEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho },
@@ -219,10 +228,60 @@ const int NUM_BYTES_MD5_HASH = 16;
 // NOTE: There is a max limit of 255, hopefully we have a better way to manage this by then.
 typedef uint8_t PacketVersion;
 
+/**
+ * @brief Returns the version number of the given packet type
+ *
+ * If the implementation of a packet type is modified in an incompatible way, the implementation
+ * of this function needs to be modified to return an incremented value.
+ *
+ * This is used to determine whether the protocol is compatible between client and server.
+ *
+ * @note Version is limited to a max of 255.
+ *
+ * @param packetType Type of packet
+ * @return PacketVersion Version
+ */
 PacketVersion versionForPacketType(PacketType packetType);
-QByteArray protocolVersionsSignature(); /// returns a unique signature for all the current protocols
+
+/**
+ * @brief Returns a unique signature for all the current protocols
+ *
+ * This computes a MD5 hash that expresses the state of the protocol's specification. The calculation
+ * is done in ensureProtocolVersionsSignature and accounts for the following:
+ *
+ * * Number of known packet types
+ * * versionForPacketType(type) for each packet type.
+ *
+ * There's no provision for backwards compatibility, anything that changes this calculation is a protocol break.
+ *
+ * @return QByteArray MD5 digest as a byte array
+ */
+QByteArray protocolVersionsSignature();
+
+/***
+ * @brief Returns a unique signature for all the current protocols
+ *
+ * Same as protocolVersionsSignature(), in base64.
+ */
 QString protocolVersionsSignatureBase64();
 
+/***
+ * @brief Returns a unique signature for all the current protocols
+ *
+ * Same as protocolVersionsSignature(), in hex;
+ */
+QString protocolVersionsSignatureHex();
+
+/***
+ * @brief Returns the data used to compute the protocol version
+ *
+ * The key is the packet type. The value is the version for that packet type.
+ *
+ * Used for aiding in development.
+ */
+QMap<PacketType, uint8_t> protocolVersionsSignatureMap();
+
+
 #if (PR_BUILD || DEV_BUILD)
 void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debugging version negotiation
 #endif
@@ -428,4 +487,5 @@ enum class AvatarQueryVersion : PacketVersion {
     ConicalFrustums = 22
 };
 
+
 #endif // hifi_PacketHeaders_h