-
-
Notifications
You must be signed in to change notification settings - Fork 845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
core: Simplifiy goto logic and use ratio field #5431
base: master
Are you sure you want to change the base?
Conversation
d125945
to
e770fe5
Compare
This also fixes the last bit of #815. 👍 There are also regressions unfortunately: Crystal Island Missile Game 3D
|
Thanks for the helpful testing, as always! Pushed a fix for these cases (was mistakenly checking the parent: |
This fix changed everything (there were issues in other games that I haven't mentioned yesterday and they're now fixed). 👍 I've made more testings today and I only observed one issue/regression which affects a Steppenwolf game (for a change 🙃): the character gets stuck forever after you use a ladder (she still reacts to keyboard inputs but acts as if she was stuck in an invisible wall). Steppenwolf 1-2
|
We should add TAS playthroughs of the Steppenwolf games as automated tests someday after we add input recording+playback 😆 |
This was a bug when goto-ing one frame past the total # of frames in the movieclip -- should be fixed now. |
Everything looks good to me. 👍 |
I'm a little hesitant to pull the trigger on this as the goto simplification affects performance quite a bit, so I think I'll split it into two PRs: one doing the |
GotoPlaceObject
andgoto_*
machinery inMovieClip
.place_frame
, and instead useratio
field to determine if a display object survives a rewinding goto (fix Handle PlaceObject ratio field for non-morph shapes #1291).Simplified goto
Previously when seeking, Ruffle would aggregate the frame-by-frame changes into a final delta, and then execute the final delta. This avoided creating objects on the intermediate frames only to destroy them immediately if they didn't exist on the final frame.
Unfortunately, this is too clever, because Flash seems to actually instantiate these transient display objects while seeking. This can be observed in a few ways:
instance1
will increase toinstance4
even if only one new object appears on the final frame. (example)Nested clips within the transient objects also affect the above, so I don't think there's a clever way to cheat this. We need to do the "dumb" way and step through the frames normally, instantiating and destroying objects as we go. The upside is that this removes all of the complicated logic to fold the PlaceObject tags into a final delta (
GotoPlaceObject
, etc.). The downside is that this will certainly be slower, requiring more allocations and GC churn.Use the
ratio
field during rewinding gotosBecause Flash timelines are stored as deltas from frame to frame, rewinding to a previous frame requires clearing everything, restarting from frame 1, and stepping forward. However, any object that was created before the target frame should "survive" the goto and be reused, not re-created.
Previously, every display object stored a
place_frame
indicating the frame that the object was created on. When rewinding, an object would survive ifplace_frame
was <= the frame we are seeking to.However, #1291 shows that Flash uses the
ratio
field of thePlaceObject
tag to handle this. If a depth is occupied both before and after a rewind, the ratio field is used to decide whether the two objects are the "same". If the ratios are equal, the original object should survive the goto and be reused instead of the new object.The Flash IDE always exports SWFs with the object's creation frame stored in the
ratio
field, which matches our currentplace_frame
behavior, so this hasn't been much of an issue. But the ratio can be any arbitrary value, and this behavior can be observed in AVM2 by storing a reference to the object before the seek, and comparing it afterwards(example). 3rd party tools also export SWFs with weirdo ratio fields (#1060).
This PR removes
DisplayObject::place_frame
in favor ofDisplayObject::ratio
. The downside is that we can't know if a display object will survive the rewind until after the rewind is finished, because we have to compare it to a potential new object at the same depth. The survival check compares multiple properties in addition to the ratio depending on the display object type. From experimentation, Flash seems to have three categories:This check is handled by
DisplayObject::survives_rewind
.Which properties must match in order to survive a rewind, in table form:
(example)
Fixes #1060, #1291.
Supersedes #1305.
TODO:
SEEKING
flag to disable AVM2 events firing while a clip is seeking, and then manually firing the events in the proper order when the seek is complete.PlaceObject
tag (such asname
) should only apply to a newly instantiated object (PlaceObjectAction::Place
, notPlaceObjectAction::Modify
). This will be for a future PR.