diff --git a/.vscode/launch.json b/.vscode/launch.json index b74e0acda049..a35df3d79d7c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,6 +15,14 @@ "preLaunchTask": "Build All", "dmb": "${workspaceFolder}/${command:CurrentDMB}", "dreamDaemon": true + }, + { + "type": "byond", + "request": "launch", + "name": "Launch Both", + "preLaunchTask": "Build and Launch Server", + "dmb": "${workspaceFolder}/${command:CurrentDMB}", + "dreamDaemon": true } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index b7d646518bdb..7edef5a57a87 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -100,6 +100,29 @@ }, "group": "build", "label": "tgui: clean", + }, + { + "type": "shell", + "label": "Background: Wait for DreamDaemon", + "command": "start", + "args": ["\"Waiting for DreamDaemon\"", "powershell", "-ExecutionPolicy", "Bypass", "-File", "${workspaceFolder}\\bin\\connect-to-local-wait.ps1"], + "options": { + "shell": { + "executable": "cmd.exe", // it needs to detach so vscode can actually launch dreamdaemon before we start waiting for it + "args": ["/c"] + } + }, + "presentation": { + "reveal": "silent" + }, + "problemMatcher": [] + }, + { + "type": "shell", + "label": "Build and Launch Server", + "dependsOn": ["Build All", "Background: Wait for DreamDaemon"], + "dependsOrder": "parallel", + "problemMatcher": [] } ] } diff --git a/bin/connect-to-local-wait.ps1 b/bin/connect-to-local-wait.ps1 new file mode 100644 index 000000000000..4bfee1b09a0e --- /dev/null +++ b/bin/connect-to-local-wait.ps1 @@ -0,0 +1,83 @@ +# Ping the local DreamDaemon server until it responds +# Autodetects port by finding what dreamdaemon.exe is listening on + +function Find-DreamDaemonPort { + # check running processes, this is slow as shit because it looks at everything so erroraction should be faster + $proc = Get-Process -Name 'dd','dreamdaemon' -ErrorAction SilentlyContinue | Select-Object -First 1 + if (-not $proc) { + return $null + } + + # found the process, get the port + $conn = Get-NetTCPConnection -OwningProcess $proc.Id -State Listen -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($conn) { + return $conn.LocalPort + } + return $null +} + +function Test-Server { + param([string]$Server = "localhost", [int]$Port) + try { + $client = New-Object System.Net.Sockets.TcpClient + $asyncResult = $client.BeginConnect($Server, $Port, $null, $null) + if (-not $asyncResult.AsyncWaitHandle.WaitOne(1000)) { + $client.Close() + return $false + } + $client.EndConnect($asyncResult) + $stream = $client.GetStream() + + $query = "?ping" + $queryBytes = [System.Text.Encoding]::ASCII.GetBytes($query) + $length = $queryBytes.Length + 6 + + # Byond topic packet construction + $packet = [byte[]]@( + 0x00, # Header byte + 0x83, # Header byte + [byte](($length -shr 8) -band 0xFF), # Message body size in big endian + [byte]($length -band 0xFF), # and lower byte, its an int16 + 0x00, 0x00, 0x00, 0x00, 0x00 # flag, port (unused here so 0) + ) + $queryBytes + [byte]0x00 # mesasge + null terminator + + $stream.Write($packet, 0, $packet.Length) + $stream.Flush() + $stream.ReadTimeout = 1000 + + $buffer = New-Object byte[] 1024 + $read = $stream.Read($buffer, 0, 1024) + + $client.Close() + return $read -gt 0 + } catch { + return $false + } +} + +$detectedPort = $null + +while ($true) { + # get whatever port dreamdaemon is on + $port = Find-DreamDaemonPort + if ($port) { + Write-Host "Found DreamDaemon on port $port" + # ping the server to make sure we can actually connect yet + if (Test-Server -Port $port) { + $detectedPort = $port + break + } + Write-Host "Server is still starting and not responding yet, retrying" + } else { + Write-Host "DreamDaemon has not yet launched, retrying" + } + Start-Sleep -Seconds 2 +} + +if ($detectedPort) { + $dsPath = "C:\Program Files (x86)\BYOND\bin\dreamseeker.exe" + if (-not (Test-Path $dsPath)) { + $dsPath = "C:\Program Files\BYOND\bin\dreamseeker.exe" + } + Start-Process $dsPath -ArgumentList "byond://localhost:$detectedPort" +}