From 292cafd159764197a1884ccabd25979465bab06b Mon Sep 17 00:00:00 2001
From: Striven <sg.striven@cutecat.club>
Date: Fri, 20 Feb 2026 16:23:23 +0000
Subject: [PATCH] Add initial Storm docs

---
 05_storm/01_overview.md |  179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 179 insertions(+), 0 deletions(-)

diff --git a/05_storm/01_overview.md b/05_storm/01_overview.md
index 4068160..d01e47e 100644
--- a/05_storm/01_overview.md
+++ b/05_storm/01_overview.md
@@ -1 +1,180 @@
 # Storm: Overview
+
+**Storm** is Ubisoft's proprietary matchmaking engine. The engine itself is **game-agnostic**, meaning all Storm-based games connect to single central regional servers which co-ordinate games between players.
+
+There is very little information online about the Storm matchmaking service.
+
+## Routers
+In the `us-sdkClientStorm` field for the game's [Space](../03_ubisoft_services/03_structure.md#spaces) parameters, a set of **router provisioning URLs** is provided:
+```json
+{
+  "parameters": {
+    "us-sdkClientStorm": {
+      "fields": {
+        "detectConfig": "EnableDebug=false;ValidateDetect=true",
+        "detectProvisioningUrl": "https://ncsa-storm.ubi.com/v1/natdetect",
+        "routerProvisioningUrl": "https://apac-storm.ubi.com/v1/router;https://emea-storm.ubi.com/v1/router;https://ncsa-storm.ubi.com/v1/router;https://ap-southeast-2-storm.ubi.com/v1/router",
+        "matchmakingSandboxName": "SM_PC_LNCH_A",
+        "traversalProvisioningUrl": "https://ncsa-storm.ubi.com/v1/nattraversal",
+        "matchmakingSandboxSpaceId": "4c29e04e-14d2-4245-a3c4-bf4703461119"
+      },
+      "relatedPopulation": null
+    }
+  }
+}
+```
+
+The URLs provided each provider an endpoint with a versioning number at the end. For example, Hyperscape makes requests to: `https://apac-storm.ubi.com/v1/router/19`.
+
+This is an **authenticated route**, meaning a Ubisoft ticket must be provided in the **Authorization** header.
+
+Here is an example request:
+
+### Endpoint
+
+```
+GET /v1/router/19
+```
+
+**Host:** `ncsa-storm.ubi.com`
+
+### Headers
+
+| Header        | Value               |
+|---------------|---------------------|
+| Authorization | `Ubi_v1 t={ticket}` |
+
+### Example Response
+
+```json
+{
+    "router": "52.207.248.146:12000"
+}
+```
+
+The client can then make UDP requests to this router at the specified port, packets which are structured according to the [Storm Protocol](#storm-protocol)
+
+## Storm Protocol
+
+Storm appears to use a mix of **positive acknowledgements** (ACKs), and **negative acknowledgements** (nACKs).
+
+### Ping Packet
+| Name          | Description                                                                                              | Type                   |
+|---------------|----------------------------------------------------------------------------------------------------------|------------------------|
+| Connection ID | The ID of the sender. At this stage `null`.                                                              | `u16`                  |
+| Unknown       | Unknown                                                                                                  | `20 bytes`             |
+| Opcode        | Packet opcode, `0x10` for ping                                                                           | [Opcode](#enum-opcode) |
+| Nonce         | Incrementing nonce. First 3 bytes are seemingly random, last byte is incrementing                        | `u32`                  |
+| App ID        | Some sort of ID. Client sends the application ID for `Hyperscape`, server responds with an unknown UUID. | `UUID`                 |
+| Unknown       | Unknown                                                                                                  | `u8`                   |
+
+### Unknown Handshake Packet 1
+
+> [!IMPORTANT]
+> This is a **possible structure**, but highly unlikely due to strange type sizes. This is derived from seeing which bytes are **static**, and which seem to change on each connection.
+
+| Name          | Description                                                        | Type                   |
+|---------------|--------------------------------------------------------------------|------------------------|
+| Connection ID | The ID of the sender. At this stage `null`.                        | `u16`                  |
+| Unknown       | Unknown                                                            | `20 bytes`             |
+| Opcode        | Packet opcode, `0xe0` for this message                             | [Opcode](#enum-opcode) |
+| Unknown       | Unknown                                                            | `u16`                  |
+| Nonce         | Some sort of incrementing nonce                                    | `u32`                  |
+| App ID        | Some sort of ID. Client sends the application ID for `Hyperscape`. | `UUID`                 |
+| Unknown       | Unknown                                                            | `UUID`                 |
+| Unknown       | Unknown                                                            | `u32`                  |
+| Unknown       | Unknown                                                            | `20 bytes`             |
+| Unknown       | Unknown                                                            | `u8`                   |
+| Unknown       | Unknown                                                            | `3 bytes`              |
+| Unknown       | Unknown                                                            | `26 bytes`             |
+| Unknown       | Unknown                                                            | `u16`                  |
+| Unknown       | Unknown                                                            | `7 bytes`              |
+| Unknown       | Unknown                                                            | `13 bytes`             |
+| Unknown       | Unknown                                                            | `10 bytes`             |
+| Unknown       | Unknown                                                            | `u16`                  |
+
+### Enum: `Opcode`
+
+Size: `u8`
+
+| Name    | Description                             | Value  |
+|---------|-----------------------------------------|--------|
+| Ping    | Used for determining round-trip latency | `0x10` |
+| Unknown | Unknown                                 | `0xe0` |
+
+## Detecting Best Region
+When multiple regional routers are provided in the [`routerProvisioningUrl` field](#routers), the game sends exactly **5** packets to calculate the average round-trip latency. The lowest latency server is then selected.
+
+This stage is not required. If only one regional router is provided, then it moves straight to the [handshake](#handshake).
+
+Here is an example conversation:
+
+```
+[C -> S] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb00 69e55abd5aca4eeebe94ce3944c72e73 c4
+[S -> C] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb00 63aba5df1da34eab863359bb2f53cd13 00
+
+[C -> S] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb01 69e55abd5aca4eeebe94ce3944c72e73 c4
+[S -> C] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb01 63aba5df1da34eab863359bb2f53cd13 00
+
+[C -> S] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb02 69e55abd5aca4eeebe94ce3944c72e73 c4
+[S -> C] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb02 63aba5df1da34eab863359bb2f53cd13 00
+
+[C -> S] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb03 69e55abd5aca4eeebe94ce3944c72e73 c4
+[S -> C] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb03 63aba5df1da34eab863359bb2f53cd13 00
+
+[C -> S] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb04 69e55abd5aca4eeebe94ce3944c72e73 c4
+[S -> C] 0000 0000000000000000000000000000000000000000 10 00000000 1e38eb04 63aba5df1da34eab863359bb2f53cd13 00
+```
+
+If the router server fails to reply to these packets, the client re-sends them with a **different** starting nonce.
+
+Once a router has been selected, the client then performs what is presumably a [handshake](#handshake).
+
+## Handshake
+This appears to be some sort of handshake sequence for connecting.
+
+```
+[C -> S] 77b2 0000000000000000000000000000000000000000 e0 0003 c210 2013 69e55abd5aca4eeebe94ce3944c72e73 3d1e94062594475797cdfb247c1730c0 00130000 0c0ba989e03c0574f2a0 a6 032e09 81500003ffff0c2b0307802a29 0e86 61800000040002 5d8a0c0a5ddaca0b4a5e00d010 989959581d 9c00
+[S -> C] 2450 0000000000000000000000000000000000000000 e0 0000 0020 2013 63aba5df1da34eab863359bb2f53cd13 ffffffffffffffffffffffffffffffff 00130001 0c0ba989e03c0574f2a0 b2 032e09 582280c85dc10a00 455455         61800000040000 1e0ff0c2b0307802a2 90e860
+[C -> S] 2450 0000000000000000000000000000000000000000 e0 0003 e230 2013 69e55abd5aca4eeebe94ce3944c72e73 3d1e94062594475797cdfb247c1730c0 00130000 0c0ba989e03c0574f2a0 a6 032e09 81500003ffff0c2b0307802a29 0e86 61800000040002 5d8a0c0a5ddaca0b4a5e00d010 989959581d 9d0022aa2a80
+```
+
+
+Note how the connection IDs swap -- the client sends the server's connection ID, and the server sends the client's connection ID.
+
+After the initial handshake, both the client and server start sending [heartbeats](#heartbeats)
+
+## Heartbeats
+```
+[C -> S] 2450 0000000000000000000000000000000000000000 e0 0000 d6 60
+[S -> C] 77b2 0000000000000000000000000000000000000000 e0 0000 00 60
+[C -> S] 2450 0000000000000000000000000000000000000000 e0 0000 d6 60
+[S -> C] 77b2 0000000000000000000000000000000000000000 e0 0000 00 60
+... repeated ...
+```
+
+These heartbeats are sent by the client and server **independently of eachother** approximately every `10` seconds, but are not acknowledged. The protocol relies on the negative presence of these packets to indicate disconnection. This seems to happen after `60` seconds of no heartbeat.
+
+## Disconnect
+When the client closes the game, the client sends `8` disconnect packets each exactly the same. This is possibly done to ensure the highest chance of arrival, however the server does not appear to take much notice.
+
+```
+[C -> S]
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+6723 0000000000000000000000000000000000000000 e0 0001 664c2b0307802a291bd25300cff05b ffffffff c3
+
+[S -> C]
+
+... heartbeats for 60 seconds ...
+
+3b6a 0000000000000000000000000000000000000000 e0 0000 004a6019fe0b6158183c015148de93 ffffffff c3
+3b6a 0000000000000000000000000000000000000000 e0 0000 004a6019fe0b6158183c015148de93 ffffffff c3
+3b6a 0000000000000000000000000000000000000000 e0 0000 004a6019fe0b6158183c015148de93 ffffffff c3
+3b6a 0000000000000000000000000000000000000000 e0 0000 004a6019fe0b6158183c015148de93 ffffffff c3
+```
\ No newline at end of file

--
Gitblit v1.10.0