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,78 @@
# 2026-04-07 — Issue #10 engine-bridge v2 Generator
- 역할: Generator
- 계약: `docs/contracts/engine-bridge-v2.md`
- 소요 시간: 약 35분 (단일 세션)
- Context 사용량: 약 95k tokens (대용량 sln/.cs 디렉터리 스캔 + MetadataLoadContext 출력)
## Editor03 / HmEG 디스커버리 결과
`MetadataLoadContext``EG-BIM Modeler/Editor03.PluginInterface.dll`,
`Editor07.WidgetPluginInterface.dll`, `HmEG.dll`, `Plugins/EgBoxPlugin/EgBoxPlugin.dll`
메타데이터 전용으로 열어 다음을 확인했다:
- **실제 plugin 컨트랙트는 `HmEG.IPlugin`** (HmEG.dll). Members:
`string Name { get; }`, `EGViewport View { get; set; }`,
`bool RethrowException { get; set; }`, `object Run(object[])`.
- **MEF Export 어트리뷰트는 사용되지 않는다.** 로딩은 `HmEG.PluginLoader`
(`LoadProjectPlugins`/`LoadPlugin(path,name)`)가 직접 수행한다.
`EgBoxPlugin.EgBoxPlugin` 샘플도 `[Export]` 없이 단순히
`Editor.PluginInterface.EditorPlugin`을 상속한다.
- **`Editor.PluginInterface.EditorPlugin`** (Editor03.PluginInterface.dll)
`HmEG.IPlugin`을 구현하는 추상 base다.
abstract 멤버: `Name { get; }`, `Description { get; }`,
`protected void Initialize()`. 그 외 `AppManager`, `RootSpace`,
`ViewportManager`, `View`, `AddModelToRootSpace(...)` 등의 헬퍼를 노출한다.
- **`Editor07.WidgetPluginInterface`** 는 위젯 전용(`WidzetPlugin`,
`HmEG_DebugWidzetPlugin`)이며 v2 범위 밖이라 미사용.
→ 결론: `HmEgBridgePlugin : Editor.PluginInterface.EditorPlugin` 으로
구현. `Name`/`Description` override + `protected override Initialize()`,
HTTP listener는 생성자에서 안전하게 부팅 (`Initialize`가 호출되지 않더라도
listener는 살아있음).
### 디스커버리 시 주의사항
- MetadataLoadContext에 `Microsoft.NETCore.App` 런타임 디렉터리만 넣으면
`WindowsBase``System.Windows.Markup.InternalTypeHelper`를 못 찾아
`TypeLoadException`이 난다. **WindowsDesktop ref pack
(`packs/Microsoft.WindowsDesktop.App.Ref/8.0.22/ref/net8.0`)** 도 함께
resolver에 등록해야 한다. 동일 파일명은 dedupe 필요.
- 같은 이유로 `Editor03.PluginInterface``<UseWPF>true</UseWPF>`
net8.0-windows 프로젝트에서만 빌드된다.
## 산출물
- `src/Recordingtest.EgPlugin/` (PortResolver, IEngineStateProvider,
Null/ReflectionEngineStateProvider, StateRouter, BridgeHttpServer,
HmEgBridgePlugin)
- `src/Recordingtest.EngineBridge.Client/` (HmEgHttpSnapshot,
EngineBridgeException)
- `tests/Recordingtest.EngineBridge.IntegrationTests/` (FakeBridgeServer +
6 xUnit tests)
- `tests/Recordingtest.EgPlugin.Tests/` (5 xUnit tests for StateRouter +
PortResolver)
- `docs/guides/engine-bridge-deploy.md`
## 빌드 / 테스트 결과
- `dotnet build recordingtest.sln` → green (warning 0, error 0)
- `dotnet test tests/Recordingtest.EngineBridge.IntegrationTests` → 6/6 통과
- `dotnet test tests/Recordingtest.EgPlugin.Tests` → 5/5 통과
- 신규 프로젝트 4개를 `recordingtest.sln`에 추가
- `EG-BIM Modeler/` 폴더에는 일체 쓰지 않음 (가드 훅 준수). 배포는 가이드
문서로만 기술.
## 리스크 / 후속
- `ReflectionEngineStateProvider`는 v2 skeleton: 모든 메서드가 안전한
default를 반환한다. **HmEG 내부 타입(특히 `HmEGAppManager`,
`EGViewport`, 선택/카메라 manager)에 대한 진짜 reflection 매핑은 v3에서
smoke test 후 확정해야 한다.** 후보 멤버는
`docs/engine-catalog/hmeg-candidates.json` 참고.
- `HttpListener` urlacl이 등록 안 된 환경에서는 첫 실행에 관리자 권한 또는
`netsh http add urlacl` 필요 — 가이드에 명시.
- `Editor.PluginInterface.EditorPlugin.Initialize()`가 protected이고
HmEG.PluginLoader가 어느 시점에 호출하는지는 메타데이터로 확인 불가
(런타임 동작). 그래서 listener를 생성자에서 부팅했다. PluginLoader가
생성자 호출만으로 충분한지는 smoke test에서 확인 필요.