private static Task <Response> HandleDebugPlay(DebugPlayRequest request) { DispatchToMainThread(() => { GodotSharpEditor.Instance.CurrentPlaySettings = new PlaySettings(request.DebuggerHost, request.DebuggerPort, request.BuildBeforePlaying ?? true); Internal.EditorRunPlay(); GodotSharpEditor.Instance.CurrentPlaySettings = null; }); return(Task.FromResult <Response>(new DebugPlayResponse())); }
private static Task <Response> HandleDebugPlay(DebugPlayRequest request) { DispatchToMainThread(() => { // Tell the build callback whether the editor already built the solution or not GodotSharpEditor.Instance.SkipBuildBeforePlaying = !(request.BuildBeforePlaying ?? true); // Pass the debugger agent settings to the player via an environment variables // TODO: It would be better if this was an argument in EditorRunPlay instead Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", "--debugger-agent=transport=dt_socket" + $",address={request.DebuggerHost}:{request.DebuggerPort}" + ",server=n"); // Run the game Internal.EditorRunPlay(); // Restore normal settings Environment.SetEnvironmentVariable("GODOT_MONO_DEBUGGER_AGENT", ""); GodotSharpEditor.Instance.SkipBuildBeforePlaying = false; }); return(Task.FromResult <Response>(new DebugPlayResponse())); }
protected override void OnRun(DebuggerStartInfo startInfo) { var godotStartInfo = (GodotStartInfo)startInfo; var executionType = GodotDebugTargetSelection.Instance.CurrentDebugTarget.ExecutionType; switch (executionType) { case ExecutionType.PlayInEditor: { _attached = false; StartListening(godotStartInfo, out var assignedDebugPort); var godotMessagingClient = GodotPackage.Instance.GodotSolutionEventsListener?.GodotMessagingClient; if (godotMessagingClient == null || !godotMessagingClient.IsConnected) { EndSessionWithError("Play Error", "No Godot editor instance connected"); return; } const string host = "127.0.0.1"; var playRequest = new DebugPlayRequest { DebuggerHost = host, DebuggerPort = assignedDebugPort, BuildBeforePlaying = false }; _ = godotMessagingClient.SendRequest <DebugPlayResponse>(playRequest) .ContinueWith(t => { if (t.Result.Status != MessageStatus.Ok) { EndSessionWithError("Play Error", $"Received Play response with status: {MessageStatus.Ok}"); } }, TaskScheduler.Default); // TODO: Read the editor player stdout and stderr somehow break; } case ExecutionType.Launch: { _attached = false; StartListening(godotStartInfo, out var assignedDebugPort); // Listener to replace the Godot editor remote debugger. // We use it to notify the game when assemblies should be reloaded. var remoteDebugListener = new TcpListener(IPAddress.Any, 0); remoteDebugListener.Start(); _ = remoteDebugListener.AcceptTcpClientAsync() .ContinueWith(OnGodotRemoteDebuggerConnectedAsync, TaskScheduler.Default); string workingDir = startInfo.WorkingDirectory; const string host = "127.0.0.1"; int remoteDebugPort = ((IPEndPoint)remoteDebugListener.LocalEndpoint).Port; // Launch Godot to run the game and connect to our remote debugger var processStartInfo = new ProcessStartInfo(GetGodotExecutablePath()) { Arguments = $"--path {workingDir} --remote-debug {host}:{remoteDebugPort}", // TODO: Doesn't work with 4.0dev. Should be tcp://host:port which doesn't work in 3.2... WorkingDirectory = workingDir, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; // Tells Godot to connect to the mono debugger we just started processStartInfo.EnvironmentVariables["GODOT_MONO_DEBUGGER_AGENT"] = "--debugger-agent=transport=dt_socket" + $",address={host}:{assignedDebugPort}" + ",server=n"; _process = new Process { StartInfo = processStartInfo }; _process.OutputDataReceived += (sendingProcess, outLine) => OutputData(outLine.Data, false); _process.ErrorDataReceived += (sendingProcess, outLine) => OutputData(outLine.Data, true); if (!_process.Start()) { EndSessionWithError("Launch Error", "Failed to start Godot process"); return; } _process.BeginOutputReadLine(); if (_process.HasExited) { EndSessionWithError("Launch Error", $"Godot process exited with code: {_process.ExitCode}"); return; } _process.Exited += (sender, args) => EndSession(); OnDebuggerOutput(false, $"Godot PID:{_process.Id}{Environment.NewLine}"); break; } case ExecutionType.Attach: { _attached = true; StartConnecting(godotStartInfo); break; } default: throw new ArgumentOutOfRangeException(executionType.ToString()); } if (!_attached) { var options = (GeneralOptionsPage)GodotPackage.Instance.GetDialogPage(typeof(GeneralOptionsPage)); // If a connection is never established and we try to stop debugging, Visual Studio will freeze // for a long time for some reason. I have no idea why this happens. There may be something // we're doing wrong. For now we'll limit the time we wait for incoming connections. _ = Task.Delay(options.DebuggerListenTimeout).ContinueWith(r => { if (!HasExited && !IsConnected) { EndSession(); if (_process != null && !_process.HasExited) { _process.Kill(); } } }, TaskScheduler.Default); } }
protected override void OnRun(DebuggerStartInfo startInfo) { var godotStartInfo = (GodotDebuggerStartInfo)startInfo; _godotCmd = godotStartInfo.GodotCmd; switch (_godotCmd.ExecutionType) { case ExecutionType.PlayInEditor: { _attached = false; StartListening(godotStartInfo, out var assignedDebugPort); var godotMessagingClient = _godotCmd.GodotIdeClient; if (!godotMessagingClient.IsConnected) { EndSessionWithError("No Godot editor instance connected"); return; } string host = "127.0.0.1"; var playRequest = new DebugPlayRequest { DebuggerHost = host, DebuggerPort = assignedDebugPort, BuildBeforePlaying = false }; _ = godotMessagingClient.SendRequest <DebugPlayResponse>(playRequest) .ContinueWith(t => { if (t.Result.Status != GodotTools.IdeMessaging.MessageStatus.Ok) { EndSessionWithError($"Received Play response with status: {MessageStatus.Ok}"); } }); // TODO: Read the editor player stdout and stderr somehow break; } case ExecutionType.Launch: { _attached = false; StartListening(godotStartInfo, out var assignedDebugPort); // Listener to replace the Godot editor remote debugger. // We use it to notify the game when assemblies should be reloaded. var remoteDebugListener = new TcpListener(IPAddress.Any, 0); remoteDebugListener.Start(); _ = remoteDebugListener.AcceptTcpClientAsync().ContinueWith(OnGodotRemoteDebuggerConnected); string workingDir = startInfo.WorkingDirectory; string host = "127.0.0.1"; int remoteDebugPort = ((IPEndPoint)remoteDebugListener.LocalEndpoint).Port; // Launch Godot to run the game and connect to our remote debugger var processStartInfo = new ProcessStartInfo(GetGodotExecutablePath()) { Arguments = $"--path {workingDir} --remote-debug {host}:{remoteDebugPort}", WorkingDirectory = workingDir, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, CreateNoWindow = true }; // Tells Godot to connect to the mono debugger we just started processStartInfo.EnvironmentVariables["GODOT_MONO_DEBUGGER_AGENT"] = "--debugger-agent=transport=dt_socket" + $",address={host}:{assignedDebugPort}" + ",server=n"; _process = new Process { StartInfo = processStartInfo }; try { if (!_process.Start()) { EndSessionWithError("Failed to start Godot process"); return; } } catch (System.ComponentModel.Win32Exception e) { EndSessionWithError($"Failed to start Godot process: {e.Message}"); return; } if (_process.HasExited) { EndSessionWithError($"Godot process exited with code: {_process.ExitCode}"); return; } // Listen for StdOut and StdErr var stdOutThread = new Thread(OutputReader) { Name = "Godot StandardOutput Reader", IsBackground = true }; stdOutThread.Start(new ThreadStartArgs { IsStdErr = false, Stream = _process.StandardOutput }); var stdErrThread = new Thread(OutputReader) { Name = "Godot StandardError Reader", IsBackground = true }; stdErrThread.Start(new ThreadStartArgs { IsStdErr = true, Stream = _process.StandardError }); _process.Exited += (sender, args) => EndSession(); OnDebuggerOutput(false, $"Godot PID:{_process.Id}{Environment.NewLine}"); break; } case ExecutionType.Attach: { _attached = true; StartConnecting(godotStartInfo); break; } default: throw new NotImplementedException(_godotCmd.ExecutionType.ToString()); } }