There's an issue with the new protocol (v1) that I'm trying to resolve but having trouble with.
Given the following filesystem tree:
Rojo should produce the following Roblox hierarchy:
src (Folder)
|-- foo (ModuleScript)
If foo.lua
is renamed to foo.not-lua
, the file watcher will tell the plugin that these paths changed:
[["src", "foo.lua"], ["src", "foo.not-lua"]]
The plugin issues a read for these paths, and gets back this result from the server:
[null, ["details about foo.not-lua"]]
The plugin tries to reconcile the ["src", "foo.lua"]
to be nil, but won't be able to find anything to delete, since the object is named foo
, not foo.lua
-- we'd have to map the file name to a Name-ClassName
pair!
The plugin ends up creating this tree:
src (Folder)
|-- foo (ModuleScript)
`-- foo.not-lua (StringValue)
This differs from the tree we would get if we did a fresh sync, which is not good!
The protocol needs to be modified to communicate paths in a different way, but I'm not entirely sure how to progress.
Soution 1: Use Name-ClassName
pairs for all route entries
Instead of getting a change as ["src", "foo.lua"]
, we'd get a change as [["src", "Folder"], ["foo", "ModuleScript"]]
-- this requires plugins to do a little bit more work when transforming files, but not too much more.
This runs into a problem when it comes to the current JsonModelPlugin
implementation -- we don't know the ClassName
of an object until we read the file. If a file is deleted, we have no way to recover the identity of the object with this idea! Normally, we'd be able to change the plugin's convention to include the ClassName
in the file name, but that won't be possible when rbxlx
and rbxmx
support is added, which have arbitrary parent containers.
Solution 2: Keep an index that maps routes to instances
This solution would introduce a map from VFS routes to Roblox instances in the plugin while it's syncing.
When the plugin picks up a change from the server, it figures out what object is affected, and can use this to destroy that object if a null
result is received. Any time an object is modified, the index would be updated.
Since partial sync operations only happen when a portion of the tree is read, the map doesn't need to stick around for very long.
The major issue I see occurs when the user mutates objects that are in the index (which will happen with bi-directional syncing) it might be difficult to update or discard the correct portions of the index.
Solution 3: Keep the entire tree in-memory on the server
This acts like an extension to solution 2, but implemented on the server. Instead of reading from the disk on demand, Rojo can limit reads to when files change, keeping the results in memory. The total number of reads should be the same, so performance should be fine.
This solution has the same advantages as solution 2, but with the advantage that the user can't mutate objects in the index directly, simplifying a lot of operations.