XP11: whole suite on net40 — Console + vPOD run on XP SP3 through Win11
The Launcher's XP11 port (8730b9b) now extends to everything: one net40
flavor across Console, vPOD, Contract, and SecureConfig (Newtonsoft.Json
everywhere; the net48/System.Text.Json legs and their #if splits are gone
since nothing consumed them).
Console (net40, single TFM like the Launcher):
- The ~31 BinaryFormatter bitmap blobs in the .resx files became raw
embedded files under assets/icons/ (extracted byte-faithfully via a
serialization surrogate — the animated square_throbber.gif survives),
loaded by Properties.Resources.EmbeddedBitmap/EmbeddedIcon. Reason:
System.Resources.Extensions' DeserializingResourceReader is net461+
and cannot load on net40. Strings stay in the .resx.
- IReadOnlyList -> IList in AppRegistry (net45+ interface).
vPOD (net40, single TFM):
- Zip extraction now shares the Launcher's MiniZip.cs (linked source), so
the diff-test install round-trip exercises it against ZipArchive zips.
- RPC args as JTokens; LaunchApps.json persistence via Newtonsoft;
Thread.VolatileRead instead of Volatile.Read.
Contract/SecureConfig: net40-only; Client/** (PodManagerConnection) now
ships in the one build. The Launcher package gains
TeslaSecureConfiguration.dll as a dependency of the client half.
Tests: the net48 xunit host loads the net40 assemblies (both CLR4), so
the suite exercises exactly what ships — 106/106 green. Also verified
live: net40 console provisioned, managed, and ran a full RP mission
against net40 vPOD (beacon/passphrase/RSA, 53290 RPC, egg load,
Run/Stop Mission).
Version: 4.11.4.3 across Launcher, Console, and vPOD (vPOD joins the
suite version line; was 1.0.0). Ship the dotNetFx40 redistributable in
Launcher/assets for XP-era pods.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,46 +14,34 @@
|
||||
// Dispatch is by method NAME (RpcRequest.Method) — the old serialized-MethodBase
|
||||
// + SerializationBinder + MethodInfoProxy machinery is gone. Both ends share this
|
||||
// one file, so the request/response shape cannot drift.
|
||||
//
|
||||
// Serializer: Newtonsoft.Json — System.Text.Json has no net40 target, and since
|
||||
// XP11 the whole suite (Console, Launcher, vPOD) is net40. The protocol briefly
|
||||
// had an STJ leg for the net48 Console era; it wrote shape-identical JSON
|
||||
// (PascalCase member names, fields included, Guids as strings, ISO-8601 dates),
|
||||
// so anything that captured wire traffic then still matches what this writes.
|
||||
// =============================================================================
|
||||
|
||||
// NET40 (XP11 single-binary launcher): System.Text.Json has no net40 target, so
|
||||
// this leg serializes with Newtonsoft.Json instead. Same framing, same JSON shape
|
||||
// (PascalCase member names, fields included, Guids as strings, ISO-8601 dates) —
|
||||
// an STJ client and a Newtonsoft server interoperate byte-compatibly on the wire.
|
||||
// Arguments surface as JToken here instead of JsonElement.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#if NET40
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
#else
|
||||
using System.Text.Json;
|
||||
#endif
|
||||
|
||||
namespace Tesla.Net
|
||||
{
|
||||
/// <summary>One RPC call: a method name plus its arguments as JSON elements.</summary>
|
||||
/// <summary>One RPC call: a method name plus its arguments as JSON tokens.</summary>
|
||||
public sealed class RpcRequest
|
||||
{
|
||||
public string Method { get; set; }
|
||||
#if NET40
|
||||
public List<JToken> Args { get; set; }
|
||||
#else
|
||||
public List<JsonElement> Args { get; set; }
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>One RPC result: the return value as JSON, or an error message.</summary>
|
||||
public sealed class RpcResponse
|
||||
{
|
||||
#if NET40
|
||||
public JToken Result { get; set; } // JSON null for void / null
|
||||
#else
|
||||
public JsonElement Result { get; set; } // JsonValueKind.Null for void / null
|
||||
#endif
|
||||
public string Error { get; set; } // null on success
|
||||
}
|
||||
|
||||
@@ -65,18 +53,9 @@ namespace Tesla.Net
|
||||
/// streamed out-of-band, not framed), guarding against hostile lengths.</summary>
|
||||
public const int MaxFrameBytes = 16 * 1024 * 1024;
|
||||
|
||||
#if NET40
|
||||
// Newtonsoft serializes public fields of the wire types by default, and
|
||||
// writes ISO-8601 dates / string Guids like STJ — no special options needed.
|
||||
// writes ISO-8601 dates / string Guids — no special options needed.
|
||||
public static readonly JsonSerializer JsonOptions = JsonSerializer.CreateDefault();
|
||||
#else
|
||||
public static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
// The Tesla.Net wire types (LaunchData, LaunchPair, ...) expose public
|
||||
// FIELDS, which System.Text.Json ignores unless this is set.
|
||||
IncludeFields = true,
|
||||
};
|
||||
#endif
|
||||
|
||||
// ── Framing ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -114,7 +93,6 @@ namespace Tesla.Net
|
||||
|
||||
// ── Request ──────────────────────────────────────────────────────────
|
||||
|
||||
#if NET40
|
||||
public static void WriteRequest(Stream stream, string method, object[] args)
|
||||
{
|
||||
var req = new RpcRequest { Method = method, Args = new List<JToken>() };
|
||||
@@ -132,23 +110,9 @@ namespace Tesla.Net
|
||||
// goes back byte-identical instead of reformatted through DateTime.
|
||||
private static readonly JsonSerializerSettings ReadSettings =
|
||||
new JsonSerializerSettings { DateParseHandling = DateParseHandling.None };
|
||||
#else
|
||||
public static void WriteRequest(Stream stream, string method, object[] args)
|
||||
{
|
||||
var req = new RpcRequest { Method = method, Args = new List<JsonElement>() };
|
||||
if (args != null)
|
||||
foreach (var a in args)
|
||||
req.Args.Add(JsonSerializer.SerializeToElement(a, JsonOptions));
|
||||
WriteFrame(stream, JsonSerializer.SerializeToUtf8Bytes(req, JsonOptions));
|
||||
}
|
||||
|
||||
public static RpcRequest ReadRequest(Stream stream)
|
||||
=> JsonSerializer.Deserialize<RpcRequest>(ReadFrame(stream), JsonOptions);
|
||||
#endif
|
||||
|
||||
// ── Response ─────────────────────────────────────────────────────────
|
||||
|
||||
#if NET40
|
||||
public static void WriteResponse(Stream stream, object result, string error)
|
||||
{
|
||||
object payload = error == null ? result : null;
|
||||
@@ -163,21 +127,5 @@ namespace Tesla.Net
|
||||
public static RpcResponse ReadResponse(Stream stream)
|
||||
=> JsonConvert.DeserializeObject<RpcResponse>(
|
||||
Encoding.UTF8.GetString(ReadFrame(stream)), ReadSettings);
|
||||
#else
|
||||
public static void WriteResponse(Stream stream, object result, string error)
|
||||
{
|
||||
var resp = new RpcResponse
|
||||
{
|
||||
// Always a valid element (JSON null when there is no result): a
|
||||
// default(JsonElement) is ValueKind.Undefined and is not serializable.
|
||||
Result = JsonSerializer.SerializeToElement(error == null ? result : null, JsonOptions),
|
||||
Error = error,
|
||||
};
|
||||
WriteFrame(stream, JsonSerializer.SerializeToUtf8Bytes(resp, JsonOptions));
|
||||
}
|
||||
|
||||
public static RpcResponse ReadResponse(Stream stream)
|
||||
=> JsonSerializer.Deserialize<RpcResponse>(ReadFrame(stream), JsonOptions);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user