goldsync 0.1.12 → 0.1.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/GoldSync.rbxmx +198 -37
- package/package.json +2 -1
package/assets/GoldSync.rbxmx
CHANGED
|
@@ -22,7 +22,7 @@ local CLIENT_HEADERS = {
|
|
|
22
22
|
}
|
|
23
23
|
local SETTINGS_PREFIX = "GoldSync:v2:"
|
|
24
24
|
local UI_SETTINGS_PREFIX = "GoldSync:ui:v1:"
|
|
25
|
-
local VERSION = "0.1.
|
|
25
|
+
local VERSION = "0.1.14"
|
|
26
26
|
local REQUEST_INTERVAL_SECONDS = 0.15
|
|
27
27
|
local REQUEST_RETRY_DELAYS = { 1, 2, 4 }
|
|
28
28
|
|
|
@@ -340,6 +340,8 @@ local lastRequestAt = 0
|
|
|
340
340
|
local serverConfig
|
|
341
341
|
local states = {}
|
|
342
342
|
local groups = {}
|
|
343
|
+
local batchingStatusUpdates = false
|
|
344
|
+
local splitRootConnections = {}
|
|
343
345
|
local refreshConfig
|
|
344
346
|
local updateGroupSummaries
|
|
345
347
|
local refreshStatusSummary
|
|
@@ -369,6 +371,31 @@ local function collectSplitPaths(root, depth)
|
|
|
369
371
|
return paths
|
|
370
372
|
end
|
|
371
373
|
|
|
374
|
+
local function collectSplitInstances(root, depth)
|
|
375
|
+
local current = { root }
|
|
376
|
+
for _ = 1, depth do
|
|
377
|
+
local nextLevel = {}
|
|
378
|
+
for _, parent in current do
|
|
379
|
+
for _, child in parent:GetChildren() do
|
|
380
|
+
table.insert(nextLevel, child)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
current = nextLevel
|
|
384
|
+
end
|
|
385
|
+
return current
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
local function isAtSplitDepth(root, instance, depth)
|
|
389
|
+
local current = instance
|
|
390
|
+
for _ = 1, depth do
|
|
391
|
+
if not current or current == root then
|
|
392
|
+
return false
|
|
393
|
+
end
|
|
394
|
+
current = current.Parent
|
|
395
|
+
end
|
|
396
|
+
return current == root
|
|
397
|
+
end
|
|
398
|
+
|
|
372
399
|
local function acquireRequestLock()
|
|
373
400
|
if requestLocked then
|
|
374
401
|
local waiter = Instance.new("BindableEvent")
|
|
@@ -464,6 +491,113 @@ local function resolveParent(studioPath)
|
|
|
464
491
|
return current
|
|
465
492
|
end
|
|
466
493
|
|
|
494
|
+
local function disconnectSplitRoot(id)
|
|
495
|
+
local watched = splitRootConnections[id]
|
|
496
|
+
if not watched then
|
|
497
|
+
return
|
|
498
|
+
end
|
|
499
|
+
for _, connection in watched.connections do
|
|
500
|
+
connection:Disconnect()
|
|
501
|
+
end
|
|
502
|
+
for _, connection in watched.nameConnections do
|
|
503
|
+
connection:Disconnect()
|
|
504
|
+
end
|
|
505
|
+
splitRootConnections[id] = nil
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
local function disconnectSplitRoots()
|
|
509
|
+
local ids = {}
|
|
510
|
+
for id in splitRootConnections do
|
|
511
|
+
table.insert(ids, id)
|
|
512
|
+
end
|
|
513
|
+
for _, id in ids do
|
|
514
|
+
disconnectSplitRoot(id)
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
local function discoverSplitRoot(splitRoot, root)
|
|
519
|
+
if not serverConfig or splitRootConnections[splitRoot.id] == nil then
|
|
520
|
+
return
|
|
521
|
+
end
|
|
522
|
+
local existingPaths = {}
|
|
523
|
+
for _, manifest in serverConfig.roots do
|
|
524
|
+
if manifest.splitRootId == splitRoot.id then
|
|
525
|
+
existingPaths[table.concat(manifest.studioPath, "\0")] = true
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
local missingPaths = {}
|
|
529
|
+
for _, relativePath in collectSplitPaths(root, splitRoot.splitDepth) do
|
|
530
|
+
local fullPath = table.clone(splitRoot.studioPath)
|
|
531
|
+
for _, segment in relativePath do
|
|
532
|
+
table.insert(fullPath, segment)
|
|
533
|
+
end
|
|
534
|
+
if not existingPaths[table.concat(fullPath, "\0")] then
|
|
535
|
+
table.insert(missingPaths, relativePath)
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
if #missingPaths > 0 then
|
|
539
|
+
request("POST", "/v1/splits/" .. splitRoot.id .. "/discover", HttpService:JSONEncode({ paths = missingPaths }), {
|
|
540
|
+
["Content-Type"] = "application/json",
|
|
541
|
+
})
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
local function watchSplitRoot(splitRoot)
|
|
546
|
+
local root = resolveRoot(splitRoot.studioPath)
|
|
547
|
+
local watched = splitRootConnections[splitRoot.id]
|
|
548
|
+
if watched and watched.root == root then
|
|
549
|
+
return
|
|
550
|
+
end
|
|
551
|
+
disconnectSplitRoot(splitRoot.id)
|
|
552
|
+
if not root then
|
|
553
|
+
return
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
local entry = {
|
|
557
|
+
root = root,
|
|
558
|
+
generation = 0,
|
|
559
|
+
connections = {},
|
|
560
|
+
nameConnections = {},
|
|
561
|
+
}
|
|
562
|
+
splitRootConnections[splitRoot.id] = entry
|
|
563
|
+
local function scheduleDiscovery()
|
|
564
|
+
entry.generation += 1
|
|
565
|
+
local generation = entry.generation
|
|
566
|
+
task.delay(0.1, function()
|
|
567
|
+
if running and splitRootConnections[splitRoot.id] == entry and entry.generation == generation then
|
|
568
|
+
discoverSplitRoot(splitRoot, root)
|
|
569
|
+
end
|
|
570
|
+
end)
|
|
571
|
+
end
|
|
572
|
+
local function watchName(instance)
|
|
573
|
+
if entry.nameConnections[instance] or not isAtSplitDepth(root, instance, splitRoot.splitDepth) then
|
|
574
|
+
return
|
|
575
|
+
end
|
|
576
|
+
entry.nameConnections[instance] = instance:GetPropertyChangedSignal("Name"):Connect(scheduleDiscovery)
|
|
577
|
+
end
|
|
578
|
+
for _, instance in collectSplitInstances(root, splitRoot.splitDepth) do
|
|
579
|
+
watchName(instance)
|
|
580
|
+
end
|
|
581
|
+
table.insert(entry.connections, root.DescendantAdded:Connect(function(descendant)
|
|
582
|
+
watchName(descendant)
|
|
583
|
+
scheduleDiscovery()
|
|
584
|
+
end))
|
|
585
|
+
table.insert(entry.connections, root.DescendantRemoving:Connect(function(descendant)
|
|
586
|
+
local connection = entry.nameConnections[descendant]
|
|
587
|
+
if connection then
|
|
588
|
+
connection:Disconnect()
|
|
589
|
+
entry.nameConnections[descendant] = nil
|
|
590
|
+
end
|
|
591
|
+
scheduleDiscovery()
|
|
592
|
+
end))
|
|
593
|
+
table.insert(entry.connections, root.AncestryChanged:Connect(function()
|
|
594
|
+
if resolveRoot(splitRoot.studioPath) ~= root then
|
|
595
|
+
disconnectSplitRoot(splitRoot.id)
|
|
596
|
+
end
|
|
597
|
+
end))
|
|
598
|
+
discoverSplitRoot(splitRoot, root)
|
|
599
|
+
end
|
|
600
|
+
|
|
467
601
|
local function applyClassIcon(image, className)
|
|
468
602
|
local success, icon = pcall(function()
|
|
469
603
|
return StudioService:GetClassIcon(className)
|
|
@@ -499,12 +633,8 @@ local function findCode(root)
|
|
|
499
633
|
end
|
|
500
634
|
|
|
501
635
|
local function setStatus(state, text, color)
|
|
502
|
-
local
|
|
503
|
-
|
|
504
|
-
state.status.TextColor3 = color or Color3.fromRGB(180, 180, 190)
|
|
505
|
-
state.dot.TextColor3 = color or Color3.fromRGB(180, 180, 190)
|
|
506
|
-
state.searchQuery = nil
|
|
507
|
-
state.statusKind = if string.sub(text, 1, 6) == "Synced"
|
|
636
|
+
local statusColor = color or Color3.fromRGB(180, 180, 190)
|
|
637
|
+
local statusKind = if string.sub(text, 1, 6) == "Synced"
|
|
508
638
|
then "synced"
|
|
509
639
|
elseif string.find(text, "syncing")
|
|
510
640
|
or string.find(text, "Serializing")
|
|
@@ -514,7 +644,16 @@ local function setStatus(state, text, color)
|
|
|
514
644
|
then "working"
|
|
515
645
|
elseif string.find(text, "Rojo") and string.find(text, "offline") then "paused"
|
|
516
646
|
else "issue"
|
|
517
|
-
if
|
|
647
|
+
if state.status.Text == text and state.status.TextColor3 == statusColor and state.statusKind == statusKind then
|
|
648
|
+
return
|
|
649
|
+
end
|
|
650
|
+
local previousStatusKind = state.statusKind
|
|
651
|
+
state.status.Text = text
|
|
652
|
+
state.status.TextColor3 = statusColor
|
|
653
|
+
state.dot.TextColor3 = statusColor
|
|
654
|
+
state.searchQuery = nil
|
|
655
|
+
state.statusKind = statusKind
|
|
656
|
+
if updateGroupSummaries and not batchingStatusUpdates then
|
|
518
657
|
if previousStatusKind ~= state.statusKind then
|
|
519
658
|
updateGroupSummaries()
|
|
520
659
|
elseif refreshStatusSummary then
|
|
@@ -2393,6 +2532,19 @@ searchBox:GetPropertyChangedSignal("Text"):Connect(function()
|
|
|
2393
2532
|
end)
|
|
2394
2533
|
end)
|
|
2395
2534
|
|
|
2535
|
+
local function manifestChanged(previous, current)
|
|
2536
|
+
return not previous
|
|
2537
|
+
or previous.hash ~= current.hash
|
|
2538
|
+
or previous.exists ~= current.exists
|
|
2539
|
+
or previous.deleted ~= current.deleted
|
|
2540
|
+
or previous.revision ~= current.revision
|
|
2541
|
+
or previous.gitConflict ~= current.gitConflict
|
|
2542
|
+
or previous.conflictToken ~= current.conflictToken
|
|
2543
|
+
or previous.file ~= current.file
|
|
2544
|
+
or previous.splitRootId ~= current.splitRootId
|
|
2545
|
+
or table.concat(previous.studioPath, "\0") ~= table.concat(current.studioPath, "\0")
|
|
2546
|
+
end
|
|
2547
|
+
|
|
2396
2548
|
refreshConfig = function()
|
|
2397
2549
|
local response, requestError = request("GET", "/v1/config")
|
|
2398
2550
|
if not response or not response.Success then
|
|
@@ -2407,13 +2559,15 @@ refreshConfig = function()
|
|
|
2407
2559
|
end
|
|
2408
2560
|
|
|
2409
2561
|
local decoded = HttpService:JSONDecode(response.Body)
|
|
2410
|
-
|
|
2562
|
+
local projectChanged = serverConfig
|
|
2411
2563
|
and (serverConfig.projectId ~= decoded.projectId or serverConfig.workspaceId ~= decoded.workspaceId)
|
|
2412
|
-
|
|
2564
|
+
local connectionChanged = serverConfig and serverConfig.rojoConnected ~= decoded.rojoConnected
|
|
2565
|
+
if projectChanged then
|
|
2413
2566
|
for _, state in states do
|
|
2414
2567
|
disconnectRoot(state)
|
|
2415
2568
|
destroyConflictWorkspace(state)
|
|
2416
2569
|
end
|
|
2570
|
+
disconnectSplitRoots()
|
|
2417
2571
|
table.clear(states)
|
|
2418
2572
|
for _, child in rootsContainer:GetChildren() do
|
|
2419
2573
|
if child ~= rootsLayout then
|
|
@@ -2432,36 +2586,38 @@ refreshConfig = function()
|
|
|
2432
2586
|
end
|
|
2433
2587
|
connectionDot.TextColor3 = connectionStatus.TextColor3
|
|
2434
2588
|
|
|
2435
|
-
local
|
|
2436
|
-
for _, manifest in decoded.roots do
|
|
2437
|
-
if manifest.splitRootId then
|
|
2438
|
-
existingSplitPaths[manifest.splitRootId .. "\0" .. table.concat(manifest.studioPath, "\0")] = true
|
|
2439
|
-
end
|
|
2440
|
-
end
|
|
2589
|
+
local currentSplitRootIds = {}
|
|
2441
2590
|
for _, splitRoot in decoded.splitRoots do
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
end
|
|
2450
|
-
if not existingSplitPaths[splitRoot.id .. "\0" .. table.concat(fullPath, "\0")] then
|
|
2451
|
-
table.insert(missingPaths, relativePath)
|
|
2452
|
-
end
|
|
2453
|
-
end
|
|
2454
|
-
if #missingPaths > 0 then
|
|
2455
|
-
request("POST", "/v1/splits/" .. splitRoot.id .. "/discover", HttpService:JSONEncode({ paths = missingPaths }), {
|
|
2456
|
-
["Content-Type"] = "application/json",
|
|
2457
|
-
})
|
|
2458
|
-
end
|
|
2591
|
+
currentSplitRootIds[splitRoot.id] = true
|
|
2592
|
+
watchSplitRoot(splitRoot)
|
|
2593
|
+
end
|
|
2594
|
+
local staleSplitRootIds = {}
|
|
2595
|
+
for id in splitRootConnections do
|
|
2596
|
+
if not currentSplitRootIds[id] then
|
|
2597
|
+
table.insert(staleSplitRootIds, id)
|
|
2459
2598
|
end
|
|
2460
2599
|
end
|
|
2600
|
+
for _, id in staleSplitRootIds do
|
|
2601
|
+
disconnectSplitRoot(id)
|
|
2602
|
+
end
|
|
2461
2603
|
|
|
2604
|
+
local treeChanged = projectChanged == true or connectionChanged == true
|
|
2605
|
+
batchingStatusUpdates = true
|
|
2462
2606
|
for order, manifest in decoded.roots do
|
|
2463
|
-
local state = states[manifest.id]
|
|
2464
|
-
|
|
2607
|
+
local state = states[manifest.id]
|
|
2608
|
+
local created = state == nil
|
|
2609
|
+
if created then
|
|
2610
|
+
state = createRootRow(manifest, order)
|
|
2611
|
+
end
|
|
2612
|
+
local resolvedRoot = resolveRoot(manifest.studioPath)
|
|
2613
|
+
local rootChanged = state.observedRoot ~= resolvedRoot
|
|
2614
|
+
state.observedRoot = resolvedRoot
|
|
2615
|
+
if created or connectionChanged or rootChanged or manifestChanged(state.manifest, manifest) then
|
|
2616
|
+
reconcile(state, manifest)
|
|
2617
|
+
treeChanged = true
|
|
2618
|
+
else
|
|
2619
|
+
state.manifest = manifest
|
|
2620
|
+
end
|
|
2465
2621
|
end
|
|
2466
2622
|
local currentIds = {}
|
|
2467
2623
|
for _, manifest in decoded.roots do
|
|
@@ -2479,9 +2635,13 @@ refreshConfig = function()
|
|
|
2479
2635
|
destroyConflictWorkspace(state)
|
|
2480
2636
|
state.row:Destroy()
|
|
2481
2637
|
states[id] = nil
|
|
2638
|
+
treeChanged = true
|
|
2639
|
+
end
|
|
2640
|
+
batchingStatusUpdates = false
|
|
2641
|
+
if treeChanged then
|
|
2642
|
+
updateGroupSummaries()
|
|
2643
|
+
applyTreeFilter()
|
|
2482
2644
|
end
|
|
2483
|
-
updateGroupSummaries()
|
|
2484
|
-
applyTreeFilter()
|
|
2485
2645
|
return true
|
|
2486
2646
|
end
|
|
2487
2647
|
|
|
@@ -2497,6 +2657,7 @@ end)
|
|
|
2497
2657
|
|
|
2498
2658
|
plugin.Unloading:Connect(function()
|
|
2499
2659
|
running = false
|
|
2660
|
+
disconnectSplitRoots()
|
|
2500
2661
|
for _, state in states do
|
|
2501
2662
|
disconnectRoot(state)
|
|
2502
2663
|
destroyConflictWorkspace(state)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goldsync",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Automatic native Roblox asset synchronization for Rojo projects.",
|
|
5
5
|
"author": "tay",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"test": "node --test",
|
|
22
22
|
"package:companion": "node scripts/package-companion.mjs",
|
|
23
|
+
"prepublishOnly": "npm test",
|
|
23
24
|
"prepack": "node scripts/prepare-npm-package.mjs",
|
|
24
25
|
"postpack": "node scripts/cleanup-npm-package.mjs"
|
|
25
26
|
},
|