Skip to main content

glue-project-loading

Run an FRB1/Glue project's .gluj/.glsj/.glej data directly in FRB2, with no Glue codegen. Triggers: GlueProject, GlueScreen, GlueEntity, EngineInitSettings.GlueProjectFile, GlueTypeMap, .gluj, CustomVariables by name.

Zur Installation springen

Quellinformationen

Repository
vchelaru/FlatRedBall2
Letzte Quellaktivität
10. August 2026 um 04:26
Erkannte Sprache von SKILL.md
Englisch
Sterne
14
Forks
6

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
glue-project-loading
description
Run an FRB1/Glue project's .gluj/.glsj/.glej data directly in FRB2, with no Glue codegen. Triggers: GlueProject, GlueScreen, GlueEntity, EngineInitSettings.GlueProjectFile, GlueTypeMap, .gluj, CustomVariables by name.
# Loading Glue Projects FRB2 reads the JSON files Glue writes and builds Screens and Entities from them at runtime. There is no generated C#: `src/Glue/` is the whole implementation, `GlueProject` is the entry point. Booting one is a single call in `Game1.Initialize`, after `base.Initialize()`: ```csharp FlatRedBallService.Default.Initialize(this, "Content/FrbEditor/MyGame.gluj"); ``` The project names its own start-up screen, Gum project and resolution, so nothing else is passed — the engine wires the screen's `Save`/`Project` itself. **The path must be relative**: it resolves through `TitleContainer`, which throws on a rooted path, so an absolute one loads the project and then fails every asset it references. To load a project but boot your own screen instead, use `Initialize<MyScreen>(game, new EngineInitSettings { GlueProjectFile = … })`. ## Where to start | Task | Go to | |---|---| | Boot a project | `Initialize(game, "Content/FrbEditor/MyGame.gluj")` | | Load without booting | `GlueProject.Load(glujPath, content)` | | Find/create an element | `GlueProject.FindScreen`/`FindEntity`/`CreateScreen`/`CreateEntity` | | Move between screens | `Screen.MoveToScreen(string glueName)` | | Read/write an authored variable | the indexer on `GlueScreen`/`GlueEntity`, or `Get<T>` | | Claim a Glue type for FRB2 | `GlueTypeMap` | ## Every loaded element shares one C# type `GlueScreen` and `GlueEntity` are what a loaded element *is*; only its `Save` data distinguishes it. So `MoveToScreen<T>()` and `Factory<T>` cannot tell two loaded screens apart — the by-name overloads (`MoveToScreen(string)`, `GlueProject.CreateEntity(name, screen)`) exist for exactly this reason. Both directions work: a loaded screen can still `MoveToScreen<AHandWrittenScreen>()`. Names are Glue's own, with the folder: `Screens\Level1`, `Entities\Player`. Either separator is accepted and case is ignored, but the prefix is required — a bare leaf is ambiguous, since a screen and an entity can share one. ## Landmines **Assign `Save` inside the `configure` callback, never before it.** The engine retains that callback and replays it on `RestartScreen`; assigning outside means a restarted screen rebuilds with no data — a silently *empty* screen rather than an error. Only applies when you construct a `GlueScreen` yourself: `Initialize(game, glujPath)` and `MoveToScreen(string)` both do it correctly. **Loading is tolerant by design: it collects diagnostics instead of throwing.** A project that references a type this build cannot construct still loads, minus that object. Check `GlueLoadResult.HasErrors` and `Diagnostics`, and `GlueScreen.BuildDiagnostics` for per-element problems — otherwise a half-built screen looks like a working one. `GlueLoadOptions.Strict` turns the first diagnostic into a throw when you would rather fail loudly. **Adding a row to `GlueTypeMap` requires a matching `DynamicDependency`.** The reflected set is closed and the engine publishes AOT/trimmed; without it the type's properties are trimmed away and every assignment to them silently does nothing — in published builds only, never in `dotnet build`. **A Glue project's Gum UI must be loaded through `GlueProjectFile`, not `GumProjectFile`.** Gum only resolves elements from a project its own `GumService` loaded, and the `.gumx` path is not known until the `.gluj` has been read. See `gum-integration` for what goes wrong otherwise. **Abstract elements cannot be created.** Glue marks an element abstract when it leaves an object for a derived element to supply, so it is incomplete by construction. `CreateScreen`/`CreateEntity` throw; this is normal for a `GameScreen` base that levels derive from. ## Hot reload A `GlueScreen` watches the source tree the `.gluj` sits in and restarts itself when Glue writes there: copy the changed file to the build output, reparse the project, rebuild the same screen from the new data. Nothing to call — it registers in `CustomInitialize` whenever `FlatRedBallService.SourceContentRoots` is non-empty, so it is dev-only by construction. Opt out with `FlatRedBallService.IsGlueHotReloadEnabled` before the first screen starts. **The reload restart replaces the retained `configure` callback.** `Save` and `Project` survive — the replacement reassigns them — but anything else a callback you passed did, a difficulty or a seed or a hand-built object, is gone from every restart after the first Glue edit. Put that setup in a `GlueScreen` subclass's `CustomInitialize` or in `RestoreHotReloadState`. Gum files are left to Gum's own in-place pipeline, and `bin`/`obj` are filtered by `ContentDirectoryWatcher.IgnoredDirectories`. `content-hot-reload` covers the watch/copy machinery underneath. ## Variables: two surfaces, two intents `Objects` is the typed dictionary of built objects — `(Circle)screen.Objects["CooldownCircle"]`. The indexer is the variable bag: `entity["Health"] = 100`, `entity.Get<int>("Health")`. The indexer resolves in a fixed order, and the order is the point: a name matching a real CLR member writes that member (`entity["X"] = 5f` moves the entity), a tunneling variable reaches the contained object it targets, and only an unclaimed name is held by name. `Get<T>` is driven by `T`, not by the variable's declared type — Glue's declared types are frequently not CLR types at all. ## Inheritance is resolved at load, once `GlueProjectLoader` flattens every element into the union of its chain before anything inspects it, so a derived screen already carries its base's objects, variables, states, and referenced files. Code reading a `ScreenSave` never walks `BaseScreen` itself. ## What is not wired Pooling (`PooledByFactory`), `SortAxis` partitioning, and input binding (`JumpInput`, `MovementInput`, `EntitySave.InputDevice`) are parsed and not applied — a loaded platformer entity has its authored physics but nothing telling it to move. `CustomClasses` and `Events` are out of scope entirely. `plan/804-glue-project-loader/` records why for each.
Auf GitHub ansehen