Skip to main content

glue-live-game-testing

Testing GlueControl's embedded runtime (CommandReceiver, GlueControlManager) against a real running game process. Triggers: LiveGameProcess, live edit runtime behavior, ScreenManager.Start vs MoveToScreen, testing Embedded/*.cs, Category=LiveGame.

Informations de source

Dépôt
vchelaru/FlatRedBall
Dernière activité de la source
26 septembre 2026 à 15:00
Langue détectée de SKILL.md
anglais
Étoiles
578
Forks
73

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
glue-live-game-testing
description
Testing GlueControl's embedded runtime (CommandReceiver, GlueControlManager) against a real running game process. Triggers: LiveGameProcess, live edit runtime behavior, ScreenManager.Start vs MoveToScreen, testing Embedded/*.cs, Category=LiveGame.
# Glue Live Game Testing `GameCommunicationPlugin/GlueControl/Embedded/*.cs` (CommandReceiver, GlueControlManager, EditingManager, ...) is `<Compile Remove>`d from `GameCommunicationPlugin.csproj` — it only exists inside a compiled, running game process, never inside Glue itself. [glue-unit-test-bootstrap](../glue-unit-test-bootstrap/SKILL.md)'s `GoldProjectCompileTests` prove that closure *compiles*; they never run it. If a bug is in what `CommandReceiver.HandleDto` actually *does* at runtime (screen transitions, entity selection, edit-mode state), a compile-only test cannot catch it — you need a real running game process. ## The harness `GlueUnitTests/TestSupport/LiveGameProcess.cs` builds a gold project, launches its real built `.exe`, and drives it through Glue's actual `CommandSender`/`GameJsonCommunicationPlugin.Common.GameConnectionManager` socket protocol — the same wire protocol production Glue uses, not a stand-in. ```csharp [Trait("Category", "LiveGame")] [StaFact] public async Task MyTest() { GlueTestBootstrap.EnsureGameProjectPluginsRegistered(); using var game = await LiveGameProcess.StartAsync( "Samples/EditorTest1", csprojRelativeToProjectRoot: "EditorTest1/EditorTest1.csproj", exeRelativeToProjectRoot: "EditorTest1/bin/Debug/net9.0/EditorTest1.exe"); var screenName = await game.GetCurrentScreenName(); // "" if no screen loaded var response = await game.SelectEntity("Entities\\Entity1"); } ``` `StartAsync` (default `refreshLiveEditCodeFromSource: true`) loads the copied project into Glue and calls `GoldProject.EmbedLiveEditCode()` before building, so the test exercises the CURRENT branch's `Embedded/*.cs`, not whatever was checked in. See `LiveGameProcessTests.cs` for worked examples. The `afterBuildBeforeLaunch` hook changes the project after the build, for tests where the running game is behind Glue (see `EditorTest1_SharedVariableChangedAfterBuild_IsAppliedByConnectSweep`). ## Adding a new drive/observe method Follow `GetCurrentScreenName()`/`SelectEntity()`: build the real DTO (`GameCommunicationPlugin.GlueControl.Dtos`) and call `CommandSender.Self.Send(dto)` directly. **Landmine — do not set `GlueState.Self.CurrentEntitySave`/`CurrentScreenSave`/`CurrentNamedObjectSave` to drive selection.** Their setters route through `GlueState.Find.TreeNodeByTag(value)`, which needs a real, populated WPF tree view. There isn't one in this headless host, so the assignment silently no-ops (`CurrentElement` stays null) and `RefreshManager.PushGlueSelectionToGame` sends nothing — no exception, no signal, just a test that mysteriously never sees the effect. Skip Glue's UI-bound selection state entirely and build the DTO by hand. ## Only `Samples/EditorTest1` works as a target project today `LiveGameProcess` needs a project whose `Game1.Generated.cs` already constructs `GlueControlManager`/`GameConnectionManager` — i.e. one that went through a real "enable live edit" Glue session. That wiring comes from `MainCompilerPlugin.HandleGluxLoaded` (`Game1GlueControlGenerator`), and `MainCompilerPlugin` cannot run in the test host (it builds real WPF tabs and opens sockets on registration — same reason `GoldProject.EmbedLiveEditCode()` calls `EmbeddedCodeManager.EmbedAll` directly instead of going through it). So `Game1.Generated.cs` can't be regenerated headlessly; `LiveGameProcess` preserves it exactly as checked in (only patching its port) rather than deleting/regenerating it. `Samples/EditorTest1` is checked in with its `Generated.cs` committed - a `.gitignore` exception like `BeefballKni`'s, since `*.Generated.cs` is gitignored repo-wide otherwise. To add another target project: turn on live edit for real in a real Glue session, copy the project in the same way (exclude `bin`/`obj`, keep the sibling `.sln` - `ProjectLoader` needs it), add the same two-line `.gitignore` exception. ## Wire protocol, if you need to touch it Game connects OUT to Glue (Glue listens). Two separate TCP sockets, one per direction, each opened with a 1-byte handshake (`1` = glue→game, `2` = game→glue) — see `GameCommunicationPlugin/Common/GameConnectionManager.cs` (Glue-side, server) and `GlueControl/Embedded/GameConnectionManager.cs` (game-side, client, namespace `GlueCommunication`). Port is baked into `Game1.Generated.cs` as a literal int at two call sites (was `8846` in the checked-in fixture) - `LiveGameProcess` text-patches both before building, to a fresh port per run so it never collides with a real Glue instance on the dev machine. The actual DTO dispatch is `GlueControlManager.ProcessMessage` (`Embedded/GlueControlManager.cs`) - `"GetCurrentScreen"` is a raw-string command handled specially; everything else is `"{DtoTypeName}:{json}"` routed to `CommandReceiver.Receive`/`HandleDto(SelectObjectDto)` etc. ## CI Tagged `Category=LiveGame` and run by its own step in `pr-tests.yml`, ahead of the slower build smoke test. `glue.yml` still excludes it, since that workflow publishes releases and should not wait on a game window. Locally: `dotnet test ... --filter "Category=LiveGame"`. **Landmine — the runners have no GPU.** `opengl32.dll` resolves to Windows' generic OpenGL 1.1, which has no framebuffer objects, so MonoGame's `GraphicsDevice` throws `NoSuitableGraphicsDeviceException` and the game dies before it can connect. `pr-tests.yml`'s "Install Mesa llvmpipe" step downloads a software GL and points `FRB_LIVE_GAME_TEST_GL_RUNTIME` at it; `LiveGameProcess` copies those DLLs next to each game, since Windows resolves `opengl32.dll` from the exe's own directory ahead of System32. Every game runs from its own temp directory, so CI cannot stage them at a fixed path.
Voir sur GitHub