Merge: net48-only launcher (pod-validated)

Launcher Service + Agent moved from net8/x64 self-contained to net48
framework-dependent; Tesla.Contract is now net48-only (single TFM across the
suite). Package drops from ~91 MB zipped to ~1.6 MB. Tested OK end-to-end on a
pod computer.
This commit is contained in:
Cyd
2026-06-30 12:59:50 -05:00
9 changed files with 79 additions and 73 deletions
+3 -2
View File
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Multi-targeted: net48 for the Console, net8.0-windows for the Launcher Service. -->
<TargetFrameworks>net48;net8.0-windows</TargetFrameworks>
<!-- net48 only: both consumers (Console and Launcher) target .NET Framework 4.8.
(Was multi-targeted net48;net8.0-windows while the Launcher was on net8.) -->
<TargetFramework>net48</TargetFramework>
<!-- CRITICAL: the output assembly MUST be named TeslaConsoleLaunchLib at
version 1.0.0.0. BinaryFormatter embeds the assembly name in the wire
+4 -3
View File
@@ -21,6 +21,7 @@ using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Text;
using System.Security.Cryptography;
using System.Linq;
using System.Threading;
using System.Diagnostics;
using Microsoft.Win32;
@@ -813,7 +814,7 @@ namespace TeslaSecureConfig
TcpReadExact(netStream, consoleIv);
var podIv = new byte[16];
RandomNumberGenerator.Fill(podIv);
using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(podIv);
netStream.Write(podIv, 0, 16);
netStream.Flush();
@@ -836,7 +837,7 @@ namespace TeslaSecureConfig
var confReply = new byte[4];
TcpReadExact(ofb, confReply);
if (!confReply.AsSpan().SequenceEqual(confMsg))
if (!confReply.SequenceEqual(confMsg))
{
Log($"CONF mismatch: got [{BitConverter.ToString(confReply)}], expected CONF — key/IV mismatch.");
return;
@@ -1188,7 +1189,7 @@ namespace TeslaSecureConfig
private static string GenerateRandomString(int length)
{
var buf = new byte[length];
RandomNumberGenerator.Fill(buf);
using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(buf);
var sb = new System.Text.StringBuilder(length);
for (int i = 0; i < length; i++)
sb.Append(Proto.Alphabet[buf[i] % Proto.Alphabet.Length]);
+3 -3
View File
@@ -422,8 +422,8 @@ namespace Tesla.Launcher.Agent
}
var resBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(response));
pipe.Write(BitConverter.GetBytes(resBytes.Length));
pipe.Write(resBytes);
pipe.Write(BitConverter.GetBytes(resBytes.Length), 0, 4);
pipe.Write(resBytes, 0, resBytes.Length);
pipe.Flush();
}
@@ -672,7 +672,7 @@ namespace Tesla.Launcher.Agent
var scalar = parms[0].ValueKind == JsonValueKind.Number
? (float)parms[0].GetDouble()
: throw new Exception("Invalid volume value");
SetMasterVolume(Math.Clamp(scalar, 0f, 1f));
SetMasterVolume(Math.Max(0f, Math.Min(1f, scalar)));
return null;
}
+5 -7
View File
@@ -1,20 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net48</TargetFramework>
<OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms>
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<WarningsAsErrors></WarningsAsErrors>
<Version>4.11.4.1</Version>
<ApplicationIcon>app.ico</ApplicationIcon>
<AssemblyName>TeslaLauncherAgent</AssemblyName>
<RootNamespace>Tesla.Launcher.Agent</RootNamespace>
<Platforms>x64</Platforms>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<StartupObject>Tesla.Launcher.Agent.AgentApplication</StartupObject>
</PropertyGroup>
@@ -25,8 +22,9 @@
</ItemGroup>
<ItemGroup>
<!-- Required by SecureConfig.cs for COM2/PlasmaIO serial output -->
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
<!-- Agent parses configuring.json + the IPC JSON; built-in on net6, a package on net48. -->
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
</Project>
+20 -11
View File
@@ -12,6 +12,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.IO.Compression;
using System.IO.Pipes;
@@ -151,11 +152,25 @@ namespace Tesla.Launcher.Service
_listener.Start();
_logger.LogInformation("Listening. Waiting for Console connections...");
// net48's TcpListener has no CancellationToken overload for accept;
// stop the listener on cancellation so the pending accept unblocks.
using var stopReg = stoppingToken.Register(() => { try { _listener.Stop(); } catch { } });
while (!stoppingToken.IsCancellationRequested)
{
TcpClient client;
try
{
var client = await _listener.AcceptTcpClientAsync(stoppingToken);
client = await _listener.AcceptTcpClientAsync();
}
catch (OperationCanceledException) { break; }
catch (Exception) when (stoppingToken.IsCancellationRequested) { break; }
catch (Exception ex)
{
_logger.LogError(ex, "Error accepting Console connection");
continue;
}
_logger.LogInformation(
"Console connected from {EP}", client.Client.RemoteEndPoint);
@@ -172,12 +187,6 @@ namespace Tesla.Launcher.Service
_clientTasks.RemoveAll(t => t.IsCompleted);
}
}
catch (OperationCanceledException) { break; }
catch (Exception ex)
{
_logger.LogError(ex, "Error accepting Console connection");
}
}
}
finally
{
@@ -209,7 +218,7 @@ namespace Tesla.Launcher.Service
// Generate and send our IV
var podIv = new byte[16];
RandomNumberGenerator.Fill(podIv);
using (var rng = RandomNumberGenerator.Create()) rng.GetBytes(podIv);
netStream.Write(podIv, 0, 16);
netStream.Flush();
@@ -230,7 +239,7 @@ namespace Tesla.Launcher.Service
_logger.LogWarning("CONF read failed: {Msg}", ex.Message);
return;
}
if (!confReply.AsSpan().SequenceEqual(confMsg))
if (!confReply.SequenceEqual(confMsg))
{
_logger.LogWarning("CONF mismatch — session key mismatch, dropping connection.");
return;
@@ -879,8 +888,8 @@ namespace Tesla.Launcher.Service
// Send IpcMessage
var reqBytes = Encoding.UTF8.GetBytes(JsonSerializer.Serialize(msg));
await pipe.WriteAsync(BitConverter.GetBytes(reqBytes.Length), ct);
await pipe.WriteAsync(reqBytes, ct);
await pipe.WriteAsync(BitConverter.GetBytes(reqBytes.Length), 0, 4, ct);
await pipe.WriteAsync(reqBytes, 0, reqBytes.Length, ct);
await pipe.FlushAsync(ct);
// Read IpcResponse
+17 -16
View File
@@ -1,20 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Worker">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<TargetFramework>net48</TargetFramework>
<OutputType>Exe</OutputType>
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<WarningsAsErrors></WarningsAsErrors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Version>4.11.4.1</Version>
<ApplicationIcon>app.ico</ApplicationIcon>
<AssemblyName>TeslaLauncherService</AssemblyName>
<RootNamespace>Tesla.Launcher.Service</RootNamespace>
<Platforms>x64</Platforms>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>true</PublishSingleFile>
<WindowsService>true</WindowsService>
</PropertyGroup>
<ItemGroup>
@@ -23,22 +20,26 @@
</ItemGroup>
<ItemGroup>
<!-- net48 reference assemblies + GAC refs the framework-dependent build needs. -->
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.ServiceProcess" />
</ItemGroup>
<ItemGroup>
<!-- Generic host on .NET Framework: Microsoft.Extensions.Hosting(.WindowsServices)
8.x ship a net462 target, so they run on net48 unchanged. -->
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.*" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.*" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="8.*" />
<!-- Required by SecureConfig.cs for COM2/PlasmaIO serial output -->
<PackageReference Include="System.Text.Json" Version="8.0.5" />
<!-- COM2 / PlasmaIO serial output in SecureConfig.cs -->
<PackageReference Include="System.IO.Ports" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<!-- Tesla.Net wire types now come from the shared contract (net6 build),
replacing the hand-maintained replica in LaunchModels_Shared.cs.
SetPlatform pins the reference to AnyCPU: this project is x86, the
contract is platform-neutral, and without the pin MSBuild's dynamic
platform negotiation corrupts this project's restore (one-shot
`dotnet build` of the solution then fails to resolve its packages). -->
<ProjectReference Include="..\Contract\Tesla.Contract.csproj" SetPlatform="Platform=AnyCPU" />
<ProjectReference Include="..\Contract\Tesla.Contract.csproj" />
</ItemGroup>
</Project>
+5 -11
View File
@@ -3,11 +3,12 @@
:: TeslaLauncher — Unified Build / Package Script
:: =============================================================================
:: Publishes TeslaLauncherService.exe and TeslaLauncherAgent.exe as
:: self-contained single-file win-x64 executables (net8) and assembles the
:: installable TeslaLauncher\ package next to install.bat.
:: framework-dependent net48 builds and assembles the installable TeslaLauncher\
:: package next to install.bat. The target pod needs .NET Framework 4.8 (built
:: into Windows 10/11) — there is no bundled runtime, so the package is small.
::
:: Requirements:
:: .NET 8 SDK https://dotnet.microsoft.com/download/dotnet/8.0
:: .NET SDK (6.0+) to drive the build https://dotnet.microsoft.com/download
:: Internet access for NuGet restore (first build only)
::
:: Usage:
@@ -31,7 +32,6 @@ setlocal enabledelayedexpansion
set ROOT=%~dp0
set BUILD_DIR=%ROOT%TeslaLauncher
set RID=win-x64
:: -- Parse arguments ----------------------------------------------------------
set BUILD_SERVICE=1
@@ -46,7 +46,7 @@ for %%a in (%*) do (
echo.
echo ============================================================
echo Tesla Launcher v0.1 - Build ^& Package (net8, %RID%)
echo Tesla Launcher v0.1 - Build ^& Package (net48, framework-dependent)
echo Output : %BUILD_DIR%
echo ============================================================
echo.
@@ -75,9 +75,6 @@ echo [1/2] Publishing TeslaLauncherService (Session 0 Windows Service)...
echo.
dotnet publish "%ROOT%TeslaLauncherService.csproj" ^
-c Release ^
-r %RID% ^
--self-contained true ^
-p:PublishSingleFile=true ^
-o "%BUILD_DIR%\Service"
if errorlevel 1 (
echo.
@@ -98,9 +95,6 @@ if %BUILD_SERVICE%==1 (echo [2/2] Publishing TeslaLauncherAgent...) else (echo [
echo.
dotnet publish "%ROOT%TeslaLauncherAgent.csproj" ^
-c Release ^
-r %RID% ^
--self-contained true ^
-p:PublishSingleFile=true ^
"-p:DefineConstants=WINFORMS" ^
-o "%BUILD_DIR%\Agent"
if errorlevel 1 (
+5 -3
View File
@@ -120,9 +120,11 @@ echo C:\Games (Users: modify access)
:: ── STEP 2: Copy files ────────────────────────────────────────────────────────
echo [2/8] Copying files...
copy /Y "%SERVICE_SRC%\TeslaLauncherService.exe" "%INSTALL_DIR%\" >nul
copy /Y "%AGENT_SRC%\TeslaLauncherAgent.exe" "%INSTALL_DIR%\" >nul
copy /Y "%AGENT_SRC%\*.dll" "%INSTALL_DIR%\" >nul 2>&1
:: net48 framework-dependent build: each folder holds the exe + its dependency
:: DLLs + .config. Copy both folders into the install dir (shared DLLs overwrite
:: identically; the two .exe.config files coexist).
copy /Y "%SERVICE_SRC%\*.*" "%INSTALL_DIR%\" >nul
copy /Y "%AGENT_SRC%\*.*" "%INSTALL_DIR%\" >nul
:: Install DirectX June 2010 runtime (required by games)
if exist "%ROOT%\dx9201006\DXSETUP.exe" (
+8 -8
View File
@@ -36,14 +36,14 @@ Global
{467AA87A-FBD4-45D7-B8F8-9336C95884D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{467AA87A-FBD4-45D7-B8F8-9336C95884D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{467AA87A-FBD4-45D7-B8F8-9336C95884D2}.Release|Any CPU.Build.0 = Release|Any CPU
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Debug|Any CPU.ActiveCfg = Debug|x64
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Debug|Any CPU.Build.0 = Debug|x64
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Release|Any CPU.ActiveCfg = Release|x64
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Release|Any CPU.Build.0 = Release|x64
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Debug|Any CPU.ActiveCfg = Debug|x64
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Debug|Any CPU.Build.0 = Debug|x64
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Release|Any CPU.ActiveCfg = Release|x64
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Release|Any CPU.Build.0 = Release|x64
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{910D4404-B3A2-4217-B61C-D43E7F22CDAA}.Release|Any CPU.Build.0 = Release|Any CPU
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{916DCDA5-3379-4383-96F0-AC7B26FAF64E}.Release|Any CPU.Build.0 = Release|Any CPU
{0B2E3F1F-56C4-4B1A-AC26-4BF34AA10F1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0B2E3F1F-56C4-4B1A-AC26-4BF34AA10F1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B2E3F1F-56C4-4B1A-AC26-4BF34AA10F1B}.Release|Any CPU.ActiveCfg = Release|Any CPU