Skip to content

Releases: Far-Beyond-Pulsar/Pulsar-Native

Release v0.2.42

Choose a tag to compare

@github-actions github-actions released this 11 Jul 16:51

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_fs usage 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.rs module created for plugin management. Thanks @tristanpoland!
  • Updates to plugin.rs and lib.rs to 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 .dockerignore and config.toml for 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

Choose a tag to compare

@github-actions github-actions released this 03 Jul 14:14

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.toml for 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.rs and onboarding.rs for internal code improvements. Thanks @tristanpoland!

Release v0.2.40

Choose a tag to compare

@github-actions github-actions released this 27 Jun 23:58

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

Choose a tag to compare

@github-actions github-actions released this 27 Jun 21:53

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

Screenshot 1 Screenshot 2
Screenshot 3 Screenshot 4
Screenshot 5 Screenshot 6
---

🧩 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 ComponentFactory closures returning Box<dyn EngineClass>
  • Full reflection pipeline: component_registry.create_instance(name)instance.get_properties() → serialized via RUNTIME_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.rs1,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 via rustup / 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:

  1. First launch → !has_seen_intro() → embedded OOBE renders fullscreen in entry window
  2. User completes/skips OOBE → IntroComplete emitted → subscriber sets intro_screen = None, show_onboarding = true, calls mark_intro_seen()
  3. If deps are missing → onboarding overlay shows automatically
  4. 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...
Read more

Release v0.2.38

Choose a tag to compare

@github-actions github-actions released this 19 Jun 17:47

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

Choose a tag to compare

@github-actions github-actions released this 19 Jun 16:45

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

Choose a tag to compare

@github-actions github-actions released this 16 Jun 04:45

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.rs with 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

Release v0.2.34

Choose a tag to compare

@github-actions github-actions released this 15 Jun 04:14

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

image image image image image

✨ 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.rs for internal consistency. Thanks, @tristanpoland!
  • Forced release to ensure deployment consistency. Kudos to @tristanpoland!

Release v0.2.33

Choose a tag to compare

@github-actions github-actions released this 14 Jun 05:20

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_db crate with new in-memory AssetIndex and UserTypeRegistry systems in engine_fs, backed by pulsar_reflection. (@tristanpoland)
  • Migrated ad-hoc global state (script registry, agent chat tool registries, fab search cache, multiuser avatar cache, and eight EngineContext fields) onto the unified engine_state::StateStore. (@tristanpoland)
  • Added two new built-in editor plugins: a visual Shader Graph editor for .material assets and a Table Editor for SQLite .db/.sqlite/.sqlite3 databases. (@tristanpoland)
  • Replaced the blueprint_compiler wrapper crate with pbgc directly, and patched pbgc to 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 .material shader assets, including AI tool integration for graph inspection. (@tristanpoland)
  • Added a built-in Table Editor plugin for browsing and editing SQLite .db/.sqlite/.sqlite3 databases, with a table browser, query editor, and data view panel. (@tristanpoland)

🛠️ Improvements

  • Introduced AssetIndex and UserTypeRegistry in engine_fs (new asset_index.rs and user_types.rs), and reworked EngineFs, operations, scanner, and watchers to use them in place of type_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 new init_task! macro, shrinking the inline setup in main.rs from ~284 lines. (@tristanpoland)
  • Added engine_state::resource::WriteGuard for RAII mutable access with automatic change notification, and converted plugin_manager to parking_lot::RwLock, removing manual .unwrap() calls. (@tristanpoland)
  • Consolidated the standalone macros crate into pulsar_macros. (@tristanpoland)

🐛 Bug Fixes

  • Fixed pure-node bytecode caching in PBGC by switching the pbgc dependency 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::context and engine_state::discord, including unused window-id tracking, multiplayer status methods, and a leftover migration module. (@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)
  • .material files are now classified as AssetKind::Material in ui_types_common::asset_kind, enabling correct drag-and-drop handling for shader assets. (@tristanpoland)

📚 Documentation

  • Added engine-state-resources.md documenting the new engine_state resource 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 the engine_state refactor. (@tristanpoland)

🧹 Internal

  • Removed obsolete files and directories (blueprint_graph_debug.json, pulsar-host-data/, projects.json) and moved pre-release-check.sh/.ps1 into tools/. (@tristanpoland)
  • Deleted pulsar_graph::type_system (422 lines) and the related prefab type-system helpers, superseded by the reflection-based AssetIndex/UserTypeRegistry. (@tristanpoland)

Release v0.2.31

Choose a tag to compare

@github-actions github-actions released this 11 Jun 03:59

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 ComponentRuntimeContext to 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 only subsystems_mut(), project_root(), and report_error(). All domain access goes through the type-erased Subsystems registry via get_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

  • Subsystems type-erased registry (pulsar_reflection) — stores subsystems by TypeId with both owned (Box<dyn Any>) and borrowed (*mut ()) variants. Includes get_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 .fbx file 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 in pulsar_scene::loader and engine_backend::renderer into shared free functions so all components use the same resolution logic (absolute → project-root → cwd → cwd/assets → engine built-ins).

🛠️ Improvements

  • Component isolation: StaticMeshComponent now internally resolves the mesh asset path, checks MeshCache, loads + uploads on miss, and consults SceneObjectCache for incremental insert/update — all without any help from the context. LightComponent gets the renderer via get_subsystem! instead of context.renderer_mut(). ScriptComponent uses LiveKeySet instead of context.mark_live().

  • Dead code removed: load_fbx, resolve_asset, default_material, embedded_primitive, prim_bytes! from pulsar_scene::loader; make_material, resolve_mesh_path, load_fbx_mesh from engine_backend::renderer. All of that logic now lives inside the components or in pulsar_rendering::subsystems.

  • Fixed project_root() in editor — was returning Path::new(""), causing all editor asset resolution to fail. Now uses engine_state::get_project_path() with current_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: how Subsystems, get_subsystem!, LiveKeySet, MeshCache, and SceneObjectCache fit together, with diagrams and component contract descriptions.

🔧 Breaking

Caution

ComponentRuntimeContext trait trimmed — The following methods are removed:

  • renderer_mut() → use get_subsystem!(context, Renderer)
  • sync_mesh_object() → use SceneObjectCache + direct scene writes
  • load_mesh_file() → use resolve_asset_path() + load_mesh_upload()
  • mark_live() → use get_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 in pulsar_rendering::subsystems called only by components
  • How mesh files are loaded (load_mesh_upload) — component-side only
  • How SceneObjectCache entries are created/updated/queried — component-side only
  • How LiveKeySet entries 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:

  1. Create subsystems (MeshCache, SceneObjectCache, LiveKeySet)
  2. Register them (along with Renderer) into Subsystems
  3. Call apply_runtime_behavior_for_class(...) for each component instance
  4. After the sync pass, cull stale entries from SceneObjectCache and SCRIPT_REGISTRY using LiveKeySet

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.