summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-29 19:01:59 +0900
committerAdam Malczewski <[email protected]>2026-06-29 19:01:59 +0900
commit9e0e06165fac96f11a9af0f46cfb66877fa5c103 (patch)
tree46bb1ced9f5dae0f8226833139b5dd74b6973ba7
parent3a9ac984fc8af783805ad223c8a11cbf6172250a (diff)
downloadstudy-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.rb31
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