-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix warped threads' event threads not inheriting their parents' warp state/timer #4035
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
base: develop
Are you sure you want to change the base?
Conversation
(Note: this should be clear since I'm the one who opened that feature request in the first place, but I am not a member of the Scratch Team! Just another outside, open-source contributor.) This is a very interesting pull request and I hope it gains some traction now that the feature request has been implemented. I'm impressed at how relatively minimal the changes seem to be (haven't gone over them closely yet, myself). I just want to let you know, though, that the Scratch Team is historically quite resistant to changes to runtime behavior (in other words, changes to behavior of the programming language itself). In one part, this is out of concern for existing projects — we obviously cannot make any changes which cause existing projects to fail! Another issue, though, is that some of the people initially responsible for VM implementation are not active in the development team anymore, and those who still are are focused on other projects. Hence the very limited changes to the programming language over the last four years. I don't believe the first part should be much of an issue. The previous behavior is distinctly "broken" behavior, causing programs to flat-out hang and cause 500 millisecond delays, not just between running the next "step" of a program, but between even processing user input, like clicking the stop sign. Frankly, I think this is a very bad issue and disruptive to anyone who experiments with "run without screen refresh" custom blocks. We can help avoid it by implementing and merging this pull request. The second part is harder to address, though. There just isn't as much knowledge and experience with the Scratch language to go around as there used to be, especially fresh knowledge. Would you be able to describe your changes in more detail? Can you explain how the system behaved before, and what you changed to make it work as it does now? Going into detail goes a long way to helping reviewers on the Scratch Team help ensure your changes make sense and won't have unexpected side effects. I also believe this kind of behavior pull request should include test cases, to ensure both that the new behavior is working as expected, and that any related old behavior (surrounding broadcasts) hasn't been broken. If this isn't something you're interested in exploring (i.e. learning how the test systems work), then I'd be willing to do that work and contribute test cases after you share a more detailed description of the behavior changes in this PR. Up to you! Would probably help get this reviewed and merged, though. |
@cwillisf @kchadha Pulling you two in because it would be a shame for this PR to rot. Is there anything blocking the merge? I understand there are imperfections as mentioned by @towerofnix, but are these issues blocking? |
I've added a test to make sure no regressions occur later on. I observed that in a warped thread, the previous implementation of the wait blocks caused the broadcast thread to complete at least one step before the parent thread completes. Now, both threads will complete within the same step. I've also updated my original message to reflect this, and to update the reason for changes. Ready to merge, though any feedback is appreciated. |
Notes on Instant BroadcastsIntroductionHi, I'm the creator of the feature request: "Better sprite communication: "Broadcast and wait" should run instantaneously inside no-refresh custom blocks", which this pull request by open source contributor Sanae6 addresses. I've been programming with Scratch for over ten years and have contributed many pull requests which were merged over the course of Scratch 3.0's development, always taking care to ensure and maintain compatibility with old Scratch projects. I am passionate about teaching programming with Scratch and I want to ensure it has the best tools available to lower the floor so that programming is approachable for anyone, and to raise the ceiling so that students can learn and practice "real world" programming habits and experiences inside Scratch. I have created seven Scratch projects which test the behavior introduced in this pull request, aiming to see if it is as powerful and predictable as intended, and doesn't introduce any new problematic behavior. In a few words, yes, it is powerful and predictable, and no, it does not introduce problematic behavior. Because this pull request offers so much for teachers and learners who want to experiment with OOP in Scratch and other common programming patterns that make use of instantaneous communications between different sprites, I would deeply appreciate if it, along with my notes, could be brought to a team conversation discussing the possibility of merging this and making these new capabilities official and available for everyone. Thank you for your time and consideration. Summary of ObservationsIn summary, this pull request changes the behavior of the "broadcast and wait" block. In current Scratch, it causes all corresponding "when I receive" blocks in the same sprite as well as other sprites to be completed all the way, and only then returns control to the blocks that come after "broadcast and wait". Due to a quirk in the Scratch runtime, if a Scratcher ever puts "broadcast and wait" inside a custom block which has been "run without screen refresh", the entire Scratch editor will lock up, which is a very negative experience. In addition to avoiding this surprising and shocking "editor lock-up" behavior, this pull request offers new behavior which is useful to learners and teachers alike. It makes "broadcast and wait" inside "run without screen refresh" run all of its receiving scripts to completion, just like when outside a custom block, but all instantaneously - just like "run without screen refresh" is supposed to. This replaces a currently existing negative experience with a wealth of learning possibilities. I would recommend everyone interested in improving the Scratch experience for learners to read the sections describing my first two projects, Basic Warp Broadcasts and Speedy Broadcast Salad!. These respectively demonstrate the behavior of "warp" broadcasts and interacitvely show off real-world use cases for them. They demonstrate that the new behavior in this pull request works as it's supposed to in a predictable way, and explain why it would be a valuable addition for students, teachers, and enthusiastic learners of programming. The remaining four projects approach the pull request in a more critical lens, giving it "harder" behavior to ensure nothing is newly broken or problematic. Fast Broadcast and Wait OOP Demo is a rework of an existing project which shows that while some of the behavior added in this PR can technically be "worked around" in current Scratch, it's extremely convoluted and not friendly to new learners at all. The updated version, while less interesting of a demo than Speedy Broadcast Salad, is much easier to walk through and understand. Recursion Demo confirms that proper broadcast recursion works now that all recursion will complete within one frame — an important capability in many programming patterns for learners. The projects Recursion Bomb and Delay Timing both attempt to "lock up" the Scratch editor, attempting to see if a defense mechanism existing in current Scratch continues to apply correctly and predictably (it does) without limiting the capabilities of Scratch projects that require a greater amount of computational work (it doesn't). The final project, Timing When I Start As a Clone, observes an interaction between cloning blocks and "warp" broadcasts. A disclosure: Although I'm experienced working with the internals of the Scratch editor and the For convenience, all discussed projects (SB3 files) are included in this archive: Instant Broadcast Projects.zip Project Descriptions and ObservationsBasic Warp BroadcastsThis project demonstrates the basics of "warp" broadcasts. A custom block, "my cool script" broadcasts two broadcasts in one order or another depending on its input. The "when I receive" scripts add a word to a variable (banana or orange) and animate drawing a line on the screen (starting from the current position and moving right or up). The project runs the custom block once with "message1" then "message2", and again with "message2" then "message1". This shows the broadcasts behaving like normal, making the variable "apple banana orange" then "apple orange banana", and animating two opposing right angles. Then the project runs another custom block. This one is marked "run without screen refresh", and simply performs the custom block described first instantaneously. The project repeats the same steps as before with this new custom block. It results in the same variable results and right angles, but this time there is no animation for the lines: they're drawn instantaneously, and only the finished angle is made visible. The goal of this project is to show off the basic expected behavior of "warp" broadcasts. In particular, it demonstrates how "run without screen refresh" (warp mode) is inherited the same way across custom blocks and broadcasts. The second custom block activates warp mode; the first custom block inherits it, so triggers its broadcasts (message1 and message2) in warp mode also; each of these receivers changes a variable then sends a further broadcast to perform the actual line animation, these because these receivers are in warp mode, they in turn broadcast in warp mode; then the final receivers (right and up) perform the "repeat 10" animation in warp mode. The consistent behavior across custom blocks and broadcasts makes it easier to teach and understand the "run without screen refresh" concept. It also makes it intuitive to use, because you only have to learn the behavior once and don't have to keep track that broadcasts behave differently from custom blocks. It's logical for broadcasts and custom blocks to behave similarly because they have very similar functions. Both cause another script to activate, returning control to the "parent" script when the "child" script has finished running all of its blocks. In current and previous versions of Scratch, users don't get to take this for granted. Putting a "broadcast and wait" block inside a custom block causes the entire Scratch editor or player (including, for viewers, the rest of the Scratch project page) to lock up and become un-interactive. Scratch handles user input only twice per second and everything else freezes: you have to click the stop sign and it takes time for the editor to respond. This is obviously a very challenging behavior for the Scratch editor to exhibit in response to any user action, putting the user off of experimenting with things they don't understand (they may not be able to figure out what went wrong!). There's room for improvement here, and the proposed behavior makes broadcasts and custom blocks, which already have similar results, handle "run without screen refresh" gracefully and in a powerful way too. Speedy Broadcast Salad!This is an interactive demo for a practical use of "warp" broadcasting. I won't walk through every block in this project because it's something you should interact with yourself to get the full picture, but in summary: The user chooses whether to run the project with "speedy" or regular broadcasts, then is instructed to click on several of a variety of characters (clones), each carrying its own secret ingredient. Once several are chosen, the main sprite uses broadcasts to collect each clone's ingredient and makes a salad out of them to share with the user. This project shows off "warp" broadcasts in two ways. First, it broadcasts a "prepare clones" message in either warp mode or not. The receiver (in another sprite) creates 10 clones of itself along the top edge of the stage, then hides itself. If the user chose to use "speedy" broadcasts, these all show up immediately; otherwise, they each appear one at a time. There's also one frame where you can see the final sprite "too far" and cut off by the edge of the screen, and it disappears on the next frame. When a clone is clicked (to select its ingredient), it sends a message to the main sprite, who receives only the clone's index, not its ingredient. When the user has finished selecting ingredients, the main sprite takes each of the items in its list of indexes and sends a broadcast to request the ingredient corresponding from the clone who corresponds to that index, adding the returned ingredient to another list of gathered ingredients (which is used for the salad at the end). While doing this, both lists are visible. If the user selected normal broadcasts, they can see items get pulled out of the index list and added to the ingredient list one at a time; using "speedy" broadcasts, all the items disappear from the index list and appear in the ingredient list at once. The rest of the project is just for fun, but also includes using non-warp broadcasts in useful ways, such as sending a message to a button which only returns control to the main script once the button has been clicked, and using another broadcast without "and wait" to make the salad animate appearing and then do something else while that animation is running. The goal of this project is to comprehensively demonstrate how broadcasts work and show a practical use case for "warp" broadcasting. The most important part is how the clones communicate and the main sprite communicate with each other, and each use "for this sprite only" variables or lists to make certain data private, using messages for communication. Clones send a message to tell the main sprite their index has been selected, which gets added to a private list: the clones can't tell what is in the ingredient list so far. (Each clone keeps track of it itself has been selected, and won't send a message to add itself twice.) When gathering the ingredients, the main sprite sends one message for each selected index, never revealing its full list of indexes, and again retaining each returned ingredient in its own list of ingredients. Using an agreed-upon common language (broadcasts and global variables) to access and interact with privately held data (local variables and lists) is a very common practice in many programming languages, especially object oriented programming (OOP), and broadcasts work together with "for this sprite only" data and clones to make a similar and educational analogue in Scratch. However, in current Scratch, its "real world" utility is limited, because a single "broadcast and wait" generally takes a whole frame to complete. This can be manageable if there are only a few objects, but in all sorts of practical uses for OOP - especially games, one of the most common kinds of projects on Scratch - there are often hundreds of objects who may each receive multiple messages within one single frame! Due purely to a technical limitation, these projects cannot make use of common OOP practices in Scratch, and a great opportunity for learning and real-world practice is lost. Merging this pull request would make that practice possible. The remaining examples are more technical, and exist either to further test the behavior of "warp" broadcasts or to determine if the pull request introduces any new problems (such as the editor freezing up or unpredictable behavior). In summary, from my testing, it does not: all behavior is predictable and doesn't introduce any new ways to lock up the editor. As best I can tell, this is a completely safe feature addition. Fast Broadcast and Wait OOP DemoThis project is a remix of one I created two years ago when attempting to figure out if "fast OOP" was feasible in existing Scratch, even if I had to use hacks or complicated workarounds to make it work. I worked with other users on the Scratch discussion forums to identify ways to make "broadcast and wait" not wait a full frame, then created a project which demonstrated some basic hidden data techniques and controlling multiple clones every frame from a main sprite using broadcasts. I don't consider this to be as good as a demo as Speedy Broadcast Salad, but have created an updated version with true "warp" broadcasts for comparison. Compare the code in the old version and the new one to see why "warp" broadcasts are such an appealing possible future! (I'm not aware of any other projects which made use of the solution we found to make "fast broadcasts" work, which is quite understandable for how convoluted it was. It also wasn't without other serious flaws. I explored the idea more in this project - read its comments for a description of a "screen tearing" issue that this approach was prone to.) Recursion DemoThis project tests the essential behavior of recursion within a "warp" broadcast. In current Scratch, broadcasts aren't allowed to trigger themselves the same way that custom blocks are. If you "broadcast message1" from inside "when I receive message1", Scratch will scrap the original execution state of "when I receive" and replace it from a new one, starting again from the top. This is a useful safeguard for Scratch projects which involve animation, because having multiple copies of the same script running simultaneously can be confusing. But recursive functions are a common pattern in real-world programming, and so custom blocks were built from the ground up to support them. This project checks if it is possible to perform recursive broadcasts when all recursion will complete within one frame (thus not posing the confusion of multiple "copies" of a script running at once). It compares the behavior of a normal, non-warp, recursive "broadcast and wait" (which returns control to the main script as soon as the first recursion occurs), and a "warp" recursive broadcast and wait, which completes all recursive execution before returning control. This demonstrates and confirms an important capability for learners. Recursion BombThis project attempts to lock up the Scratch editor by sending a "warp" broadcast which endlessly broadcasts itself again. Current Scratch has a built-in defense against this, in which "run without screen refresh" is essentially revoked, and the web browser is allowed to evaluate any user events that have been sent (such as clicking the stop sign). The goal of this project is to see if this pull request bypasses that defense. It does not: the defense is in full effect. It appears to affect broadcast recursion in a slightly awkward way. The original thread, which initially sent the recursive broadcast, regains control after only a thirtieth of a second. The recursive broadcast, however, continues recursing, now a "runaway" (since the script who sent a "broadcast and wait" isn't keeping track of it anymore). This recursion continues for the remainder of 500 milliseconds (14/30ths of a second), after which warp mode is fully revoked from the "runaway" recursive broadcast, and full control is returned to the user - the broadcast continues recursing but only once per tick/frame, instead of as many times as it can fit into a thirtieth of a second. Because this returns full control after half a second instead of only processing user input twice every second, this is a better experience for the user. It still catches that something has gone wrong (after all an endlessly recursive broadcast that's supposed to finish in one frame is a problem), but shortly returns full control to the user instead of making the editor largely unresponsive until interrupted. Delay TimingThe "Recursion Bomb" project caused concern for work that just takes a while. The goal of this project is to identify what happens when the receiver of a "warp" broadcast takes a long time to finish. This uses a slider variable to experiment with how long an imaginary workload will take to finish (using "days since 2000" to just wait until the specified time passes, avoiding an awkwardness of the "timer" block's interactions with "run without screen refresh" that was retained for compatibility with early Scratch 2.0 projects). Thankfully, long work behaves as expected. Control is not returned to the original thread until the receiver finishes its work, including when nested under another receiver. Every 500 milliseconds, other threads (scripts) get the opportunity to continue evaluating, and user interaction is also processed (e.g. hovering or clicking the stop sign). This means projects which perform a great amount of work, or which are performant on one device but lag on another, won't unexpectedly fail as a result of the original thread continuing despite the receiver thread not yet having finished its work. Timing When I Start As a CloneThis project uses a variety of broadcasts to identify how newly created clones interact with broadcasts, including with "warp" broadcasts. The key takeaway is that "create a clone of" only queues corresponding "when I start as a clone" scripts to be evaluated on the next tick/frame. Until that next tick comes to pass, all values such as "for this sprite only" variables, shown/hidden, and position are inherited from the clone's parent. This means "warp" broadcasts, which are began and completed within the same tick as the rest of the script they're broadcasted from, will be evaluated before "when I start as a clone" if broadcasted within the same tick that the clone was created (or within the next tick by a sprite who is earlier in the evaluation queue, which is controlled by visual layer). This is primarily of concern for project creators who usually set a variable such as "Am I a clone?" or "Clone ID" within the "when I start as a clone" script. It can't be taken for granted as the true first thing to evaluate for a clone when "warp" broadcasts are involved, so projects should take care to update "Am I a clone?" or "Clone ID" within the parent before creating the clone (and restore immediately after). Note that this behavior cannot be avoided. One approach would be to make "create a clone of myself" always immediately run corresponding "when I start as a clone" scripts, but this isn't workable because many projects depend on the existing timing (even if the person who created that project isn't aware). Alternatively, if we were to change introduce immediate "when I start as a clone" exclusively when the "create a clone of" block is positioned in a warp context, it would still be a change of existing behavior. This is because "when I start as a clone" can mutate other global state, not just values contained inside the clone itself, and those changes can be observed by other sprites or scripts earlier in the execution order than "when I start as a clone". This is an unavoidable awkwardness, but I would strongly disagree that it outweighs the overall benefits of "warp" broadcasts, including when paired with clones. I would expect project creators to share awareness that it's better to set initial values to be inherited rather than set under "when I start as a clone" when using "warp" broadcasts - this is a niche behavior but Scratchers are already aware of details to do with script timing, see educational community projects like Why is my sprite lagging? by TheLogFather, Sprite Depth Sorting by griffpatch_tutor, and an older project Script execution order by TheLogFather. In a theoretical world we could introduce a new "create clone of ... and wait" block, which follows the same rules introduced for "broadcast and wait". There's merit to this idea, but it's a limited use case block, and should be focused on at another time. |
Well! Now that would be something :)
Due to the broadcast and wait not functioning well within a "run without
refresh" custom block, it can't be said that affecting their execution
would impact a lot of existing projects which makes this idea quite fun.
I have to say that I do agree with the idea, it makes sense, is something
most people try and are sad to see fail, and it would open up some
fascinating possibilities. A question, would this then allow for
broadcasting and waiting on the same event message multiple times within a
loop in a single screen refresh (no currently possible as you can't queue
the same message multiple times. And if so, what happens if that broadcast
was already queued up before you entered the run without screen refresh,
would it still be queued up to run one more time after the custom block
ended.
Griffpatch
…On Wed, 19 Jul 2023 at 15:58, (quasar) nebula ***@***.***> wrote:
Notes on Instant Broadcasts Introduction
Hi, I'm the creator of the feature request: "Better sprite communication:
"Broadcast and wait" should run instantaneously inside no-refresh custom
blocks", which this pull request by open source contributor Sanae6
addresses. I've been programming with Scratch for over ten years and have
contributed many pull requests which were merged over the course of
Scratch 3.0's development
<https://github.com/search?q=author%3Atowerofnix+org%3Ascratchfoundation++is%3Amerged&type=pullrequests&s=created&o=asc>,
always taking care to ensure and maintain compatibility with old Scratch
projects. I am passionate about teaching programming with Scratch and I
want to ensure it has the best tools available to lower the floor so that
programming is approachable for anyone, and to raise the ceiling so that
students can learn and practice "real world" programming habits and
experiences inside Scratch.
I have created six Scratch projects which test the behavior introduced in
this pull request, aiming to see if it is as powerful and predictable as
intended, and doesn't introduce any new problematic behavior. In a few
words, yes, it is powerful and predictable, and no, it does not introduce
problematic behavior.
Because this pull requests offers so much for teachers and learners who
want to experiment with OOP in Scratch and other common programming
patterns that make use of instantaneous communications between different
sprites, I would deeply appreciate if it, along with my notes, could be
brought to a team conversation discussing the possibility of merging this
and making these new capabilities official and available for everyone.
Thank you for your time and consideration.
Summary of Observations
In summary, this pull request changes the behavior of the "broadcast and
wait" block. In current Scratch, it causes all corresponding "when I
receive" blocks in the same sprite as well as other sprites to be completed
all the way, and only then returns control to the blocks that come after
"broadcast and wait". Due to a quirk in the Scratch runtime, if a Scratcher
ever puts "broadcast and wait" inside a custom block which has been "run
without screen refresh", the entire Scratch editor will lock up, which is a
very negative experience.
In addition to avoiding this surprising and shocking "editor lock-up"
behavior, this pull request offers new behavior which is useful to learners
and teachers alike. It makes "broadcast and wait" inside "run without
screen refresh" run all of its receiving scripts to completion, just like
when outside a custom block, but all instantaneously - just like "run
without screen refresh" is supposed to. This replaces a currently existing
negative experience with a wealth of learning possibilities.
I would recommend everyone interested in improving the Scratch experience
for learners to read the sections describing my first two projects, *Basic
Warp Broadcasts* and *Speedy Broadcast Salad!*. These respectively
demonstrate the behavior of "warp" broadcasts and interacitvely show off
real-world use cases for them. They demonstrate that the new behavior in
this pull request works as it's supposed to in a predictable way, and
explain why it would be a valuable addition for students, teachers, and
enthusiastic learners of programming.
The remaining four projects approach the pull request in a more critical
lens, giving it "harder" behavior to ensure nothing is newly broken or
problematic. *Fast Broadcast and Wait OOP Demo* is a rework of an
existing project which shows that while some of the behavior added in this
PR can technically be "worked around" in current Scratch, it's extremely
convoluted and not friendly to new learners at all. The updated version,
while less interesting of a demo than Speedy Broadcast Salad, is much
easier to walk through and understand. The projects *Recursion Bomb* and *Delay
Timing* both attempt to "lock up" the Scratch editor, attempting to see
if a defense mechanism existing in current Scratch continues to apply
correctly and predictably (it does) without limiting the capabilities of
Scratch projects that require a greater amount of computational work (it
doesn't). The final project, *Timing When I Start As a Clone*, observes
an interaction between cloning blocks and "warp" broadcasts.
A disclosure: Although I'm experienced working with the internals of the
Scratch editor and the scratch-vm codebase in particular, I did not
consult the code of this pull request before creating these projects and
performing these observations. This was so that I wouldn't have my judgment
clouded by familiarity with the code. I did run the code locally, creating
and testing all these projects with the pull request in full effect. Thus,
these notes are not a review of the proposed code changes themselves, but
an observation of their behavior, ensuring that nothing is broken and all
behaves as expected. An engineer on the Scratch Team should still perform a
quality pass across the code changes, but as an observer I can confidently
say that the pull request does what it is supposed to and does not
introduce any problematic behavior to the areas of the Scratch runtime it
affects.
Project Descriptions and Observations Basic Warp Broadcasts
This project demonstrates the basics of "warp" broadcasts. A custom block,
"my cool script" broadcasts two broadcasts in one order or another
depending on its input. The "when I receive" scripts add a word to a
variable (banana or orange) and animate drawing a line on the screen
(starting from the current position and moving right or up). The project
runs the custom block once with "message1" then "message2", and again with
"message2" then "message1". This shows the broadcasts behaving like normal,
making the variable "apple banana orange" then "apple orange banana", and
animating two opposing right angles.
Then the project runs another custom block. This one is marked "run
without screen refresh", and simply performs the custom block described
first instantaneously. The project repeats the same steps as before with
this new custom block. It results in the same variable results and right
angles, but this time there is no animation for the lines: they're drawn
instantaneously, and only the finished angle is made visible.
The goal of this project is to show off the basic expected behavior of
"warp" broadcasts. In particular, it demonstrates how "run without screen
refresh" (warp mode) is inherited the same way across custom blocks and
broadcasts. The second custom block activates warp mode; the first custom
block inherits it, so triggers its broadcasts (message1 and message2) in
warp mode also; each of these receivers changes a variable then sends a
further broadcast to perform the actual line animation, these because these
receivers are in warp mode, they in turn broadcast in warp mode; then the
final receivers (right and up) perform the "repeat 10" animation in warp
mode.
The consistent behavior across custom blocks and broadcasts makes it
easier to teach and understand the "run without screen refresh" concept. It
also makes it intuitive to use, because you only have to learn the behavior
once and don't have to keep track that broadcasts behave differently from
custom blocks.
It's logical for broadcasts and custom blocks to behave similarly because
they have very similar functions. Both cause another script to activate,
returning control to the "parent" script when the "child" script has
finished running all of its blocks.
In current and previous versions of Scratch, users don't get to take this
for granted. Putting a "broadcast and wait" block inside a custom block
causes the entire Scratch editor or player (including, for viewers, the
rest of the Scratch project page) to lock up and become un-interactive.
Scratch handles user input only twice per second and everything else
freezes: you have to click the stop sign and it takes time for the editor
to respond. This is obviously a very challenging behavior for the Scratch
editor to exhibit in response to any user action, putting the user off of
experimenting with things they don't understand (they may not be able to
figure out what went wrong!). There's room for improvement here, and the
proposed behavior makes broadcasts and custom blocks, which already have
similar results, handle "run without screen refresh" gracefully and in a
powerful way too.
Speedy Broadcast Salad!
This is an interactive demo for a practical use of "warp" broadcasting. I
won't walk through every block in this project because it's something you
should interact with yourself to get the full picture, but in summary: The
user chooses whether to run the project with "speedy" or regular
broadcasts, then is instructed to click on several of a variety of
characters (clones), each carrying its own secret ingredient. Once several
are chosen, the main sprite uses broadcasts to collect each clone's
ingredient and makes a salad out of them to share with the user.
This project shows off "warp" broadcasts in two ways. First, it broadcasts
a "prepare clones" message in either warp mode or not. The receiver (in
another sprite) creates 10 clones of itself along the top edge of the
stage, then hides itself. If the user chose to use "speedy" broadcasts,
these all show up immediately; otherwise, they each appear one at a time.
There's also one frame where you can see the final sprite "too far" and cut
off by the edge of the screen, and it disappears on the next frame.
When a clone is clicked (to select its ingredient), it sends a message to
the main sprite, who receives only the clone's index, not its ingredient.
When the user has finished selecting ingredients, the main sprite takes
each of the items in its list of indexes and sends a broadcast to request
the ingredient corresponding from the clone who corresponds to that index,
adding the returned ingredient to another list of gathered ingredients
(which is used for the salad at the end). While doing this, both lists are
visible. If the user selected normal broadcasts, they can see items get
pulled out of the index list and added to the ingredient list one at a
time; using "speedy" broadcasts, all the items disappear from the index
list and appear in the ingredient list at once.
The rest of the project is just for fun, but also includes using non-warp
broadcasts in useful ways, such as sending a message to a button which only
returns control to the main script once the button has been clicked, and
using another broadcast without "and wait" to make the salad animate
appearing and then do something else while that animation is running.
The goal of this project is to comprehensively demonstrate how broadcasts
work and show a practical use case for "warp" broadcasting. The most
important part is how the clones communicate and the main sprite
communicate with each other, and each use "for this sprite only" variables
or lists to make certain data private, using messages for communication.
Clones send a message to tell the main sprite their index has been
selected, which gets added to a private list: the clones can't tell what is
in the ingredient list so far. (Each clone keeps track of it itself has
been selected, and won't send a message to add itself twice.) When
gathering the ingredients, the main sprite sends one message for each
selected index, never revealing its full list of indexes, and again
retaining each returned ingredient in its own list of ingredients.
Using an agreed-upon common language (broadcasts and global variables) to
access and interact with privately held data (local variables and lists) is
a very common practice in many programming languages, especially object
oriented programming (OOP), and broadcasts work together with "for this
sprite only" data and clones to make a similar and educational analogue in
Scratch. However, in current Scratch, its "real world" utility is limited,
because a single "broadcast and wait" generally takes a whole frame to
complete. This can be manageable if there are only a few objects, but in
all sorts of practical uses for OOP - especially games, one of the most
common kinds of projects on Scratch - there are often hundreds of objects
who may each receive multiple messages within one single frame! Due purely
to a technical limitation, these projects cannot make use of common OOP
practices in Scratch, and a great opportunity for learning and real-world
practice is lost. Merging this pull request would make that practice
possible.
*The remaining examples are more technical, and exist either to further
test the behavior of "warp" broadcasts or to determine if the pull request
introduces any new problems (such as the editor freezing up or
unpredictable behavior). In summary, from my testing, it does not: all
behavior is predictable and doesn't introduce any new ways to lock up the
editor. As best I can tell, this is a completely safe feature addition.*
Fast Broadcast and Wait OOP Demo
This project is a remix of one I created two years ago
<https://scratch.mit.edu/projects/538141012/> when attempting to figure
out if "fast OOP" was feasible in existing Scratch, even if I had to use
hacks or complicated workarounds to make it work. I worked with other users
on the Scratch discussion forums to identify ways to make "broadcast and
wait" not wait a full frame, then created a project which demonstrated some
basic hidden data techniques and controlling multiple clones every frame
from a main sprite using broadcasts. I don't consider this to be as good as
a demo as Speedy Broadcast Salad, but have created an updated version with
true "warp" broadcasts for comparison. Compare the code in the old version
and the new one to see why "warp" broadcasts are such an appealing possible
future!
(I'm not aware of any other projects which made use of the solution we
found to make "fast broadcasts" work, which is quite understandable for how
convoluted it was. It also wasn't without other serious flaws. I explored
the idea more in this project
<https://scratch.mit.edu/projects/596883630/> - read its comments for a
description of a "screen tearing" issue that this approach was prone to.)
Recursion Bomb
This project attempts to lock up the Scratch editor by sending a "warp"
broadcast which endlessly broadcasts itself again. Current Scratch has a
built-in defense against this, in which "run without screen refresh" is
essentially revoked, and the web browser is allowed to evaluate any user
events that have been sent (such as clicking the stop sign). The goal of
this project is to see if this pull request bypasses that defense.
It does not: the defense is in full effect. It appears to affect broadcast
recursion in a slightly awkward way. The original thread, which initially
sent the recursive broadcast, regains control after only a thirtieth of a
second. The recursive broadcast, however, continues recursing, now a
"runaway" (since the script who sent a "broadcast and wait" isn't keeping
track of it anymore). This recursion continues for the remainder of 500
milliseconds (14/30ths of a second), after which warp mode is fully revoked
from the "runaway" recursive broadcast, and full control is returned to the
user - the broadcast continues recursing but only once per tick/frame,
instead of as many times as it can fit into a thirtieth of a second.
Because this returns full control after half a second instead of only
processing user input twice every second, this is a better experience for
the user. It still catches that something has gone wrong (after all an
endlessly recursive broadcast that's supposed to finish in one frame is a
problem), but shortly returns full control to the user instead of making
the editor largely unresponsive until interrupted.
Delay Timing
The "Recursion Bomb" project caused concern for work that just takes a
while. The goal of this project is to identify what happens when the
receiver of a "warp" broadcast takes a long time to finish. This uses a
slider variable to experiment with how long an imaginary workload will take
to finish (using "days since 2000" to just wait until the specified time
passes, avoiding an awkwardness of the "timer" block's interactions with
"run without screen refresh" that was retained for compatibility with early
Scratch 2.0 projects).
Thankfully, long work behaves as expected. Control is not returned to the
original thread until the receiver finishes its work, including when nested
under another receiver. Every 500 milliseconds, other threads (scripts) get
the opportunity to continue evaluating, and user interaction is also
processed (e.g. hovering or clicking the stop sign). This means projects
which perform a great amount of work, or which are performant on one device
but lag on another, won't unexpectedly fail as a result of the original
thread continuing despite the receiver thread not yet having finished its
work.
Timing When I Start As a Clone
This project uses a variety of broadcasts to identify how newly created
clones interact with broadcasts, including with "warp" broadcasts. The key
takeaway is that "create a clone of" only queues corresponding "when I
start as a clone" scripts to be evaluated on the next tick/frame. Until
that next tick comes to pass, all values such as "for this sprite only"
variables, shown/hidden, and position are inherited from the clone's
parent. This means "warp" broadcasts, which are began and completed within
the same tick as the rest of the script they're broadcasted from, will be
evaluated before "when I start as a clone" if broadcasted within the same
tick that the clone was created (or within the next tick by a sprite who is
earlier in the evaluation queue, which is controlled by visual layer).
This is primarily of concern for project creators who usually set a
variable such as "Am I a clone?" or "Clone ID" within the "when I start as
a clone" script. It can't be taken for granted as the true first thing to
evaluate for a clone when "warp" broadcasts are involved, so projects
should take care to update "Am I a clone?" or "Clone ID" within the parent
before creating the clone (and restore immediately after).
Note that this behavior cannot be avoided. One approach would be to make
"create a clone of myself" always immediately run corresponding "when I
start as a clone" scripts, but this isn't workable because many projects
depend on the existing timing (even if the person who created that project
isn't aware). Alternatively, if we were to change introduce immediate "when
I start as a clone" exclusively when the "create a clone of" block is
positioned in a warp context, it would still be a change of existing
behavior. This is because "when I start as a clone" can mutate other global
state, not just values contained inside the clone itself, and those changes
can be observed by other sprites or scripts earlier in the execution order
than "when I start as a clone".
This is an unavoidable awkwardness, but I would strongly disagree that it
outweighs the overall benefits of "warp" broadcasts, including when paired
with clones. I would expect project creators to share awareness that it's
better to set initial values to be inherited rather than set under "when I
start as a clone" when using "warp" broadcasts - this is a niche behavior
but Scratchers are already aware of details to do with script timing, see
educational community projects like Why is my sprite lagging?
<https://scratch.mit.edu/projects/105607329/> by TheLogFather, Sprite
Depth Sorting <https://scratch.mit.edu/projects/307388424/> by
griffpatch_tutor, and an older project Script execution order
<https://scratch.mit.edu/projects/18490761/> by TheLogFather.
In a theoretical world we could introduce a new "create clone of ... and
wait" block, which follows the same rules introduced for "broadcast and
wait". There's merit to this idea, but it's a limited use case block, and
should be focused on at another time.
—
Reply to this email directly, view it on GitHub
<#4035 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABTM3PX7OFB2I7VJ4IEKLPTXQ7YY7ANCNFSM6AAAAAA2ACLGEM>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
@griffpatch yes! It does allow you to BaW in a loop. Queued broadcasts will get executed after the loop. You can imagine the BaW in a warp context to act similar to the "call" operator in most other languages. Me and Sanae6 worked on this PR together with that explicit goal. Currently procedure call needs to know the procedure statically, but having a arbitrary call "procedure reference" (broadcast name) if you will allows for some really neat OOP principles, not the least of which are Interfaces, Traits and Class Instances |
I myself think this should be implemented. any and all bugs of course should be fixed, and this seems like a bug. |
Any update on this? |
Resolves
Implements feature request #2834
Proposed Changes
BlockUtility.startHatsAndWait
and add on logic proposed by @towerofnix in (Feature Request) Better sprite communication: "Broadcast and wait" should run instantaneously inside no-refresh custom blocks #2834event.broadcastAndWait
andlooks.switchBackdropAndWait
to use new wait functionReason for Changes
A thread marked for running with no refresh will run in warp mode, and it would be reasonable to expect that its child threads should also run in warp mode.
Test Coverage
Added test which verifies that both the original thread and the broadcast thread will complete in the same step.