Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions cli/dispatcher/internal/dispatcher/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ import (
)

const (
launchLockfilePoll = 100 * time.Millisecond
launchLockfileTimeout = 5 * time.Second
launchProcessExitPoll = 100 * time.Millisecond
launchProcessExitTimeout = 20 * time.Second
launchReadinessTimeout = 10 * time.Minute
projectVersionFilePath = "ProjectSettings/ProjectVersion.txt"
recoveryDirectoryPath = "Assets/_Recovery"
launchTempDirectoryName = "Temp"
unityLockfileName = "UnityLockfile"
launchLockfilePoll = 100 * time.Millisecond
launchLockfileTimeout = 5 * time.Second
launchProcessExitPoll = 100 * time.Millisecond
launchProcessExitTimeout = 20 * time.Second
launchReadinessTimeout = 10 * time.Minute
launchV2ServerReadyPoll = 500 * time.Millisecond
launchV2ServerDialTimeout = 1 * time.Second
projectVersionFilePath = "ProjectSettings/ProjectVersion.txt"
recoveryDirectoryPath = "Assets/_Recovery"
launchTempDirectoryName = "Temp"
unityLockfileName = "UnityLockfile"
)

var editorVersionPattern = regexp.MustCompile(`(?m)^m_EditorVersion:\s*(.+)$`)
Expand Down Expand Up @@ -106,6 +108,11 @@ func runLaunchWithDeps(ctx context.Context, options launchOptions, startPath str
return 1
}
v2Project, _ := detectV2DispatcherProject(projectRoot)
// Capture before kill (-r) or start so session-id generation compare sees the prior generation.
previousV2ServerSessionID := ""
if v2Project.IsV2 {
previousV2ServerSessionID = readPreviousV2ServerSessionID(projectRoot)
}

runningProcess, handled, code := findLaunchRunningProcess(ctx, options, projectRoot, stdout, stderr, deps)
if handled {
Expand All @@ -122,7 +129,7 @@ func runLaunchWithDeps(ctx context.Context, options launchOptions, startPath str
return writeLaunchQuitResponse(stdout, stderr, projectRoot, nil, launchNoProcessMessage)
}

return startUnityAndWaitForReadiness(ctx, options, projectRoot, runningProcess, v2Project.IsV2, stdout, stderr, deps)
return startUnityAndWaitForReadiness(ctx, options, projectRoot, runningProcess, v2Project.IsV2, previousV2ServerSessionID, stdout, stderr, deps)
}

func writeLaunchProjectSearch(stdout io.Writer, options launchOptions, startPath string) {
Expand Down Expand Up @@ -185,7 +192,7 @@ func handleExistingLaunchProcess(
return true, 1
}
if isV2 {
return true, writeExistingV2LaunchOpenedResponse(stdout, stderr, projectRoot, runningProcess.Pid)
return true, waitForExistingV2LaunchReadiness(ctx, projectRoot, runningProcess.Pid, stdout, stderr, deps)
}
return true, waitForExistingLaunchReadiness(ctx, projectRoot, runningProcess.Pid, stdout, stderr, deps)
}
Expand Down Expand Up @@ -234,6 +241,7 @@ func startUnityAndWaitForReadiness(
projectRoot string,
runningProcess *unityprocess.UnityProcess,
isV2 bool,
previousV2ServerSessionID string,
stdout io.Writer,
stderr io.Writer,
deps launchDeps,
Expand Down Expand Up @@ -279,7 +287,7 @@ func startUnityAndWaitForReadiness(
return 1
}
if isV2 {
return waitForV2ProjectOpened(ctx, projectRoot, runningProcess, currentPid, stdout, stderr, launchStartedAt, deps)
return waitForV2ProjectOpened(ctx, projectRoot, runningProcess, currentPid, stdout, stderr, launchStartedAt, previousV2ServerSessionID, spinner, deps)
}
if err := deps.waitForUnityStartupMarker(ctx, unityLockfilePath(projectRoot), launchLockfilePoll, launchLockfileTimeout); err != nil {
clierrors.WriteClassifiedError(stderr, err, clierrors.ErrorContext{ProjectRoot: projectRoot, Command: clicore.LaunchCommandName})
Expand Down
8 changes: 6 additions & 2 deletions cli/dispatcher/internal/dispatcher/launch_deps.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type launchDeps struct {
waitForUnityProcessExit func(context.Context, string, int, time.Duration, time.Duration) error
waitForUnityStartupMarker func(context.Context, string, time.Duration, time.Duration) error
waitForFreshUnityLockfile func(context.Context, string, time.Time, time.Duration, time.Duration) error
waitForV2ServerReady func(context.Context, string, string, time.Duration, time.Duration) error
waitForToolReadiness func(context.Context, string, time.Duration) error
probeProjectIpcFallback func(context.Context, string) error
}
Expand All @@ -31,7 +32,10 @@ func defaultLaunchDeps() launchDeps {
waitForUnityProcessExit: waitForUnityProcessExit,
waitForUnityStartupMarker: waitForUnityStartupMarkerOrTimeout,
waitForFreshUnityLockfile: waitForFreshUnityLockfile,
waitForToolReadiness: clicore.WaitForToolReadinessWithTimeout,
probeProjectIpcFallback: clicore.ProbeToolReadinessSequence,
waitForV2ServerReady: func(ctx context.Context, projectRoot string, previousServerSessionID string, poll time.Duration, timeout time.Duration) error {
return waitForV2ServerReady(ctx, projectRoot, previousServerSessionID, defaultV2ServerDial, poll, timeout)
},
waitForToolReadiness: clicore.WaitForToolReadinessWithTimeout,
probeProjectIpcFallback: clicore.ProbeToolReadinessSequence,
}
}
52 changes: 52 additions & 0 deletions cli/dispatcher/internal/dispatcher/launch_focus_log.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,55 @@ func logLaunchExistingFocusFailure(projectRoot string, pid int, focusErr error,
CorrelationID: correlationID,
})
}

// logLaunchV2FocusWithDeps focuses a freshly spawned V2 Editor so delayCall can tick.
// Failure is non-fatal: the readiness probe continues either way.
func logLaunchV2FocusWithDeps(ctx context.Context, projectRoot string, pid int, deps launchDeps) {
correlationID := vibelog.NewCLIVibeCorrelationID()
logLaunchV2FocusAttempt(projectRoot, pid, correlationID)
if err := deps.focusUnityProcess(ctx, pid); err != nil {
logLaunchV2FocusFailure(projectRoot, pid, err, correlationID)
return
}
logLaunchV2FocusSuccess(projectRoot, pid, correlationID)
}

func logLaunchV2FocusAttempt(projectRoot string, pid int, correlationID string) {
_ = vibelog.WriteCLIVibeLog(projectRoot, vibelog.CLIVibeLogEntry{
Level: "INFO",
Operation: "cli_launch_v2_focus_attempt",
Message: "Attempting to focus the newly launched V2 Unity process so server startup can tick.",
Context: map[string]any{
"command": "launch",
"pid": pid,
},
CorrelationID: correlationID,
})
}

func logLaunchV2FocusSuccess(projectRoot string, pid int, correlationID string) {
_ = vibelog.WriteCLIVibeLog(projectRoot, vibelog.CLIVibeLogEntry{
Level: "INFO",
Operation: "cli_launch_v2_focus_success",
Message: "Focused the newly launched V2 Unity process.",
Context: map[string]any{
"command": "launch",
"pid": pid,
},
CorrelationID: correlationID,
})
}

func logLaunchV2FocusFailure(projectRoot string, pid int, focusErr error, correlationID string) {
_ = vibelog.WriteCLIVibeLog(projectRoot, vibelog.CLIVibeLogEntry{
Level: "WARNING",
Operation: "cli_launch_v2_focus_failed",
Message: "Failed to focus the newly launched V2 Unity process; continuing readiness probe.",
Context: map[string]any{
"command": "launch",
"pid": pid,
"focusError": clicore.ErrorMessage(focusErr),
},
CorrelationID: correlationID,
})
}
12 changes: 8 additions & 4 deletions cli/dispatcher/internal/dispatcher/launch_ready.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,16 @@ func writeExistingLaunchReadyResponse(stdout io.Writer, stderr io.Writer, projec
})
}

func writeExistingV2LaunchOpenedResponse(stdout io.Writer, stderr io.Writer, projectRoot string, currentPid int) int {
func writeExistingV2LaunchReadyResponse(stdout io.Writer, stderr io.Writer, projectRoot string, currentPid int) int {
return writeLaunchResponse(stdout, stderr, launchReadyResponse{
Success: true,
Ready: true,
ServerReady: true,
ProjectIpcReady: false,
AlreadyRunning: true,
CurrentProcessId: &currentPid,
ProjectRoot: projectRoot,
Message: "Unity is already running for this V2 project. V2 server readiness was not checked.",
Message: "Unity is already running and the V2 uloop server is ready. This project uses uloop V2; commands run through the V2 CLI.",
})
}

Expand All @@ -102,7 +104,7 @@ func writeLaunchedReadyResponse(
})
}

func writeLaunchedV2ProjectOpenedResponse(
func writeLaunchedV2ReadyResponse(
stdout io.Writer,
stderr io.Writer,
projectRoot string,
Expand All @@ -112,12 +114,14 @@ func writeLaunchedV2ProjectOpenedResponse(
return writeLaunchResponse(stdout, stderr, launchReadyResponse{
Success: true,
Ready: true,
ServerReady: true,
ProjectIpcReady: false,
Launched: true,
Restarted: previousPid != nil,
PreviousProcessId: previousPid,
CurrentProcessId: &currentPid,
ProjectRoot: projectRoot,
Message: "Unity started and opened the V2 project. V2 server readiness was not checked.",
Message: "Unity started and the V2 uloop server is ready. This project uses uloop V2; commands run through the V2 CLI.",
})
}

Expand Down
Loading
Loading