Releases: Far-Beyond-Pulsar/Pulsar-Native
Release list
Release v0.2.42
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.42
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.42, latest |
Container Images
✨ Highlights
- Working plugin system implemented, enabling dynamic plugin loading. Thanks @tristanpoland!
- File manager overhaul with improved functionality and formatting. Thanks @tristanpoland!
- Live configuration validation added for chat providers. Thanks @tristanpoland!
- UI refactor and updates, including fixes for drag-and-drop functionality. Thanks @tristanpoland!
⚠️ Breaking Changes
Caution
Breaking change: Plugin API refactored, requiring updates to existing plugins. Thanks @tristanpoland!
🚀 Features
- Support for OpenCode paths and key validation added. Thanks @tristanpoland!
- Modules added to improve project structure and functionality. Thanks @tristanpoland!
🛠️ Improvements
- Enhanced
engine_fsusage for better file system operations. Thanks @tristanpoland! - Async model refresh implemented with safer provider updates. Thanks @tristanpoland!
- Cached models preserved during provider setup refactor. Thanks @tristanpoland!
- Improved OpenCode support with additional functionality. Thanks @tristanpoland!
🐛 Bug Fixes
- Fixed asset drag start and hover state reset issues. Thanks @tristanpoland!
- Prevented click bleedthrough in UI interactions. Thanks @tristanpoland!
- Fixed provider URL handling and added tracing for better debugging. Thanks @tristanpoland!
⚡ Performance
- Optimized file manager operations for faster performance. Thanks @tristanpoland!
🧩 Plugin API
- Plugin API refactored to support dynamic plugin loading. Thanks @tristanpoland!
- New
plugin_loading.rsmodule created for plugin management. Thanks @tristanpoland! - Updates to
plugin.rsandlib.rsto enhance plugin functionality. Thanks @tristanpoland!
📚 Documentation
- README updated to remove media gallery and project status sections. Thanks @tristanpoland!
- Added README files for new modules. Thanks @tristanpoland!
- ROADMAP updated with clearer project goals. Thanks @tristanpoland!
🧰 Developer Experience
- Updated CI release workflows for reliability. Thanks @tristanpoland!
- Code cleanup and linting performed across the project. Thanks @tristanpoland!
- Updated
.dockerignoreandconfig.tomlfor better build configurations. Thanks @tristanpoland!
🧹 Internal
- Modularized project structure for better maintainability. Thanks @tristanpoland!
- Refactored
cloud_service.rs,remote.rs, and other internal modules. Thanks @tristanpoland! - Dependency updates and version bumps applied. Thanks @tristanpoland!
Release v0.2.41
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.41
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.41, latest |
✨ Highlights
- Introduced a new plugin install screen for easier plugin management. Thanks @tristanpoland!
- Overhauled the entry screen for a more streamlined user experience. Thanks @tristanpoland!
- Fixed selected sidebar item icon color for better visual clarity. Thanks @tristanpoland!
- Updated onboarding flow to improve user guidance. Thanks @tristanpoland!
🚀 Features
- Added HTTP fallback for enhanced connectivity. Thanks @tristanpoland!
- Embedded Git support for better version control integration. Thanks @tristanpoland!
🛠️ Improvements
- Updated WGPUI library, though it introduces a known issue with brighter canvases. Thanks @tristanpoland!
- Fixed surface tint issues for improved UI consistency. Thanks @tristanpoland!
- Cleaned up launcher UI for a more polished appearance. Thanks @tristanpoland!
🐛 Bug Fixes
- Resolved an issue with the onboarding screen trigger not functioning as expected. Thanks @tristanpoland!
- Fixed image rendering issues across the application. Thanks @tristanpoland!
- Prevented overlay input leakage to improve input handling. Thanks @tristanpoland!
🧰 Developer Experience
- Cleaned up
Cargo.tomlfor better dependency management. Thanks @tristanpoland! - Performed general cargo cleanup to streamline the build process. Thanks @tristanpoland!
🧹 Internal
- Added a TODO note for future development. Thanks @tristanpoland!
- Reverted UI surface element fixes to address unintended side effects. Thanks @tristanpoland!
- Updated
main.rsandonboarding.rsfor internal code improvements. Thanks @tristanpoland!
Release v0.2.40
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.40
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.40, latest |
✨ Highlights
- Added intro screen images for an enhanced user experience. Thanks, @tristanpoland!
🚀 Features
- Introduced intro screen images to improve the visual appeal of the application. Kudos to @tristanpoland!
🧹 Internal
- Performed a force release to ensure the latest changes are deployed. Credit to @tristanpoland.
Release v0.2.39
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.39
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.39, latest |
v0.2.39 — Plugin API, Onboarding, GPU Flamegraph + Level Editor State Overhaul
|
|
|
|
|
|
🧩 Plugin System (PR #277)
Extracted engine_subsystems crate
The monolithic subsystem framework that lived inside engine_backend::subsystems::framework (~428 lines) has been extracted into a standalone engine_subsystems crate.
crates/engine_subsystems/
├── Cargo.toml # deps: thiserror + tracing (zero engine/UI deps)
└── src/lib.rs # 508 lines: Subsystem trait, SubsystemRegistry,
# SubsystemId, SubsystemContext, Lifecycle API
engine_backend::subsystems::framework is now a thin re-export shim.
Note
This extraction was driven by DLL plugin ABI requirements. Plugins link against engine_subsystems without pulling in the full engine backend or any GPU/UI dependency chain.
Key types:
| Type | Purpose |
|---|---|
SubsystemId |
&'static str newtype — unique identifier for ordered registration |
Subsystem |
trait with id(), init(), shutdown(), update() |
SubsystemRegistry |
Kahn's algorithm topological sort, register_boxed(), merge(), lifecycle |
SubsystemContext |
Empty marker struct (tokio runtime handle removed — subsystems manage their own threading) |
Plugin component factories via EngineClass
New engine_backend/src/component_registry.rs:
pub type ComponentFactory = Box<dyn Fn() -> Box<dyn EngineClass> + Send + Sync>;
pub struct PluginComponentRegistry {
factories: HashMap<String, ComponentFactory>,
}- Plugin components register
ComponentFactoryclosures returningBox<dyn EngineClass> - Full reflection pipeline:
component_registry.create_instance(name)→instance.get_properties()→ serialized viaRUNTIME_TYPE_REGISTRY - Identical code path for built-in and plugin components — no opaque JSON shortcut
Important
The old DefaultComponentData opaque JSON type has been removed. Plugin components now go through the exact same EngineClass reflection pipeline as #[engine_class] built-in components.
EditorPlugin trait modularized
plugin_editor_api/src/lib.rs — the single EditorPlugin trait is now complemented by two extension traits:
| Trait | Provides |
|---|---|
EditorPluginComponents |
component_definitions(), component_factories() (returns Vec<(String, ComponentFactory)>) |
EditorPluginSubsystems |
subsystems() (returns Vec<Box<dyn Subsystem>>) |
EditorPluginFull |
Combined trait (auto-implemented by export_plugin!) |
The export_plugin! macro generates a wrapper struct that implements all three extension traits with sensible defaults.
Physics migrated to unified pipeline
EngineBackend::init() now starts with an empty subsystem registry — no hardcoded subsystems:
// Before (engine_backend/src/lib.rs):
let mut registry = SubsystemRegistry::new();
registry.register(PhysicsEngine::new()).expect("...");
// After:
EngineBackend { subsystems: SubsystemRegistry::new(), plugin_components: … }Registration happens in ui_core/src/app/constructors.rs:
plugin_manager.register_builtin_subsystem(Box::new(PhysicsEngine::new()));
plugin_manager.register_builtin_component_definitions(vec![
ComponentDefinition { id: "PhysicsComponent".into(), … },
ComponentDefinition { id: "RigidbodyComponent".into(), … },
]);
plugin_manager.register_builtin_component_factories(vec![
("PhysicsComponent".into(), Box::new(|| Box::new(PhysicsComponent::default()))),
…
]);EngineBackend::global() fixed
Caution
Critical bug fix. EngineBackend::set_global() previously piggybacked on EngineContext::global(), which isn't available during the BACKEND init step (step 5 of the init graph). This caused set_global() to silently no-op, meaning every consumer that called EngineBackend::global() received None and subsystems were never registered.
Fixed by switching to a module-level OnceLock:
static GLOBAL_BACKEND: OnceLock<ResourceHandle<EngineBackend>> = OnceLock::new();ResourceHandle::new() was made pub (previously pub(crate)) in engine_state/src/resource.rs to support this.
🎨 Onboarding & OOBE Rework
New onboarding overlay
ui_entry/src/entry_screen/views/onboarding.rs — 1,162 lines of new setup UI:
- Two-column layout: Theme picker (left) + Dependencies + Account (right)
- Theme cards: 160px bento-style cards with mini color swatches, name, and dark/light badge, arranged via
h_flex().flex_wrap() - Dependency installer: Detects Rust + build tools (
rustc,clang/gcc) and offers one-click install viarustup/ system package manager - GitHub sign-in: Device code flow shown inline (no modal) with copy/verify UX
- Avatar display: When signed in, loads from GitHub URL via background thread →
Arc<RenderImage>, shows circular avatar + display name +@login - Google sign-in button removed
- Scrollable content areas using
scrollable(ScrollbarAxis::Vertical)with.h_full()+.overflow_hidden()clipping pattern
Tip
The scrollable body pattern used in onboarding is: v_flex().flex_1().min_h_0().h_full().overflow_hidden().child(header).child(v_flex().id("x-scroll").flex_1().min_h_0().scrollable(ScrollbarAxis::Vertical)...)
OOBE embedded into entry screen
Previously the intro/OOBE tour opened in its own separate window. It is now rendered inside the entry screen as a conditional step before the onboarding modal.
Changes:
| File | What |
|---|---|
ui_entry/src/lib.rs |
Removed if !seen_intro branch that created a separate OOBE window. create_entry_component() always creates the entry screen directly |
ui_entry/src/entry_screen/mod.rs |
EntryScreen gains intro_screen: Option<Entity<IntroScreen>> field. In new(), if !has_seen_intro(), creates an embedded IntroScreen via new_embedded() and subscribes to IntroComplete. Render checks OOBE before onboarding |
ui_entry/src/oobe/intro_screen.rs |
IntroScreen gains embedded: bool field and new_embedded() constructor. When embedded, should_close does NOT call remove_window() — the parent handles cleanup |
Flow:
- First launch →
!has_seen_intro()→ embedded OOBE renders fullscreen in entry window - User completes/skips OOBE →
IntroCompleteemitted → subscriber setsintro_screen = None,show_onboarding = true, callsmark_intro_seen() - If deps are missing → onboarding overlay shows automatically
- If deps are fine → user can dismiss onboarding and use the entry screen
OOBE expiry
has_seen_intro() now checks the marker file's mtime against a compile-time constant:
const OOBE_EXPIRY: &str = "2026-06-26T00:00:00Z";If the oobe_complete marker file's last-modified time is before this date, it is treated as expired and OOBE replays. mark_intro_seen() overwrites the file each time, updating the mtime.
Note
Override the expiry by changing OOBE_EXPIRY in ui_entry/src/oobe/intro_screen.rs:974. Backdate the marker file with touch -t 202606250000 <path> to force a replay without recompiling.
Entry window size increased
crates/window_manager/src/configs.rs:
| Before | After |
|---|---|
| 1100×700 (min 800×500) | 1450×850 (min 1000×600) |
🔥 Custom GPU Flamegraph Renderer
Complete rewrite of the flamegraph profiler — from a CPU canvas renderer to a GPU-accelerated implementation using wgpu with custom WGSL shaders.
New files (ui-crates/ui_flamegraph/):
| File | Purpose |
|---|---|
| `src/renderi... |
Release v0.2.38
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.38
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.38, latest |
✨ Highlights
- Multi-architecture Docker image support added (amd64 + arm64). Thanks @tristanpoland!
🐛 Bug Fixes
- Fixed build process to support multi-arch Docker images (amd64 + arm64). Thanks @tristanpoland!
🧰 Developer Experience
- Improved release process with forced release mechanism. Thanks @tristanpoland!
Release v0.2.37
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.37
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.37, latest |
✨ Highlights
- Multi-architecture Docker image support (amd64 + arm64) added. Thanks to @tristanpoland.
🐛 Bug Fixes
- Fixed build process to support multi-arch Docker images (amd64 + arm64). Thanks to @tristanpoland.
🧰 Developer Experience
- Improved release process with forced release commit. Thanks to @tristanpoland.
Release v0.2.35
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.35
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.35, latest |
✨ Highlights
- Added project thumbnails for improved project visualization. Thanks, @tristanpoland!
- Fixed EntryScreen title bar to allow proper dragging. Kudos to @tristanpoland!
🚀 Features
- Added project thumbnails to enhance the user interface. Great work, @tristanpoland!
🛠️ Improvements
- Updated
helio_viewport.rswith minor enhancements. Thanks, @tristanpoland!
🐛 Bug Fixes
- Fixed EntryScreen title bar to allow proper dragging. Credit to @tristanpoland.
- Resolved issue where clicks on internal buttons leaked to open projects. Thanks, @tristanpoland!
🧹 Internal
- Merged pull request #257 for project thumbnails. Thanks, @tristanpoland!
- Force release for version 0.2.35. Handled by @tristanpoland.
Release v0.2.34
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.34
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.34, latest |
✨ Highlights
- Redesigned account popover for a more centralized and user-friendly experience. Thanks, @tristanpoland!
- Reuse open editor tabs to improve workflow efficiency. Kudos to @trchitho and @tristanpoland!
- Theme dropdown added for easier customization. Great work, @tristanpoland!
- Loading process now uses real tasks, significantly reducing editor initialization time. Thanks, @tristanpoland!
- Bumped blueprints plugin to support alt+click for disconnecting pins. Shoutout to @tristonarmstrong!
- Redesign categories in level editor properties panel. Thanks @ItsCubeTime, @tristanpoland!
🚀 Features
- Added a theme dropdown for enhanced user customization. Thanks, @tristanpoland!
🛠️ Improvements
- Redesigned and centralized account popover for better usability. Kudos to @tristanpoland!
- Updated blueprints plugin to support alt+clicking a pin to disconnect. Thanks, @tristanpoland!
🐛 Bug Fixes
- Fixed an issue where popovers were overlaying their triggers. Thanks, @tristanpoland!
- Resolved a freeze caused by AI chat health checks on each provider. Great work, @tristanpoland!
- Fixed deduplication of script tabs to prevent redundant editor instances. Kudos to @trchitho and @tristanpoland!
⚡ Performance
- Optimized loading process by using real tasks, reducing editor initialization time. Thanks, @tristanpoland!
🧹 Internal
- Updated
category_section.rsfor internal consistency. Thanks, @tristanpoland! - Forced release to ensure deployment consistency. Kudos to @tristanpoland!
Release v0.2.33
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.33
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.33, latest |
Note
Fixed major bug in last release which caused 0.2.32 to be yanked:
Known Issue in v0.2.32
A critical bug has been identified in release v0.2.32. When attempting to open a Blueprint Script Graph, the editor may incorrectly open the Shader Editor instead.
Cause
The issue is caused by conflicting file-type definitions in the file identification system, resulting in Blueprint Script files being misidentified and handled incorrectly.
Important Warning
Do not save a Blueprint Script that has been opened in this state.
Saving the file after it has been incorrectly loaded by the Shader Editor WILL cause irreversible file corruption.
Recommended Action
If you are currently using v0.2.32:
- Stop using this version immediately.
- Back up all project files as a precaution.
- Avoid opening Blueprint Script files until you have updated to a fixed release.
- If possible, revert to a previous stable version.
This release will be yanked later today to prevent further accidental data loss.
✨ Highlights
- Replaced the legacy
type_dbcrate with new in-memoryAssetIndexandUserTypeRegistrysystems inengine_fs, backed bypulsar_reflection. (@tristanpoland) - Migrated ad-hoc global state (script registry, agent chat tool registries, fab search cache, multiuser avatar cache, and eight
EngineContextfields) onto the unifiedengine_state::StateStore. (@tristanpoland) - Added two new built-in editor plugins: a visual Shader Graph editor for
.materialassets and a Table Editor for SQLite.db/.sqlite/.sqlite3databases. (@tristanpoland) - Replaced the
blueprint_compilerwrapper crate withpbgcdirectly, and patchedpbgcto fix pure-node bytecode caching. (@tristanpoland, Trae Assistant)
⚠️ Breaking Changes
Caution
Breaking change: Removed the type_db crate. Asset and user-defined type lookups now go through engine_fs's new AssetIndex and UserTypeRegistry, with dynamic type-alias registration via pulsar_reflection. Code calling the old type_db APIs (including pulsar_graph::type_system and prefab helpers, both removed) must migrate to the new registries.
Caution
Breaking change: Removed the blueprint_compiler crate. Consumers must depend on pbgc directly and update imports from blueprint_compiler::... to pbgc::....
Caution
Breaking change: EngineContext's ad-hoc Arc<RwLock<...>> fields (project, launch, discord, auth_profile, user_types, dev, default_level_bytes, window_manager) were migrated into engine_state::StateStore resources, the deprecated EngineState alias was removed, and the dead multiplayer module (GameSession) and several unused EngineContext/discord methods (next_window_id, register_window, window_count, clear_project, multiuser status setters, disconnect, is_enabled, etc.) were deleted. Direct field/method access must be replaced with StateStore resource lookups.
🚀 Features
- Added a built-in Shader Graph Editor plugin for visually authoring
.materialshader assets, including AI tool integration for graph inspection. (@tristanpoland) - Added a built-in Table Editor plugin for browsing and editing SQLite
.db/.sqlite/.sqlite3databases, with a table browser, query editor, and data view panel. (@tristanpoland)
🛠️ Improvements
- Introduced
AssetIndexandUserTypeRegistryinengine_fs(newasset_index.rsanduser_types.rs), and reworkedEngineFs,operations,scanner, andwatchersto use them in place oftype_db. (@tristanpoland) - Refactored engine startup into modular
crates/engine/src/steps/modules (logging, appdata, settings, runtime, backend, engine_context, dev_detect, set_global, discord, uri_registration, file_association) driven by a newinit_task!macro, shrinking the inline setup inmain.rsfrom ~284 lines. (@tristanpoland) - Added
engine_state::resource::WriteGuardfor RAII mutable access with automatic change notification, and convertedplugin_managertoparking_lot::RwLock, removing manual.unwrap()calls. (@tristanpoland) - Consolidated the standalone
macroscrate intopulsar_macros. (@tristanpoland)
🐛 Bug Fixes
- Fixed pure-node bytecode caching in PBGC by switching the
pbgcdependency to a patched fork (j4flmao/PBGC#fix-pure-node-bytecode-caching) via a new[patch]entry. (Trae Assistant, @tristanpoland)
⚡ Performance
- Removed ~135 lines of dead code from
engine_state::contextandengine_state::discord, including unused window-id tracking, multiplayer status methods, and a leftovermigrationmodule. (@tristanpoland)
🧩 Plugin API
- Plugins can now be wrapped as built-in editor providers with no DLL boundary, as demonstrated by the new Shader and Table Editor plugins registered in
ui_core::builtin_editors. (@tristanpoland) .materialfiles are now classified asAssetKind::Materialinui_types_common::asset_kind, enabling correct drag-and-drop handling for shader assets. (@tristanpoland)
📚 Documentation
- Added
engine-state-resources.mddocumenting the newengine_stateresource model. (@tristanpoland) - Added a README for
pulsar_auth. (@tristanpoland)
🧰 Developer Experience
- Updated the CI workflow (
ci.yml) and test files (config_test.rs,session_test.rs,ecs_bench.rs) to match theengine_staterefactor. (@tristanpoland)
🧹 Internal
- Removed obsolete files and directories (
blueprint_graph_debug.json,pulsar-host-data/,projects.json) and movedpre-release-check.sh/.ps1intotools/. (@tristanpoland) - Deleted
pulsar_graph::type_system(422 lines) and the relatedprefabtype-system helpers, superseded by the reflection-basedAssetIndex/UserTypeRegistry. (@tristanpoland)
Release v0.2.31
Container Images
Pull the sidecar images for this release from the GitHub Container Registry:
docker pull ghcr.io/far-beyond-pulsar/pulsar-relay:v0.2.31
| Image | Package page | Tags |
|---|---|---|
pulsar-relay |
ghcr.io/far-beyond-pulsar/pulsar-relay | v0.2.31, latest |
✨ Highlights
Warning
Breaking change: ComponentRuntimeContext trait stripped of all domain-specific methods. Implementors must migrate to Subsystems registry. See 🔧 Breaking below.
Important
Mesh visibility bug fixed: Editor was clearing all scene objects every frame, causing helio's remove_object to cascade-free mesh/material GPU handles (ref_count hits zero → resources destroyed). Objects appeared in the scene but with null/invalid geometry. Introduced SceneObjectCache for incremental per-instance updates: objects with unchanged meshes get their transform updated in-place; only new or changed meshes trigger insert/remove.
-
Refactored
ComponentRuntimeContextto eliminate all domain knowledge — stripped 5+ subsystem-specific methods (renderer_mut(),sync_mesh_object(),load_mesh_file(),mark_live(), etc.) from the context trait. The trait now exposes onlysubsystems_mut(),project_root(), andreport_error(). All domain access goes through the type-erasedSubsystemsregistry viaget_subsystem!macro. -
Components are now fully self-contained silos — each component (StaticMesh, Light, Script) parses its own data, resolves asset paths, loads meshes, manages GPU uploads, and writes directly to the renderer through the subsystem registry. Central systems (editor, game loader) have zero knowledge of what any component does.
🚀 Features
-
Subsystemstype-erased registry (pulsar_reflection) — stores subsystems byTypeIdwith both owned (Box<dyn Any>) and borrowed (*mut ()) variants. Includesget_subsystem!macro that panics with the subsystem name if unregistered. -
MeshCache(pulsar_rendering::subsystems) — persists GPU-uploaded mesh geometry across frames so components don't re-load and re-upload the same.fbxfile every sync pass. -
SceneObjectCache(pulsar_rendering::subsystems) — tracks scene object instances by scene-object ID for incremental updates, eliminating the clear-all-objects-per-frame pattern that caused cascade-free. -
LiveKeySet(pulsar_reflection) —HashSet<String>wrapper for stale-cleanup tracking. Components mark themselves live during the sync pass; central systems cull stale entries afterward. -
resolve_asset_path()+load_mesh_upload()(pulsar_rendering::subsystems) — ported from dead code inpulsar_scene::loaderandengine_backend::rendererinto shared free functions so all components use the same resolution logic (absolute → project-root → cwd → cwd/assets → engine built-ins).
🛠️ Improvements
-
Component isolation:
StaticMeshComponentnow internally resolves the mesh asset path, checksMeshCache, loads + uploads on miss, and consultsSceneObjectCachefor incremental insert/update — all without any help from the context.LightComponentgets the renderer viaget_subsystem!instead ofcontext.renderer_mut().ScriptComponentusesLiveKeySetinstead ofcontext.mark_live(). -
Dead code removed:
load_fbx,resolve_asset,default_material,embedded_primitive,prim_bytes!frompulsar_scene::loader;make_material,resolve_mesh_path,load_fbx_meshfromengine_backend::renderer. All of that logic now lives inside the components or inpulsar_rendering::subsystems. -
Fixed
project_root()in editor — was returningPath::new(""), causing all editor asset resolution to fail. Now usesengine_state::get_project_path()withcurrent_dir()fallback.
Note
The Path::new("") bug meant any mesh path got resolved relative to the empty string (i.e., the current working directory of the editor process), which was /Users/tristanpoland/Documents/GitHub/Pulsar-Native (the engine repo), not the user's project. Pulsar-Native assets like SM_Cube.fbx happened to work because resolve_asset_path fell through to the cwd/assets/ check. Project-specific assets like monkey.fbx failed until this was fixed.
- Reduced log spam — removed verbose
[RAP],[SMC], and per-frame component timing traces that were added during debugging.
📚 Documentation
docs/subsystems-architecture.md— documents the full architecture: howSubsystems,get_subsystem!,LiveKeySet,MeshCache, andSceneObjectCachefit together, with diagrams and component contract descriptions.
🔧 Breaking
Caution
ComponentRuntimeContext trait trimmed — The following methods are removed:
renderer_mut()→ useget_subsystem!(context, Renderer)sync_mesh_object()→ useSceneObjectCache+ direct scene writesload_mesh_file()→ useresolve_asset_path()+load_mesh_upload()mark_live()→ useget_subsystem!(context, LiveKeySet).insert()
Architectural goal
The entire point of this change is that central systems (editor, game loader)
must have zero knowledge of component internals. Every replacement above
is component-side code — the central system never imports or touches it.
What central systems know:
| Item | What it knows | Why |
|---|---|---|
Subsystems |
The registry type (store by TypeId, retrieve by TypeId) |
Necessary to pass subsystems through to components |
MeshCache |
It exists, must be registered before sync pass | Cache is frame-persistent, central owns its lifetime |
SceneObjectCache |
It exists, must be registered before sync pass | Cache is frame-persistent, central owns its lifetime |
LiveKeySet |
It exists, must be registered before sync pass; read after pass for stale-cleanup | Central needs to cull stale entries |
Renderer |
Opaque &mut pointer passed through to components via register_ref() |
Central owns the GPU context lifetime |
What central systems do NOT know:
- How mesh assets are resolved (
resolve_asset_path) — that's a free function inpulsar_rendering::subsystemscalled only by components - How mesh files are loaded (
load_mesh_upload) — component-side only - How
SceneObjectCacheentries are created/updated/queried — component-side only - How
LiveKeySetentries are inserted — component-side only - What any specific component's data or behavior is — components register themselves via
inventory::submit!, central just iterates the list
The central system's job is now just:
- Create subsystems (
MeshCache,SceneObjectCache,LiveKeySet) - Register them (along with
Renderer) intoSubsystems - Call
apply_runtime_behavior_for_class(...)for each component instance - After the sync pass, cull stale entries from
SceneObjectCacheandSCRIPT_REGISTRYusingLiveKeySet
That's it. No mesh loading, no asset resolution, no object insertion logic,
no script lifecycle management — all of that lives in the components.
Migration guide
Implementors must register required subsystems (Renderer, MeshCache, SceneObjectCache, LiveKeySet) before the component sync pass. The Subsystems registry accepts both owned values (register()) and borrowed references (register_ref()). Calling get_subsystem! for an unregistered type will panic with the type name.





