Skip to content

Commit df8701d

Browse files
committed
Use msgpack for API [rojo-rbx#1176]
Switches the rojo API to use msgpack instead of JSON. Fixes rojo-rbx#363. Fixes rojo-rbx#881.
1 parent cfe071d commit df8701d

File tree

15 files changed

+160
-94
lines changed

15 files changed

+160
-94
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@
1616
[submodule "plugin/Packages/Highlighter"]
1717
path = plugin/Packages/Highlighter
1818
url = https://github.com/boatbomber/highlighter.git
19+
[submodule "plugin/Packages/msgpack-luau"]
20+
path = plugin/Packages/msgpack-luau
21+
url = https://github.com/cipharius/msgpack-luau/

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ Making a new release? Simply add the new header with the version and date undern
3434

3535
[#1179]: https://github.com/rojo-rbx/rojo/pull/1179
3636

37+
* `inf` and `nan` values in properties are now synced ([#1176])
38+
39+
[#1176]: https://github.com/rojo-rbx/rojo/pull/1176
40+
3741
## [7.7.0-rc.1] (November 27th, 2025)
3842

3943
* Fixed a bug where passing `--skip-git` to `rojo init` would still create a file named `gitignore.txt` ([#1172])

Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ blake3 = "1.5.0"
105105
float-cmp = "0.9.0"
106106
indexmap = { version = "2.10.0", features = ["serde"] }
107107
normpath = "1.2.0"
108+
rmp-serde = "1.3.0"
109+
serde_bytes = "0.11.19"
108110

109111
[target.'cfg(windows)'.dependencies]
110112
winreg = "0.10.1"

build.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
3030
continue;
3131
}
3232

33+
// Ignore images in msgpack-luau because they aren't UTF-8 encoded.
34+
if file_name.ends_with(".png") {
35+
continue;
36+
}
37+
3338
let child_snapshot = snapshot_from_fs_path(&entry.path())?;
3439
children.push((file_name, child_snapshot));
3540
}

plugin/Packages/msgpack-luau

Submodule msgpack-luau added at 40f67fc

plugin/http/Response.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
local HttpService = game:GetService("HttpService")
22

3+
local msgpack = require(script.Parent.Parent.msgpack)
4+
35
local stringTemplate = [[
46
Http.Response {
57
code: %d
@@ -31,4 +33,8 @@ function Response:json()
3133
return HttpService:JSONDecode(self.body)
3234
end
3335

36+
function Response:msgpack()
37+
return msgpack.decode(self.body)
38+
end
39+
3440
return Response

plugin/http/init.lua

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
local HttpService = game:GetService("HttpService")
22

3-
local Promise = require(script.Parent.Promise)
43
local Log = require(script.Parent.Log)
4+
local msgpack = require(script.Parent.msgpack)
5+
local Promise = require(script.Parent.Promise)
56

67
local HttpError = require(script.Error)
78
local HttpResponse = require(script.Response)
@@ -68,4 +69,12 @@ function Http.jsonDecode(source)
6869
return HttpService:JSONDecode(source)
6970
end
7071

72+
function Http.msgpackEncode(object)
73+
return msgpack.encode(object)
74+
end
75+
76+
function Http.msgpackDecode(source)
77+
return msgpack.decode(source)
78+
end
79+
7180
return Http

plugin/src/ApiContext.lua

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ function ApiContext:connect()
145145

146146
return Http.get(url)
147147
:andThen(rejectFailedRequests)
148-
:andThen(Http.Response.json)
148+
:andThen(Http.Response.msgpack)
149149
:andThen(rejectWrongProtocolVersion)
150150
:andThen(function(body)
151151
assert(validateApiInfo(body))
@@ -163,7 +163,7 @@ end
163163
function ApiContext:read(ids)
164164
local url = ("%s/api/read/%s"):format(self.__baseUrl, table.concat(ids, ","))
165165

166-
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
166+
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.msgpack):andThen(function(body)
167167
if body.sessionId ~= self.__sessionId then
168168
return Promise.reject("Server changed ID")
169169
end
@@ -191,9 +191,9 @@ function ApiContext:write(patch)
191191
table.insert(updated, fixedUpdate)
192192
end
193193

194-
-- Only add the 'added' field if the table is non-empty, or else Roblox's
195-
-- JSON implementation will turn the table into an array instead of an
196-
-- object, causing API validation to fail.
194+
-- Only add the 'added' field if the table is non-empty, or else the msgpack
195+
-- encode implementation will turn the table into an array instead of a map,
196+
-- causing API validation to fail.
197197
local added
198198
if next(patch.added) ~= nil then
199199
added = patch.added
@@ -206,13 +206,16 @@ function ApiContext:write(patch)
206206
added = added,
207207
}
208208

209-
body = Http.jsonEncode(body)
209+
body = Http.msgpackEncode(body)
210210

211-
return Http.post(url, body):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(responseBody)
212-
Log.info("Write response: {:?}", responseBody)
211+
return Http.post(url, body)
212+
:andThen(rejectFailedRequests)
213+
:andThen(Http.Response.msgpack)
214+
:andThen(function(responseBody)
215+
Log.info("Write response: {:?}", responseBody)
213216

214-
return responseBody
215-
end)
217+
return responseBody
218+
end)
216219
end
217220

218221
function ApiContext:connectWebSocket(packetHandlers)
@@ -234,7 +237,7 @@ function ApiContext:connectWebSocket(packetHandlers)
234237
local closed, errored, received
235238

236239
received = self.__wsClient.MessageReceived:Connect(function(msg)
237-
local data = Http.jsonDecode(msg)
240+
local data = Http.msgpackDecode(msg)
238241
if data.sessionId ~= self.__sessionId then
239242
Log.warn("Received message with wrong session ID; ignoring")
240243
return
@@ -280,7 +283,7 @@ end
280283
function ApiContext:open(id)
281284
local url = ("%s/api/open/%s"):format(self.__baseUrl, id)
282285

283-
return Http.post(url, ""):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
286+
return Http.post(url, ""):andThen(rejectFailedRequests):andThen(Http.Response.msgpack):andThen(function(body)
284287
if body.sessionId ~= self.__sessionId then
285288
return Promise.reject("Server changed ID")
286289
end
@@ -292,7 +295,7 @@ end
292295
function ApiContext:serialize(ids: { string })
293296
local url = ("%s/api/serialize/%s"):format(self.__baseUrl, table.concat(ids, ","))
294297

295-
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
298+
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.msgpack):andThen(function(body)
296299
if body.sessionId ~= self.__sessionId then
297300
return Promise.reject("Server changed ID")
298301
end
@@ -306,7 +309,7 @@ end
306309
function ApiContext:refPatch(ids: { string })
307310
local url = ("%s/api/ref-patch/%s"):format(self.__baseUrl, table.concat(ids, ","))
308311

309-
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
312+
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.msgpack):andThen(function(body)
310313
if body.sessionId ~= self.__sessionId then
311314
return Promise.reject("Server changed ID")
312315
end

plugin/src/Reconciler/diff.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ local function trueEquals(a, b): boolean
5454
end
5555
return true
5656

57+
-- For NaN, check if both values are not equal to themselves
58+
elseif a ~= a and b ~= b then
59+
return true
60+
5761
-- For numbers, compare with epsilon of 0.0001 to avoid floating point inequality
5862
elseif typeA == "number" and typeB == "number" then
5963
return fuzzyEq(a, b, 0.0001)
@@ -274,4 +278,4 @@ local function diff(instanceMap, virtualInstances, rootId)
274278
return true, patch
275279
end
276280

277-
return diff
281+
return diff

0 commit comments

Comments
 (0)