Implement engine-bridge v2 plugin masquerade (#10)

This commit is contained in:
minsung
2026-04-07 16:08:31 +09:00
parent 4cee3c2d86
commit b1c2383a54
18 changed files with 1017 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
namespace Recordingtest.EngineBridge.Client;
public sealed class EngineBridgeException : Exception
{
public string Endpoint { get; }
public EngineBridgeException(string endpoint, string message, Exception? inner = null)
: base($"engine-bridge {endpoint}: {message}", inner)
{
Endpoint = endpoint;
}
}

View File

@@ -0,0 +1,121 @@
using System.Net.Http;
using System.Text.Json;
namespace Recordingtest.EngineBridge.Client;
public sealed class HmEgHttpSnapshot : IEngineSnapshot, IDisposable
{
public const string DefaultBaseUrl = "http://localhost:38080";
private readonly HttpClient _http;
private readonly bool _ownsClient;
private readonly string _baseUrl;
public HmEgHttpSnapshot(string baseUrl = DefaultBaseUrl, HttpClient? httpClient = null, TimeSpan? timeout = null)
{
_baseUrl = baseUrl.TrimEnd('/');
if (httpClient is null)
{
_http = new HttpClient { Timeout = timeout ?? TimeSpan.FromSeconds(2) };
_ownsClient = true;
}
else
{
_http = httpClient;
if (timeout.HasValue) _http.Timeout = timeout.Value;
_ownsClient = false;
}
}
public IReadOnlyList<string> SelectedObjectIds
{
get
{
using var doc = Get("/selection");
var arr = doc.RootElement.GetProperty("selected_ids");
var list = new List<string>(arr.GetArrayLength());
foreach (var e in arr.EnumerateArray()) list.Add(e.GetString() ?? string.Empty);
return list;
}
}
public CameraState Camera
{
get
{
using var doc = Get("/camera");
var r = doc.RootElement;
return new CameraState(
ToArray(r.GetProperty("eye")),
ToArray(r.GetProperty("target")),
ToArray(r.GetProperty("up")),
r.GetProperty("fov").GetDouble());
}
}
public SceneSummary Scene
{
get
{
using var doc = Get("/scene");
var r = doc.RootElement;
string? path = r.TryGetProperty("document_path", out var dp) && dp.ValueKind == JsonValueKind.String ? dp.GetString() : null;
return new SceneSummary(r.GetProperty("object_count").GetInt32(), path);
}
}
public bool IsRenderComplete
{
get
{
using var doc = Get("/render");
return doc.RootElement.GetProperty("complete").GetBoolean();
}
}
public bool IsHealthy
{
get
{
try
{
using var doc = Get("/health");
return doc.RootElement.TryGetProperty("status", out var s) && s.GetString() == "ok";
}
catch (EngineBridgeException) { return false; }
}
}
private JsonDocument Get(string endpoint)
{
try
{
using var resp = _http.GetAsync(_baseUrl + endpoint).GetAwaiter().GetResult();
if (!resp.IsSuccessStatusCode)
throw new EngineBridgeException(endpoint, $"HTTP {(int)resp.StatusCode}");
var body = resp.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return JsonDocument.Parse(body);
}
catch (EngineBridgeException) { throw; }
catch (TaskCanceledException ex)
{
throw new EngineBridgeException(endpoint, "timeout", ex);
}
catch (Exception ex)
{
throw new EngineBridgeException(endpoint, ex.Message, ex);
}
}
private static double[] ToArray(JsonElement e)
{
var arr = new double[e.GetArrayLength()];
int i = 0;
foreach (var item in e.EnumerateArray()) arr[i++] = item.GetDouble();
return arr;
}
public void Dispose()
{
if (_ownsClient) _http.Dispose();
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<RootNamespace>Recordingtest.EngineBridge.Client</RootNamespace>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Recordingtest.EngineBridge\Recordingtest.EngineBridge.csproj" />
</ItemGroup>
</Project>