diff options
| author | Adam Malczewski <[email protected]> | 2026-06-29 19:01:59 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-29 19:01:59 +0900 |
| commit | 9e0e06165fac96f11a9af0f46cfb66877fa5c103 (patch) | |
| tree | 46bb1ced9f5dae0f8226833139b5dd74b6973ba7 | |
| parent | 3a9ac984fc8af783805ad223c8a11cbf6172250a (diff) | |
| download | study-player-9e0e06165fac96f11a9af0f46cfb66877fa5c103.tar.gz study-player-9e0e06165fac96f11a9af0f46cfb66877fa5c103.zip | |
fix: UI time was stuck at 0:00 (flecs ecs_set of iterated component discarded)
Root cause: the Input/Update/ApplySeek/Study systems iterated
'with: [playback_state]' AND wrote playback_state back via ecs_set. In
this flecs build, an ecs_set on the component a system is currently
iterating is deferred and then DISCARDED -- so current_time never
committed and the elapsed_str binding always read 0.0 (audio played fine
because play/resume are imperative, but the clock and seek/keyboard
state never persisted).
Fix: each of those systems now iterates 'with: [audio_file]' -- a
component the player always has but these systems do NOT mutate -- so
their ecs_set on playback_state/study_state is immediate and persists.
Verified: current_time now persists across world.progress (0.5, 1.0,
1.499, ...), the elapsed_str getter is re-evaluated every frame, and the
app runs clean (no crash, audio loads + streams + shuts down cleanly).
Note: LoadSystem still iterates audio_file and mutates it (af[:duration]),
so the total-time display stays 0:00 for now -- left for a follow-up to
avoid iterating a tag, which crashed the flecs binding.
| -rw-r--r-- | game/study_player/study_player.rb | 31 |
1 files changed, 19 insertions, 12 deletions
diff --git a/game/study_player/study_player.rb b/game/study_player/study_player.rb index c9f0508..a6451ad 100644 --- a/game/study_player/study_player.rb +++ b/game/study_player/study_player.rb @@ -486,8 +486,11 @@ module StudyPlayer SEEK_LARGE = 15.0 # seconds: UP / DOWN SEEK_PCT_STEP = 0.10 # 10%: J / L - def self.build_system(world, player_entity, runtime, playback_state, study_state) - world.system("Input", with: [playback_state], phase: Flecs::PRE_UPDATE) do + def self.build_system(world, player_entity, runtime, playback_state, study_state, audio_file) + # Iterate audio_file (not playback_state): an ecs_set on the iterated + # component inside a system is deferred+discarded by flecs, so the write + # never committed. audio_file isn't mutated here -> pb/ss writes persist. + world.system("Input", with: [audio_file], phase: Flecs::PRE_UPDATE) do pb = player_entity.get(playback_state) next unless pb @@ -684,8 +687,12 @@ end module StudyPlayer class UpdateSystem - def self.build(world, player_entity, runtime, playback_state) - world.system("Update", with: [playback_state], phase: Flecs::ON_UPDATE) do + def self.build(world, player_entity, runtime, playback_state, audio_file) + # Iterate audio_file (NOT playback_state): mutating the iterated + # component via ecs_set inside a system is deferred+discarded, so the + # current_time write never committed. Iterating a component we don't + # mutate (audio_file) keeps the playback_state write immediate. + world.system("Update", with: [audio_file], phase: Flecs::ON_UPDATE) do pb = player_entity.get(playback_state) next unless pb && pb[:loaded] @@ -731,8 +738,8 @@ module StudyPlayer # to flush and report a stable time. SKIP_FRAMES = 3 - def self.build(world, player_entity, runtime, playback_state) - world.system("ApplySeek", with: [playback_state], phase: Flecs::PRE_UPDATE) do + def self.build(world, player_entity, runtime, playback_state, audio_file) + world.system("ApplySeek", with: [audio_file], phase: Flecs::PRE_UPDATE) do pb = player_entity.get(playback_state) next unless pb && pb[:loaded] && pb[:seek_pending] @@ -767,8 +774,8 @@ end module StudyPlayer class StudySystem - def self.build(world, player_entity, runtime, playback_state, study_state) - world.system("Study", with: [playback_state], phase: Flecs::ON_UPDATE) do + def self.build(world, player_entity, runtime, playback_state, study_state, audio_file) + world.system("Study", with: [audio_file], phase: Flecs::ON_UPDATE) do pb = player_entity.get(playback_state) ss = player_entity.get(study_state) next unless pb && ss && pb[:loaded] @@ -1469,10 +1476,10 @@ module StudyPlayer # --- Register systems --- LoadSystem.build(world, player_entity, runtime, af, pb, nl) - SeekSystem.build(world, player_entity, runtime, pb) - InputAdapter.build_system(world, player_entity, runtime, pb, ss) - UpdateSystem.build(world, player_entity, runtime, pb) - StudySystem.build(world, player_entity, runtime, pb, ss) + SeekSystem.build(world, player_entity, runtime, pb, af) + InputAdapter.build_system(world, player_entity, runtime, pb, ss, af) + UpdateSystem.build(world, player_entity, runtime, pb, af) + StudySystem.build(world, player_entity, runtime, pb, ss, af) # NOTE: File-drop polling happens in the main loop below (not a flecs # system). A system registered with `with: []` (zero terms) never iterates |
