Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/rojo-rbx/rojo into headle…
Browse files Browse the repository at this point in the history
…ss-api
  • Loading branch information
boatbomber committed Feb 1, 2024
2 parents 3a28b03 + f3b0b00 commit 9397dba
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 44 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Added headless API for Studio companion plugins. ([#631])
* Added popout diff visualizer for table properties like Attributes and Tags ([#834])
* Updated Theme to use Studio colors ([#838])
* Added experimental setting for Auto Connect in playtests ([#840])
* Projects may now specify rules for syncing files as if they had a different file extension. ([#813])
This is specified via a new field on project files, `syncRules`:

Expand Down Expand Up @@ -55,6 +56,7 @@
[#813]: https://github.com/rojo-rbx/rojo/pull/813
[#834]: https://github.com/rojo-rbx/rojo/pull/834
[#838]: https://github.com/rojo-rbx/rojo/pull/838
[#840]: https://github.com/rojo-rbx/rojo/pull/840

## [7.4.0] - January 16, 2024
* Improved the visualization for array properties like Tags ([#829])
Expand Down
35 changes: 25 additions & 10 deletions plugin/src/App/StatusPages/Settings/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,12 @@ function SettingsPage:init()
end

function SettingsPage:render()
local layoutOrder = 0
local function layoutIncrement()
layoutOrder += 1
return layoutOrder
end

return Theme.with(function(theme)
theme = theme.Settings

Expand All @@ -87,15 +93,15 @@ function SettingsPage:render()
Navbar = e(Navbar, {
onBack = self.props.onBack,
transparency = self.props.transparency,
layoutOrder = 0,
layoutOrder = layoutIncrement(),
}),

ShowNotifications = e(Setting, {
id = "showNotifications",
name = "Show Notifications",
description = "Popup notifications in viewport",
transparency = self.props.transparency,
layoutOrder = 1,
layoutOrder = layoutIncrement(),
}),

SyncReminder = e(Setting, {
Expand All @@ -104,15 +110,15 @@ function SettingsPage:render()
description = "Notify to sync when opening a place that has previously been synced",
transparency = self.props.transparency,
visible = Settings:getBinding("showNotifications"),
layoutOrder = 2,
layoutOrder = layoutIncrement(),
}),

ConfirmationBehavior = e(Setting, {
id = "confirmationBehavior",
name = "Confirmation Behavior",
description = "When to prompt for confirmation before syncing",
transparency = self.props.transparency,
layoutOrder = 3,
layoutOrder = layoutIncrement(),

options = confirmationBehaviors,
}),
Expand All @@ -122,7 +128,7 @@ function SettingsPage:render()
name = "Confirmation Threshold",
description = "How many modified instances to be considered a large change",
transparency = self.props.transparency,
layoutOrder = 4,
layoutOrder = layoutIncrement(),
visible = Settings:getBinding("confirmationBehavior"):map(function(value)
return value == "Large Changes"
end),
Expand Down Expand Up @@ -153,7 +159,16 @@ function SettingsPage:render()
name = "Play Sounds",
description = "Toggle sound effects",
transparency = self.props.transparency,
layoutOrder = 5,
layoutOrder = layoutIncrement(),
}),

AutoConnectPlaytestServer = e(Setting, {
id = "autoConnectPlaytestServer",
name = "Auto Connect Playtest Server",
description = "Automatically connect game server to Rojo when playtesting while connected in Edit",
experimental = true,
transparency = self.props.transparency,
layoutOrder = layoutIncrement(),
}),

Permissions = e(Setting, {
Expand Down Expand Up @@ -203,7 +218,7 @@ function SettingsPage:render()
locked = self.props.syncActive,
experimental = true,
transparency = self.props.transparency,
layoutOrder = 7,
layoutOrder = layoutIncrement(),
}),

TwoWaySync = e(Setting, {
Expand All @@ -213,15 +228,15 @@ function SettingsPage:render()
locked = self.props.syncActive,
experimental = true,
transparency = self.props.transparency,
layoutOrder = 8,
layoutOrder = layoutIncrement(),
}),

LogLevel = e(Setting, {
id = "logLevel",
name = "Log Level",
description = "Plugin output verbosity level",
transparency = self.props.transparency,
layoutOrder = 100,
layoutOrder = layoutIncrement(),

options = invertedLevels,
showReset = Settings:getBinding("logLevel"):map(function(value)
Expand All @@ -237,7 +252,7 @@ function SettingsPage:render()
name = "Typechecking",
description = "Toggle typechecking on the API surface",
transparency = self.props.transparency,
layoutOrder = 101,
layoutOrder = layoutIncrement(),
}),

Layout = e("UIListLayout", {
Expand Down
76 changes: 71 additions & 5 deletions plugin/src/App/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -214,11 +214,29 @@ function App:init()
},
})
end

if self:isAutoConnectPlaytestServerAvailable() then
self:useRunningConnectionInfo()
self:startSession()
end
self.autoConnectPlaytestServerListener = Settings:onChanged("autoConnectPlaytestServer", function(enabled)
if enabled then
if self:isAutoConnectPlaytestServerWriteable() and self.serveSession ~= nil then
-- Write the existing session
local baseUrl = self.serveSession.__apiContext.__baseUrl
self:setRunningConnectionInfo(baseUrl)
end
else
self:clearRunningConnectionInfo()
end
end)
end

function App:willUnmount()
self.waypointConnection:Disconnect()
self.confirmationBindable:Destroy()
self.autoConnectPlaytestServerListener()
self:clearRunningConnectionInfo()
end

function App:addNotification(
Expand Down Expand Up @@ -368,10 +386,7 @@ function App:getHostAndPort(): (string, string)
local host = self.host:getValue()
local port = self.port:getValue()

local host = if #host > 0 then host else Config.defaultHost
local port = if #port > 0 then port else Config.defaultPort

return host, port
return if #host > 0 then host else Config.defaultHost, if #port > 0 then port else Config.defaultPort
end

function App:isSyncLockAvailable()
Expand Down Expand Up @@ -493,6 +508,49 @@ function App:requestPermission(
return response
end

function App:isAutoConnectPlaytestServerAvailable()
return RunService:IsRunMode()
and RunService:IsServer()
and Settings:get("autoConnectPlaytestServer")
and workspace:GetAttribute("__Rojo_ConnectionUrl")
end

function App:isAutoConnectPlaytestServerWriteable()
return RunService:IsEdit() and Settings:get("autoConnectPlaytestServer")
end

function App:setRunningConnectionInfo(baseUrl: string)
if not self:isAutoConnectPlaytestServerWriteable() then
return
end

Log.trace("Setting connection info for play solo auto-connect")
workspace:SetAttribute("__Rojo_ConnectionUrl", baseUrl)
end

function App:clearRunningConnectionInfo()
if not RunService:IsEdit() then
-- Only write connection info from edit mode
return
end

Log.trace("Clearing connection info for play solo auto-connect")
workspace:SetAttribute("__Rojo_ConnectionUrl", nil)
end

function App:useRunningConnectionInfo()
local connectionInfo = workspace:GetAttribute("__Rojo_ConnectionUrl")
if not connectionInfo then
return
end

Log.trace("Using connection info for play solo auto-connect")
local host, port = string.match(connectionInfo, "^(.+):(.-)$")

self.setHost(host)
self.setPort(port)
end

function App:startSession(host: string?, port: string?)
local claimedLock, priorOwner = self:claimSyncLock()
if not claimedLock then
Expand Down Expand Up @@ -592,6 +650,7 @@ function App:startSession(host: string?, port: string?)
self.headlessAPI:_updateProperty("ProjectName", details)

self.knownProjects[details] = true
self:setRunningConnectionInfo(baseUrl)

self:setState({
appStatus = AppStatus.Connected,
Expand All @@ -603,6 +662,7 @@ function App:startSession(host: string?, port: string?)
elseif status == ServeSession.Status.Disconnected then
self.serveSession = nil
self:releaseSyncLock()
self:clearRunningConnectionInfo()
self:setState({
patchData = {
patch = PatchSet.newEmpty(),
Expand Down Expand Up @@ -644,6 +704,12 @@ function App:startSession(host: string?, port: string?)
return "Accept"
end

-- Play solo auto-connect does not require confirmation
if self:isAutoConnectPlaytestServerAvailable() then
Log.trace("Accepting patch without confirmation because play solo auto-connect is enabled")
return "Accept"
end

local confirmationBehavior = Settings:get("confirmationBehavior")
if confirmationBehavior == "Initial" then
-- Only confirm if we haven't synced this project yet this session
Expand Down Expand Up @@ -779,7 +845,7 @@ function App:render()
value = self.props.plugin,
}, {
e(Theme.StudioProvider, nil, {
e(Tooltip.Provider, nil, {
tooltip = e(Tooltip.Provider, nil, {
popups = Roact.createFragment(popups),

gui = e(StudioPluginGui, {
Expand Down
22 changes: 12 additions & 10 deletions plugin/src/InstanceMap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,27 +113,29 @@ end
function InstanceMap:destroyInstance(instance)
local id = self.fromInstances[instance]

local descendants = instance:GetDescendants()
instance:Destroy()

-- After the instance is successfully destroyed,
-- we can remove all the id mappings

if id ~= nil then
self:removeId(id)
end

for _, descendantInstance in ipairs(instance:GetDescendants()) do
for _, descendantInstance in descendants do
self:removeInstance(descendantInstance)
end

instance:Destroy()
end

function InstanceMap:destroyId(id)
local instance = self.fromIds[id]
self:removeId(id)

if instance ~= nil then
for _, descendantInstance in ipairs(instance:GetDescendants()) do
self:removeInstance(descendantInstance)
end

instance:Destroy()
self:destroyInstance(instance)
else
-- There is no instance with this id, so we can just remove the id
-- without worrying about instance destruction
self:removeId(id)
end
end

Expand Down
77 changes: 63 additions & 14 deletions plugin/src/PatchTree.lua
Original file line number Diff line number Diff line change
Expand Up @@ -426,22 +426,71 @@ function PatchTree.updateMetadata(tree, patch, instanceMap, unappliedPatch)
-- Update isWarning metadata
for _, failedChange in unappliedPatch.updated do
local node = tree:getNode(failedChange.id)
if node then
node.isWarning = true
Log.trace("Marked node as warning: {} {}", node.id, node.name)

if node.changeList then
for _, change in node.changeList do
if failedChange.changedProperties[change[1]] then
Log.trace(" Marked property as warning: {}", change[1])
if change[4] == nil then
change[4] = {}
end
change[4].isWarning = true
end
end
if not node then
continue
end

node.isWarning = true
Log.trace("Marked node as warning: {} {}", node.id, node.name)

if not node.changeList then
continue
end
for _, change in node.changeList do
local property = change[1]
local propertyFailedToApply = if property == "Name"
then failedChange.changedName ~= nil -- Name is not in changedProperties, so it needs a special case
else failedChange.changedProperties[property] ~= nil

if not propertyFailedToApply then
-- This change didn't fail, no need to mark
continue
end
if change[4] == nil then
change[4] = { isWarning = true }
else
change[4].isWarning = true
end
Log.trace(" Marked property as warning: {}.{}", node.name, property)
end
end
for failedAdditionId in unappliedPatch.added do
local node = tree:getNode(failedAdditionId)
if not node then
continue
end

node.isWarning = true
Log.trace("Marked node as warning: {} {}", node.id, node.name)

if not node.changeList then
continue
end
for _, change in node.changeList do
-- Failed addition means that all properties failed to be added
if change[4] == nil then
change[4] = { isWarning = true }
else
change[4].isWarning = true
end
Log.trace(" Marked property as warning: {}.{}", node.name, change[1])
end
end
for _, failedRemovalIdOrInstance in unappliedPatch.removed do
local failedRemovalId = if Types.RbxId(failedRemovalIdOrInstance)
then failedRemovalIdOrInstance
else instanceMap.fromInstances[failedRemovalIdOrInstance]
if not failedRemovalId then
continue
end

local node = tree:getNode(failedRemovalId)
if not node then
continue
end

node.isWarning = true
Log.trace("Marked node as warning: {} {}", node.id, node.name)
end

-- Update if instances exist
Expand Down
Loading

0 comments on commit 9397dba

Please sign in to comment.