Neos-Metaverse / NeosPublic

A public issue/wiki only repository for the NeosVR project
195 stars 9 forks source link

Cancellable delays for LogiX #2325

Open RobertBaruch opened 3 years ago

RobertBaruch commented 3 years ago

Is your feature request related to a problem? Please describe.

Imagine an arcade game with an attract screen. The attract screen continuously runs until someone puts in a quarter, at which point the attract screen stops and some other process takes over. Or, imagine a reset button which stops the attract screen and restarts it.

The attract screen necessarily involves delays. For example, printing a string with delays between each character.

This shows an attract screen that I built.

Relevant issues

Describe the solution you'd like

A Cancellable Delay node would be nice. It would work like Delay, except there would be three inputs: Trigger, Cancel, and Delay.

image

Alternatively, Delay nodes could also output a DelayID that uniquely identifies that delay. Another node, Cancel Delay, would take a Trigger impulse and a DelayID and output a Done impulse.

image

Describe alternatives you've considered

I could just insert some logix after every delay to prevent the output from being triggered if it was cancelled. Say we use a bool. This doesn't work when you want to restart the delay, because you'd have to reset the bool, and that allows the previous delay to escape.

I did try something based on random GUIDs and delay with value, since that emulates unique delay instances. Only if the value from the delay is equal to the current GUID is the delay ok to proceed. The problem is, that's a lot of logix to add in for every delay scattered throughout the code.

20210522193617_1

I also tried deleting the slot with the delay, but apparently there is some delay inherent in deleting slots, since I found that the slot's logix continued for another second or so before stopping.

Additional context

Requesting User

Xekri

RobertBaruch commented 3 years ago

Based on the same idea, here's a resetting delay, where pulsing a delay will "remove" anything in the delay's queue.

20210528134134_1

H3BO3 commented 3 years ago

Delay is local, so this feature would work for individual users only.

ProbablePrime commented 3 years ago

Thanks for your suggestion, I had some followup questions:

For your Arcade Game screen, I've previously made some solutions that don't explicitly need cancellable delays. In this case i'd check on the output of Delay to see if we still needed to run the attrack operation the delay was waiting for. This can be done by perhaps checking a game state variable to see if the game was still in the "idle" state.

A state variable for games, screens and objects is something I find myself doing a lot.

RobertBaruch commented 3 years ago

Let me answer the last question first:

In this case i'd check on the output of Delay to see if we still needed to run the attrack operation the delay was waiting for. This can be done by perhaps checking a game state variable...

This wouldn't help if you cancel the attract screen and then start it again within the delay period. In that case the game state variable would be, say, reset but then set shortly thereafter. When the initial delay expires, because it has not been cancelled, it will go through, incorrectly.

Now for the other questions:

How should multiple sequential delays be handled?

For my use-case, it would be sufficient to cancel all queued delays that were initiated from that node.

For your Delay ID suggestion, how should multiple delay ids be handled? We don't currently have collections to store and process them.

In retrospect, it's not clear that this alternative suggestion is viable. Just being able to cancel all delays from the node is sufficient. I think the use-case for cancelling some delays but not others may be very rare and could probably be solved through other means.

RobertBaruch commented 3 years ago

Here's another mechanism for cancellable delays. The basic idea is to copy the slot for the logix of a template delay to a new slot, and then trigger it. On cancel, delete the slot. Sounds simple, but it is not, because now you have to specify to that slot where to go after the delay is done. And if that happens to be something that includes a cancellable delay, then it has to be something that is also copied. Thus:

Here's the cancellable delay template. It is triggered when the Start message is received, and cancelled when the CancelProcess message is received. The delay itself has to be passed in via dynvar. The reason we pass a slot in with the Start message is so that the logix knows "itself" so that it can be deleted when done or cancelled.

Photo1

Here's how we can start such a "process". Upon receipt of a StartProcess message with a slot containing the template of the "process" to start, we copy it to the "Processes" slot, then issue the Start message to it.

Photo2

When the "process" "exits" normally, it sends the ProcessReturn message with itself. This does a standard thing, reading the "return" message along with which slot should get that message (because that slot could be another process). Once that message is sent, the "process" is deleted.

Photo3

If the "process" is cancelled, though, we just delete it.

Photo4

Finally, here's how it looks like when we want to start up a cancellable delay. You have to write the return message and slot, and the delay, then "call" StartProcess.

Photo5

This is a LOT of work and is prone to getting things wrong because of all the manual steps involved.

In contrast, I hope that having a cancellable delay node would obviate the necessity of keeping track of cancellable "processes".

bentallea commented 3 years ago

A blueprint for this is available in Rubik's Public Folder.

Psychpsyo commented 3 years ago

The new 'Local Leaky Impulse Bucket' node has this feature built in and I'd imagine it'd work the same way for Delay nodes. (Cancelling all queued delays.) On the impulse bucket, this input is called Reset.