Impulses: Difference between revisions

From Resonite Wiki
put info on their proper pages for variable types
overhaul this page. it was due for one. also some TODOs but oh well
Line 1: Line 1:
{{Stub}}
'''Impulses''', sometimes known as '''calls''', are discrete actions within [[ProtoFlux]] and one of the two basic types of chains to build. In contrast to chains of inputs and outputs to a node, which are generally continuous to one another, impulses push a task execution along the impulse chain, much like conventional [https://en.wikipedia.org/wiki/Imperative_programming imperative programming].


See Also: [[Dynamic Impulses]]
== Overview ==


== Impulses ==
In ProtoFlux, nodes that perform a discrete action require an impulse to execute. For example, [[ProtoFlux:Duplicate Slot|duplicating a slot]] can't trigger whenever it wants--it would be impossible to control such a thing. As such, a special input <code>*</code> of type ''Call'' is used to trigger the action. This chain can then be continued via the <code>Next</code> output.
Impulses (or Calls) are triggered actions. They normally do not have a duration (unless using Async calls) and happen in a singular instantaneous moment. Some examples of Impulses could be when a user clicks a button, when a user collides with something such as the ground, or when a object gets duplicated.


Impulses have some data associated with them. This data includes:
Upon an action node receiving an impulse, it evaluates all the non-impulse inputs connected to the node. This contextualizes the relationship between impulses and other node chains: Impulses push task execution, while action nodes pull their inputs, which may pull their inputs, and so on until the original input can be evaluated.
* The user who sent the impulse, which can be read via a [[ProtoFlux:Local User|Local User]] node during the impulse.
* The time of the impulse, read by the [[ProtoFlux:World_Time_Float|WorldTime]] (and similar) nodes.


When a ProtoFlux value is connected solely into nodes that receive Impulses, the value is no longer evaluated every game update but only when the node is impulsed. This is useful to optimize performance as instead of doing a constant evaluation, you wait until a Impulse is received to evaluate. A heavy computations which benefits from this approach is [[ProtoFlux:Find_Child_By_Name|FindChildByName]] which searches an entire hierarchy when it is evaluated. In this scenario, the search only has to be done once during the Impulse, and then optionally it can be cached using a Write node.
Impulses are a local construction by default within ProtoFlux. If interacting with the [[FrooxEngine]] [[data model]] in any fashion, however, the actions they perform get synced across users. Impulses being local mean that they implicitly carry the user that "owns" the impulse, which can be retrieved during an impulse chain with a [[ProtoFlux:Local User|Local User]] node.


{{Note|Non-Asynchronous Impulse Code can freeze the game engine until the code is finished. This can cause performance issues in certain scenarios like when using loops with lots of iterations, and is why it is recommended to make such lengthy computations Asynchronous so they can be dealt with over a longer time period, and to not evaluate such things constantly.|warning}}
Impulses can come from a variety of sources. Most commonly, when building ProtoFlux, a [[ProtoFlux:Call Input|Call Input]] can be created by dragging out an impulse input and pressing [[Basic Controls|secondary]]. Impulses may also come from ''events'', or impulse chains that start once receiving some sort of signal. These include, but are not limited to, [[Dynamic Impulses|dynamic impulses]], [[ProtoFlux:Button Events|button events]], [[ProtoFlux:Fire On True|''fire on'' events]], and [[:Category:ProtoFlux:Flow:Events|world/item events]].


== Async ==
== Contexts ==
Async or Async Impulses are a way to take your code and run it over more than one engine update cycle. Async Impulses are required to run nodes that are in the ProtoFlux Async category as these nodes require a Async Operation to start executing and will often take some time to complete what they do. Nodes like this include but are not limited to [[ProtoFlux:Delay|Delays]], [[:Category:ProtoFlux:Variables:Cloud|Cloud variable nodes]], and [[ProtoFlux:ASync While|ASync Whiles]].


Async is a way in some cases to reduce lag in some code as instead of halting the engine while you do a bunch of work in one update, you can spread the work over time and over multiple updates instead.
Impulses exist within a '''context'''. Contexts are picked up by the trigger of the original impulse and carried until the chain completes. Contexts carry certain values with them throughout the duration of the chain, including [[ProtoFlux:Local|local values]] and outputs to action nodes. Outside of the specific context (and nested contexts) that they are used, it is impossible to access the underlying value of one of these context-sensitive values.


Async however will not completely remove lag from actions done in world. So if you create a massive amount of data that has to be networked all at once, then making the code Async will not always prevent the lag from occurring. Likewise if running Async code constantly it can also cause performance issues.
Context is kept when passing through the same ''node group'' and lost if there is a disconnect of execution to a different node group. Node groups are the nodes that are all connected in some way or another, whether through direct wire connections or by references to another node, such as referencing a variable for a write node.


Async is currently not multi-threaded which is talked about in this video (at 40:00): [https://youtu.be/1losWav_AZQ?t=2392 link to the video at the specified time]
For example, if a [[dynamic impulse]] is pulsed, and that dynamic impulse pulses another one, and that third dynamic impulse writes to a local, the change will ''not'' be seen by the <code>Next</code> path of the first dynamic impulse trigger. This is because the execution leaves the node group of the first trigger after the first dynamic impulse is pulsed, causing the second dynamic impulse to not be within the same context as the initial chain. If the second dynamic impulse wrote to the same local, then all the node groups are connected, and the change <em>will</em> be seen by the initial <code>Next</code> path.
<!-- TODO: make an example image illustrating the above... -->


== Contexts ==
As of the time of writing, there are two different kinds of contexts in ProtoFlux: the ''sync'' context and the ''async'' context.
 
=== Sync Context ===
 
The '''sync context''' is the "default" context that most nodes will assume when developing a chain. Chains in this context run entirely in one engine update, or frame, and are incompatible with certain nodes that expect to be ran across multiple engine updates, which expect async contexts.
 
=== Async Context ===
 
'''Async contexts''' are ProtoFlux's way of being able to use [https://en.wikipedia.org/wiki/Coroutine coroutines] within the language. They allow execution to be delayed by the user (such as the [[ProtoFlux:Delay|Delay]] node) or paused as an action not dependent on execution needs to complete (such as the [[ProtoFlux:Play One Shot And Wait|Play One Shot And Wait]] node).
 
Async contexts are able to preserve locals and action node outputs across multiple engine updates, something not possible with a sync context.
 
Async contexts are created using the [[ProtoFlux:Start Async Task|Start Async Task]] node. This node actually creates a ''nested context'' relative to the input context, meaning that every value used by the surrounding context is duplicated, and the new async context can change these values <em>without</em> the changes being reflected in the <code>OnStarted</code> continuation of the previous chain.
 
If processing a lot of data using ProtoFlux, it may be desirable to use an async context and spread execution across multiple frames, as it will prevent a massive hitch when executing at the cost of taking slightly longer.
<!-- TODO: make an example image showing the difference between async sequence and sequence -> start async tasks... -->


Contexts are a way for ProtoFlux to store a state of local variables in the scope of the executing code. A new context can be created using the [[ProtoFlux:Start_ASync_Task|StartAsyncTask]] node. Variables in the context will still be able to be accessed and read with the same values even if Delays are used. A context can be thought of as what data is available during a certain Impulse.
== See Also ==


For variable types, see [[ProtoFlux:Store|Store]], [[ProtoFlux:Local|Local]], and [[ProtoFlux:Data Model Store|Data Model Store]] pages.
* [[ProtoFlux:Local]], [[ProtoFlux:Store]], and [[ProtoFlux:Data Model Store]] for the three types of variables one can access in ProtoFlux.
* [[:Category:ProtoFlux:Flow:Async]] for async flow nodes.

Revision as of 23:18, 16 December 2024

Impulses, sometimes known as calls, are discrete actions within ProtoFlux and one of the two basic types of chains to build. In contrast to chains of inputs and outputs to a node, which are generally continuous to one another, impulses push a task execution along the impulse chain, much like conventional imperative programming.

Overview

In ProtoFlux, nodes that perform a discrete action require an impulse to execute. For example, duplicating a slot can't trigger whenever it wants--it would be impossible to control such a thing. As such, a special input * of type Call is used to trigger the action. This chain can then be continued via the Next output.

Upon an action node receiving an impulse, it evaluates all the non-impulse inputs connected to the node. This contextualizes the relationship between impulses and other node chains: Impulses push task execution, while action nodes pull their inputs, which may pull their inputs, and so on until the original input can be evaluated.

Impulses are a local construction by default within ProtoFlux. If interacting with the FrooxEngine data model in any fashion, however, the actions they perform get synced across users. Impulses being local mean that they implicitly carry the user that "owns" the impulse, which can be retrieved during an impulse chain with a Local User node.

Impulses can come from a variety of sources. Most commonly, when building ProtoFlux, a Call Input can be created by dragging out an impulse input and pressing secondary. Impulses may also come from events, or impulse chains that start once receiving some sort of signal. These include, but are not limited to, dynamic impulses, button events, fire on events, and world/item events.

Contexts

Impulses exist within a context. Contexts are picked up by the trigger of the original impulse and carried until the chain completes. Contexts carry certain values with them throughout the duration of the chain, including local values and outputs to action nodes. Outside of the specific context (and nested contexts) that they are used, it is impossible to access the underlying value of one of these context-sensitive values.

Context is kept when passing through the same node group and lost if there is a disconnect of execution to a different node group. Node groups are the nodes that are all connected in some way or another, whether through direct wire connections or by references to another node, such as referencing a variable for a write node.

For example, if a dynamic impulse is pulsed, and that dynamic impulse pulses another one, and that third dynamic impulse writes to a local, the change will not be seen by the Next path of the first dynamic impulse trigger. This is because the execution leaves the node group of the first trigger after the first dynamic impulse is pulsed, causing the second dynamic impulse to not be within the same context as the initial chain. If the second dynamic impulse wrote to the same local, then all the node groups are connected, and the change will be seen by the initial Next path.

As of the time of writing, there are two different kinds of contexts in ProtoFlux: the sync context and the async context.

Sync Context

The sync context is the "default" context that most nodes will assume when developing a chain. Chains in this context run entirely in one engine update, or frame, and are incompatible with certain nodes that expect to be ran across multiple engine updates, which expect async contexts.

Async Context

Async contexts are ProtoFlux's way of being able to use coroutines within the language. They allow execution to be delayed by the user (such as the Delay node) or paused as an action not dependent on execution needs to complete (such as the Play One Shot And Wait node).

Async contexts are able to preserve locals and action node outputs across multiple engine updates, something not possible with a sync context.

Async contexts are created using the Start Async Task node. This node actually creates a nested context relative to the input context, meaning that every value used by the surrounding context is duplicated, and the new async context can change these values without the changes being reflected in the OnStarted continuation of the previous chain.

If processing a lot of data using ProtoFlux, it may be desirable to use an async context and spread execution across multiple frames, as it will prevent a massive hitch when executing at the cost of taking slightly longer.

See Also