Merge branch 'master' into headless-api

This commit is contained in:
boatbomber 2025-11-06 19:52:18 -08:00 • committed by GitHub
commit deace925b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 598 additions and 130 deletions

View file

@ -1 +1 @@
7.6.0
7.6.1

View file

@ -1,9 +1,9 @@
{
"Version": [
0,
694,
697,
0,
6940982
6970925
],
"Classes": {
"Accessory": {
@ -550,6 +550,9 @@
"Scale": {
"Float32": 1.0
},
"SlimHash": {
"SharedString": ""
},
"SourceAssetId": {
"Int64": -1
},
@ -1975,6 +1978,47 @@
}
}
},
"AnimationGraphDefinition": {
"Name": "AnimationGraphDefinition",
"Tags": [],
"Superclass": "AnimationClip",
"Properties": {},
"DefaultProperties": {
"Archivable": {
"Bool": true
},
"Attributes": {
"Attributes": {}
},
"Capabilities": {
"SecurityCapabilities": 0
},
"GuidBinaryString": {
"BinaryString": "AAAAAAAAAAAAAAAAAAAAAA=="
},
"HistoryId": {
"UniqueId": "00000000000000000000000000000000"
},
"Loop": {
"Bool": true
},
"Priority": {
"Enum": 2
},
"Sandboxed": {
"Bool": false
},
"SourceAssetId": {
"Int64": -1
},
"Tags": {
"Tags": []
},
"UniqueId": {
"UniqueId": "00000000000000000000000000000000"
}
}
},
"AnimationImportData": {
"Name": "AnimationImportData",
"Tags": [
@ -1995,6 +2039,84 @@
}
}
},
"AnimationNode": {
"Name": "AnimationNode",
"Tags": [
"NotCreatable",
"NotReplicated"
],
"Superclass": "Object",
"Properties": {},
"DefaultProperties": {}
},
"AnimationNodeDefinition": {
"Name": "AnimationNodeDefinition",
"Tags": [],
"Superclass": "Instance",
"Properties": {
"InputPinData": {
"Name": "InputPinData",
"Scriptability": "None",
"DataType": {
"Value": "BinaryString"
},
"Tags": [
"Hidden",
"NotScriptable"
],
"Kind": {
"Canonical": {
"Serialization": "Serializes"
}
}
},
"NodeType": {
"Name": "NodeType",
"Scriptability": "ReadWrite",
"DataType": {
"Enum": "AnimationNodeType"
},
"Tags": [],
"Kind": {
"Canonical": {
"Serialization": "Serializes"
}
}
}
},
"DefaultProperties": {
"Archivable": {
"Bool": true
},
"Attributes": {
"Attributes": {}
},
"Capabilities": {
"SecurityCapabilities": 0
},
"HistoryId": {
"UniqueId": "00000000000000000000000000000000"
},
"InputPinData": {
"BinaryString": ""
},
"NodeType": {
"Enum": 0
},
"Sandboxed": {
"Bool": false
},
"SourceAssetId": {
"Int64": -1
},
"Tags": {
"Tags": []
},
"UniqueId": {
"UniqueId": "00000000000000000000000000000000"
}
}
},
"AnimationRigData": {
"Name": "AnimationRigData",
"Tags": [],
@ -6332,9 +6454,7 @@
},
"AudioSpeechToText": {
"Name": "AudioSpeechToText",
"Tags": [
"NotBrowsable"
],
"Tags": [],
"Superclass": "Instance",
"Properties": {
"Enabled": {
@ -21030,7 +21150,7 @@
"NotBrowsable",
"NotCreatable",
"NotReplicated",
"Settings"
"Service"
],
"Superclass": "Instance",
"Properties": {
@ -23979,6 +24099,27 @@
}
}
},
"EncodingService": {
"Name": "EncodingService",
"Tags": [
"NotCreatable",
"NotReplicated",
"Service"
],
"Superclass": "Instance",
"Properties": {},
"DefaultProperties": {
"Archivable": {
"Bool": true
},
"HistoryId": {
"UniqueId": "00000000000000000000000000000000"
},
"UniqueId": {
"UniqueId": "00000000000000000000000000000000"
}
}
},
"EqualizerSoundEffect": {
"Name": "EqualizerSoundEffect",
"Tags": [],
@ -24135,27 +24276,6 @@
}
}
},
"ExampleService": {
"Name": "ExampleService",
"Tags": [
"NotCreatable",
"NotReplicated",
"Service"
],
"Superclass": "Instance",
"Properties": {},
"DefaultProperties": {
"Archivable": {
"Bool": true
},
"HistoryId": {
"UniqueId": "00000000000000000000000000000000"
},
"UniqueId": {
"UniqueId": "00000000000000000000000000000000"
}
}
},
"ExampleV2Service": {
"Name": "ExampleV2Service",
"Tags": [
@ -26293,6 +26413,9 @@
"Scale": {
"Float32": 1.0
},
"SlimHash": {
"SharedString": ""
},
"SourceAssetId": {
"Int64": -1
},
@ -27586,7 +27709,7 @@
"NotBrowsable",
"NotCreatable",
"NotReplicated",
"Settings"
"Service"
],
"Superclass": "Instance",
"Properties": {
@ -32260,6 +32383,9 @@
"Scale": {
"Float32": 1.0
},
"SlimHash": {
"SharedString": ""
},
"SourceAssetId": {
"Int64": -1
},
@ -41477,6 +41603,22 @@
"Serialization": "DoesNotSerialize"
}
}
},
"IsSlimEnabled": {
"Name": "IsSlimEnabled",
"Scriptability": "None",
"DataType": {
"Value": "Bool"
},
"Tags": [
"Hidden",
"NotScriptable"
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
}
}
}
},
"DefaultProperties": {
@ -41593,7 +41735,7 @@
"Tags": [
"NotCreatable",
"NotReplicated",
"Settings"
"Service"
],
"Superclass": "Instance",
"Properties": {},
@ -42203,6 +42345,19 @@
}
}
},
"EmissiveFilePath": {
"Name": "EmissiveFilePath",
"Scriptability": "ReadWrite",
"DataType": {
"Value": "String"
},
"Tags": [],
"Kind": {
"Canonical": {
"Serialization": "Serializes"
}
}
},
"IsPbr": {
"Name": "IsPbr",
"Scriptability": "Read",
@ -45055,7 +45210,7 @@
"Name": "SlimHash",
"Scriptability": "None",
"DataType": {
"Value": "BinaryString"
"Value": "SharedString"
},
"Tags": [
"Hidden",
@ -45063,7 +45218,7 @@
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
"Serialization": "Serializes"
}
}
},
@ -45163,6 +45318,9 @@
"Scale": {
"Float32": 1.0
},
"SlimHash": {
"SharedString": ""
},
"SourceAssetId": {
"Int64": -1
},
@ -50221,7 +50379,7 @@
"Tags": [
"NotCreatable",
"NotReplicated",
"Settings"
"Service"
],
"Superclass": "Instance",
"Properties": {
@ -51305,6 +51463,22 @@
}
}
},
"ChatAvailabilityStatus": {
"Name": "ChatAvailabilityStatus",
"Scriptability": "None",
"DataType": {
"Value": "String"
},
"Tags": [
"Hidden",
"NotReplicated"
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
}
}
},
"ChatMode": {
"Name": "ChatMode",
"Scriptability": "None",
@ -51568,10 +51742,12 @@
"DataType": {
"Value": "Bool"
},
"Tags": [],
"Tags": [
"Hidden"
],
"Kind": {
"Canonical": {
"Serialization": "Serializes"
"Serialization": "DoesNotSerialize"
}
}
},
@ -53409,6 +53585,23 @@
],
"Superclass": "LayerCollector",
"Properties": {
"Plugin": {
"Name": "Plugin",
"Scriptability": "None",
"DataType": {
"Value": "Ref"
},
"Tags": [
"Hidden",
"NotReplicated",
"ReadOnly"
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
}
}
},
"Title": {
"Name": "Title",
"Scriptability": "ReadWrite",
@ -58013,6 +58206,22 @@
}
}
},
"FrameNumber": {
"Name": "FrameNumber",
"Scriptability": "Read",
"DataType": {
"Value": "Int64"
},
"Tags": [
"NotReplicated",
"ReadOnly"
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
}
}
},
"PredictionState": {
"Name": "PredictionState",
"Scriptability": "Read",
@ -58043,22 +58252,6 @@
"Serialization": "DoesNotSerialize"
}
}
},
"ServerFrame": {
"Name": "ServerFrame",
"Scriptability": "Read",
"DataType": {
"Value": "Int64"
},
"Tags": [
"NotReplicated",
"ReadOnly"
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
}
}
}
},
"DefaultProperties": {
@ -66735,6 +66928,19 @@
}
}
},
"DefaultScriptSyncFileType": {
"Name": "DefaultScriptSyncFileType",
"Scriptability": "None",
"DataType": {
"Enum": "DefaultScriptSyncFileType"
},
"Tags": [],
"Kind": {
"Canonical": {
"Serialization": "Serializes"
}
}
},
"DeprecatedObjectsShown": {
"Name": "DeprecatedObjectsShown",
"Scriptability": "ReadWrite",
@ -74683,7 +74889,7 @@
"SecurityCapabilities": 0
},
"ChatTranslationFTUXShown": {
"Bool": true
"Bool": false
},
"ChatTranslationToggleEnabled": {
"Bool": false
@ -76244,6 +76450,9 @@
"Scale": {
"Float32": 1.0
},
"SlimHash": {
"SharedString": ""
},
"SourceAssetId": {
"Int64": -1
},
@ -80533,7 +80742,7 @@
"Tags": [
"NotCreatable",
"NotReplicated",
"UserSettings"
"Service"
],
"Superclass": "Instance",
"Properties": {
@ -82451,6 +82660,90 @@
}
}
},
"ValueCurve": {
"Name": "ValueCurve",
"Tags": [],
"Superclass": "Instance",
"Properties": {
"Length": {
"Name": "Length",
"Scriptability": "Read",
"DataType": {
"Value": "Int32"
},
"Tags": [
"NotReplicated",
"ReadOnly"
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
}
}
},
"ValueType": {
"Name": "ValueType",
"Scriptability": "Read",
"DataType": {
"Value": "String"
},
"Tags": [
"NotReplicated",
"ReadOnly"
],
"Kind": {
"Canonical": {
"Serialization": "DoesNotSerialize"
}
}
},
"ValuesAndTimes": {
"Name": "ValuesAndTimes",
"Scriptability": "None",
"DataType": {
"Value": "BinaryString"
},
"Tags": [
"Hidden",
"NotScriptable"
],
"Kind": {
"Canonical": {
"Serialization": "Serializes"
}
}
}
},
"DefaultProperties": {
"Archivable": {
"Bool": true
},
"Attributes": {
"Attributes": {}
},
"Capabilities": {
"SecurityCapabilities": 0
},
"HistoryId": {
"UniqueId": "00000000000000000000000000000000"
},
"Sandboxed": {
"Bool": false
},
"SourceAssetId": {
"Int64": -1
},
"Tags": {
"Tags": []
},
"UniqueId": {
"UniqueId": "00000000000000000000000000000000"
},
"ValuesAndTimes": {
"BinaryString": "AgAAAAAAAAABAAAAAAAAAA=="
}
}
},
"Vector3Curve": {
"Name": "Vector3Curve",
"Tags": [],
@ -86753,21 +87046,6 @@
}
}
},
"UseImprovedModelLod": {
"Name": "UseImprovedModelLod",
"Scriptability": "None",
"DataType": {
"Enum": "RolloutState"
},
"Tags": [
"NotScriptable"
],
"Kind": {
"Canonical": {
"Serialization": "Serializes"
}
}
},
"UseNewLuauTypeSolver": {
"Name": "UseNewLuauTypeSolver",
"Scriptability": "None",
@ -86942,6 +87220,9 @@
"SignalBehavior": {
"Enum": 0
},
"SlimHash": {
"SharedString": ""
},
"SourceAssetId": {
"Int64": -1
},
@ -86975,9 +87256,6 @@
"UniqueId": {
"UniqueId": "00000000000000000000000000000000"
},
"UseImprovedModelLod": {
"Enum": 0
},
"UseNewLuauTypeSolver": {
"Enum": 0
},
@ -87103,6 +87381,9 @@
"Scale": {
"Float32": 1.0
},
"SlimHash": {
"SharedString": ""
},
"SourceAssetId": {
"Int64": -1
},
@ -88147,6 +88428,25 @@
"Timeout": 10
}
},
"AnimationNodeType": {
"name": "AnimationNodeType",
"items": {
"AddNode": 1,
"Blend1DNode": 3,
"Blend2DNode": 4,
"BlendNode": 2,
"ClipNode": 5,
"GraphOutput": 6,
"InvalidNode": 0,
"MaskNode": 7,
"PrioritySelectNode": 8,
"RandomSequenceNode": 9,
"SelectNode": 10,
"SequenceNode": 11,
"SpeedNode": 12,
"SubtractNode": 13
}
},
"AnimationPriority": {
"name": "AnimationPriority",
"items": {
@ -88888,6 +89188,13 @@
"Relevance": 1
}
},
"CatalogTimedOptionFilter": {
"name": "CatalogTimedOptionFilter",
"items": {
"All": 1,
"TimedOptionOnly": 2
}
},
"CellBlock": {
"name": "CellBlock",
"items": {
@ -89120,6 +89427,12 @@
"TriggerForIncompleteCompletions": 3
}
},
"CompressionAlgorithm": {
"name": "CompressionAlgorithm",
"items": {
"Zstd": 0
}
},
"ComputerCameraMovementMode": {
"name": "ComputerCameraMovementMode",
"items": {
@ -89403,6 +89716,13 @@
"Timeout": 1
}
},
"DefaultScriptSyncFileType": {
"name": "DefaultScriptSyncFileType",
"items": {
"Lua": 0,
"Luau": 1
}
},
"DevCameraOcclusionMode": {
"name": "DevCameraOcclusionMode",
"items": {
@ -89482,7 +89802,8 @@
"DeviceFeatureType": {
"name": "DeviceFeatureType",
"items": {
"DeviceCapture": 0
"DeviceCapture": 0,
"InExperienceFAE": 1
}
},
"DeviceForm": {
@ -89670,6 +89991,16 @@
"DefaultScope": 0
}
},
"ExperienceEventStatus": {
"name": "ExperienceEventStatus",
"items": {
"Active": 0,
"Cancelled": 1,
"Moderated": 2,
"Unknown": 4,
"Unpublished": 3
}
},
"ExperienceStateRecordingLoadMode": {
"name": "ExperienceStateRecordingLoadMode",
"items": {
@ -90141,6 +90472,16 @@
"UINotification": 3
}
},
"HashAlgorithm": {
"name": "HashAlgorithm",
"items": {
"Blake2b": 0,
"Blake3": 1,
"Md5": 2,
"Sha1": 3,
"Sha256": 4
}
},
"HighlightDepthMode": {
"name": "HighlightDepthMode",
"items": {
@ -90531,14 +90872,14 @@
"Menu": 319,
"Minus": 45,
"Mode": 313,
"MouseBackButton": 1021,
"MouseLeftButton": 1018,
"MouseMiddleButton": 1020,
"MouseNoButton": 1022,
"MousePosition": 1025,
"MouseRightButton": 1019,
"MouseX": 1023,
"MouseY": 1024,
"MouseBackButton": 1029,
"MouseLeftButton": 1026,
"MouseMiddleButton": 1028,
"MouseNoButton": 1030,
"MousePosition": 1033,
"MouseRightButton": 1027,
"MouseX": 1031,
"MouseY": 1032,
"N": 110,
"Nine": 57,
"NumLock": 300,
@ -90581,7 +90922,15 @@
"Tab": 9,
"Three": 51,
"Thumbstick1": 1016,
"Thumbstick1Down": 1019,
"Thumbstick1Left": 1020,
"Thumbstick1Right": 1021,
"Thumbstick1Up": 1018,
"Thumbstick2": 1017,
"Thumbstick2Down": 1023,
"Thumbstick2Left": 1024,
"Thumbstick2Right": 1025,
"Thumbstick2Up": 1022,
"Tilde": 126,
"Two": 50,
"U": 117,
@ -91023,6 +91372,7 @@
"items": {
"Automatic": 0,
"Disabled": 2,
"SLIM": 4,
"StreamingMesh": 1
}
},
@ -91044,6 +91394,30 @@
"PersistentPerPlayer": 3
}
},
"ModerationResultCategory": {
"name": "ModerationResultCategory",
"items": {
"Borderline": 1,
"NoViolationDetected": 2,
"ViolationDetected": 0
}
},
"ModerationResultLabel": {
"name": "ModerationResultLabel",
"items": {
"ChildExploitation": 0,
"DiscriminationSlursAndHateSpeech": 4,
"IllegalAndRegulatedGoodsAndActivities": 8,
"Other": 100,
"Profanity": 9,
"RealWorldSensitiveEvents": 5,
"RomanticAndSexualContent": 7,
"SuicideSelfInjuryAndHarmfulBehavior": 1,
"TerrorismAndViolentExtremism": 3,
"ThreatsBullyingAndHarassment": 2,
"ViolentContentAndGore": 6
}
},
"ModerationStatus": {
"name": "ModerationStatus",
"items": {
@ -91855,6 +92229,14 @@
"UnderWater": 23
}
},
"ReviewableContentState": {
"name": "ReviewableContentState",
"items": {
"Completed": 1,
"Failed": 2,
"Pending": 0
}
},
"RibbonTool": {
"name": "RibbonTool",
"items": {
@ -92084,6 +92466,7 @@
"AccessOutsideWrite": 2,
"Animation": 15,
"AssetRequire": 3,
"Assistant": 31,
"Audio": 8,
"Avatar": 16,
"Basic": 7,
@ -92094,16 +92477,25 @@
"DataStore": 9,
"Environment": 18,
"Input": 17,
"InternalTest": 29,
"LegacySound": 20,
"LoadString": 4,
"LocalUser": 24,
"Network": 10,
"Physics": 11,
"Players": 21,
"Plugin": 23,
"PluginOrOpenCloud": 30,
"RemoteCommand": 32,
"RemoteEvent": 19,
"RobloxEngine": 27,
"RobloxScript": 26,
"RunClientScript": 0,
"RunServerScript": 1,
"ScriptGlobals": 5,
"UI": 12
"UI": 12,
"Unassigned": 28,
"WritePlayer": 25
}
},
"SelectionBehavior": {
@ -92659,6 +93051,35 @@
"Voxel": 1
}
},
"TelemetryBackend": {
"name": "TelemetryBackend",
"items": {
"Counter": 6,
"EphemeralCounter": 4,
"EphemeralStat": 5,
"EventIngest": 1,
"Points": 2,
"Stat": 7,
"Teletune": 3,
"UNSPECIFIED": 0
}
},
"TelemetryStandardizedField": {
"name": "TelemetryStandardizedField",
"items": {
"AddArchitectureInfo": 7,
"AddCpuInfo": 8,
"AddCurrentContextName": 5,
"AddDatacenterId": 0,
"AddMemoryInfo": 9,
"AddOsInfo": 6,
"AddPlaceId": 1,
"AddPlaceInstanceId": 3,
"AddPlaySessionId": 4,
"AddSessionInfo": 10,
"AddUniverseId": 2
}
},
"TeleportMethod": {
"name": "TeleportMethod",
"items": {
@ -93098,7 +93519,8 @@
"CaptureNotInGallery": 3,
"IneligibleCapture": 4,
"NeedPermission": 1,
"Success": 0
"Success": 0,
"UploadQuotaReached": 5
}
},
"UsageContext": {
@ -93430,7 +93852,8 @@
"name": "WebStreamClientType",
"items": {
"RawStream": 1,
"SSE": 0
"SSE": 0,
"WebSocket": 2
}
},
"WeldConstraintPreserve": {

View file

@ -4,8 +4,6 @@ local Packages = Rojo.Packages
local Roact = require(Packages.Roact)
local Timer = require(Plugin.Timer)
local PatchTree = require(Plugin.PatchTree)
local Settings = require(Plugin.Settings)
local Theme = require(Plugin.App.Theme)
local TextButton = require(Plugin.App.Components.TextButton)
@ -24,7 +22,6 @@ function ConfirmingPage:init()
self.containerSize, self.setContainerSize = Roact.createBinding(Vector2.new(0, 0))
self:setState({
patchTree = nil,
showingStringDiff = false,
oldString = "",
newString = "",
@ -32,28 +29,6 @@ function ConfirmingPage:init()
oldTable = {},
newTable = {},
})
if self.props.confirmData and self.props.confirmData.patch and self.props.confirmData.instanceMap then
self:buildPatchTree()
end
end
function ConfirmingPage:didUpdate(prevProps)
if prevProps.confirmData ~= self.props.confirmData then
self:buildPatchTree()
end
end
function ConfirmingPage:buildPatchTree()
Timer.start("ConfirmingPage:buildPatchTree")
self:setState({
patchTree = PatchTree.build(
self.props.confirmData.patch,
self.props.confirmData.instanceMap,
{ "Property", "Current", "Incoming" }
),
})
Timer.stop()
end
function ConfirmingPage:render()
@ -79,7 +54,7 @@ function ConfirmingPage:render()
transparency = self.props.transparency,
layoutOrder = 3,
patchTree = self.state.patchTree,
patchTree = self.props.patchTree,
showStringDiff = function(oldString: string, newString: string)
self:setState({

View file

@ -4,6 +4,8 @@ local Packages = Rojo.Packages
local Roact = require(Packages.Roact)
local Theme = require(Plugin.App.Theme)
local Spinner = require(Plugin.App.Components.Spinner)
local e = Roact.createElement
@ -11,11 +13,35 @@ local e = Roact.createElement
local ConnectingPage = Roact.Component:extend("ConnectingPage")
function ConnectingPage:render()
return e(Spinner, {
position = UDim2.new(0.5, 0, 0.5, 0),
anchorPoint = Vector2.new(0.5, 0.5),
transparency = self.props.transparency,
})
return Theme.with(function(theme)
return e("Frame", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}, {
Spinner = e(Spinner, {
position = UDim2.new(0.5, 0, 0.5, 0),
anchorPoint = Vector2.new(0.5, 0.5),
transparency = self.props.transparency,
}),
Text = if type(self.props.text) == "string" and #self.props.text > 0
then e("TextLabel", {
Text = self.props.text,
Position = UDim2.new(0.5, 0, 0.5, 30),
Size = UDim2.new(1, -40, 0.5, -40),
AnchorPoint = Vector2.new(0.5, 0),
TextXAlignment = Enum.TextXAlignment.Center,
TextYAlignment = Enum.TextYAlignment.Top,
RichText = true,
FontFace = theme.Font.Thin,
TextSize = theme.TextSize.Medium,
TextColor3 = theme.SubTextColor,
TextTruncate = Enum.TextTruncate.AtEnd,
TextTransparency = self.props.transparency,
BackgroundTransparency = 1,
})
else nil,
})
end)
end
return ConnectingPage

View file

@ -740,6 +740,12 @@ function App:startSession(host: string?, port: string?)
twoWaySync = Settings:get("twoWaySync"),
})
serveSession:setUpdateLoadingTextCallback(function(text: string)
self:setState({
connectingText = text,
})
end)
self.cleanupPrecommit = serveSession:hookPrecommit(function(patch, instanceMap)
-- Build new tree for patch
self:setState({
@ -914,11 +920,13 @@ function App:startSession(host: string?, port: string?)
end
end
self:setState({
connectingText = "Computing diff view...",
})
self:setState({
appStatus = AppStatus.Confirming,
patchTree = PatchTree.build(patch, instanceMap, { "Property", "Current", "Incoming" }),
confirmData = {
instanceMap = instanceMap,
patch = patch,
serverInfo = serverInfo,
},
toolbarIcon = Assets.Images.PluginButton,
@ -1051,6 +1059,7 @@ function App:render()
ConfirmingPage = createPageElement(AppStatus.Confirming, {
confirmData = self.state.confirmData,
patchTree = self.state.patchTree,
createPopup = not self.state.guiEnabled,
onAbort = function()
@ -1064,7 +1073,9 @@ function App:render()
end,
}),
Connecting = createPageElement(AppStatus.Connecting),
Connecting = createPageElement(AppStatus.Connecting, {
text = self.state.connectingText,
}),
Connected = createPageElement(AppStatus.Connected, {
projectName = self.state.projectName,

View file

@ -16,6 +16,14 @@ local Types = require(Plugin.Types)
local decodeValue = require(Plugin.Reconciler.decodeValue)
local getProperty = require(Plugin.Reconciler.getProperty)
local function yieldIfNeeded(clock)
if os.clock() - clock > 1 / 20 then
task.wait()
return os.clock()
end
return clock
end
local function alphabeticalNext(t, state)
-- Equivalent of the next function, but returns the keys in the alphabetic
-- order of node names. We use a temporary ordered key table that is stored in the
@ -132,7 +140,6 @@ end
-- props must contain id, and cannot contain children or parentId
-- other than those three, it can hold anything
function Tree:addNode(parent, props)
Timer.start("Tree:addNode")
assert(props.id, "props must contain id")
parent = parent or "ROOT"
@ -143,7 +150,6 @@ function Tree:addNode(parent, props)
for k, v in props do
node[k] = v
end
Timer.stop()
return node
end
@ -154,25 +160,25 @@ function Tree:addNode(parent, props)
local parentNode = self:getNode(parent)
if not parentNode then
Log.warn("Failed to create node since parent doesnt exist: {}, {}", parent, props)
Timer.stop()
return
end
parentNode.children[node.id] = node
self.idToNode[node.id] = node
Timer.stop()
return node
end
-- Given a list of ancestor ids in descending order, builds the nodes for them
-- using the patch and instanceMap info
function Tree:buildAncestryNodes(previousId: string?, ancestryIds: { string }, patch, instanceMap)
Timer.start("Tree:buildAncestryNodes")
local clock = os.clock()
-- Build nodes for ancestry by going up the tree
previousId = previousId or "ROOT"
for _, ancestorId in ancestryIds do
clock = yieldIfNeeded(clock)
local value = instanceMap.fromIds[ancestorId] or patch.added[ancestorId]
if not value then
Log.warn("Failed to find ancestor object for " .. ancestorId)
@ -186,8 +192,6 @@ function Tree:buildAncestryNodes(previousId: string?, ancestryIds: { string }, p
})
previousId = ancestorId
end
Timer.stop()
end
local PatchTree = {}
@ -196,12 +200,16 @@ local PatchTree = {}
-- uses changeListHeaders in node.changeList
function PatchTree.build(patch, instanceMap, changeListHeaders)
Timer.start("PatchTree.build")
local clock = os.clock()
local tree = Tree.new()
local knownAncestors = {}
Timer.start("patch.updated")
for _, change in patch.updated do
clock = yieldIfNeeded(clock)
local instance = instanceMap.fromIds[change.id]
if not instance then
continue
@ -281,6 +289,8 @@ function PatchTree.build(patch, instanceMap, changeListHeaders)
Timer.start("patch.removed")
for _, idOrInstance in patch.removed do
clock = yieldIfNeeded(clock)
local instance = if Types.RbxId(idOrInstance) then instanceMap.fromIds[idOrInstance] else idOrInstance
if not instance then
-- If we're viewing a past patch, the instance is already removed
@ -325,6 +335,8 @@ function PatchTree.build(patch, instanceMap, changeListHeaders)
Timer.start("patch.added")
for id, change in patch.added do
clock = yieldIfNeeded(clock)
-- Gather ancestors from existing DOM or future additions
local ancestryIds = {}
local parentId = change.Parent

View file

@ -107,6 +107,7 @@ function ServeSession.new(options)
__connections = connections,
__precommitCallbacks = {},
__postcommitCallbacks = {},
__updateLoadingText = function() end,
}
setmetatable(self, ServeSession)
@ -137,6 +138,14 @@ function ServeSession:setConfirmCallback(callback)
self.__userConfirmCallback = callback
end
function ServeSession:setUpdateLoadingTextCallback(callback)
self.__updateLoadingText = callback
end
function ServeSession:setLoadingText(text: string)
self.__updateLoadingText(text)
end
--[=[
Hooks a function to run before patch application.
The provided function is called with the incoming patch and an InstanceMap
@ -181,11 +190,14 @@ end
function ServeSession:start()
self:__setStatus(Status.Connecting)
self:setLoadingText("Connecting to server...")
self.__apiContext
:connect()
:andThen(function(serverInfo)
self:setLoadingText("Loading initial data from server...")
return self:__initialSync(serverInfo):andThen(function()
self:setLoadingText("Starting sync loop...")
self:__setStatus(Status.Connected, serverInfo.projectName)
self:__applyGameAndPlaceId(serverInfo)
@ -449,11 +461,13 @@ function ServeSession:__initialSync(serverInfo)
-- For any instances that line up with the Rojo server's view, start
-- tracking them in the reconciler.
Log.trace("Matching existing Roblox instances to Rojo IDs")
self:setLoadingText("Hydrating instance map...")
self.__reconciler:hydrate(readResponseBody.instances, serverInfo.rootInstanceId, game)
-- Calculate the initial patch to apply to the DataModel to catch us
-- up to what Rojo thinks the place should look like.
Log.trace("Computing changes that plugin needs to make to catch up to server...")
self:setLoadingText("Finding differences between server and Studio...")
local success, catchUpPatch =
self.__reconciler:diff(readResponseBody.instances, serverInfo.rootInstanceId, game)