Session Rule Implementation
(WIP transcription) |
Revision as of 23:09, 27 November 2018
This page seeks to clearly define expected script behavior for Session Rule assets. This focuses specifically on correct behavior from the code perspective, rather than addressing how rules should be designed for best gameplay outcomes.
Contents |
Legacy Behavior Note
Please note that many existing rules do not behave correctly as defined here. This is considered a bug in the individual session rules. N3V Games has a policy of updating session rules to behave correctly even where that may lead to changes of function in existing session assets. Where reasonable to achieve, emulation of the existing (buggy) behavior may occur for existing rule instances in existing sessions, however any newly created rule instances will use the corrected behavior.
These expectations were codified at the end of TS12 development due to an increasing inability of session rules to work together resulting from the unpredictable nature of their responses to various common use-cases. The systems as a whole, including most of the core rules discussed in this document, have existed for far longer than this document. The document does not redefine any existing systems, but simply presents the only reasonable implementation approach possible if rules are to be expected to work properly in all use-cases. In some cases we also choose to mandate specific points of behavior relating to user expectations rather than technical necessities.
Certain additional API mechanisms were introduced alongside the development of this document, to allow better communication with session authors regarding the differences in behavior between certain types of rule. The correct usage for these mechanisms is also included in this document.
The Rule Hierarchy
All rules within a given session form a simple tree structure.
Rule State Flags
Each rule in the tree may have certain associated state flagged, including a combination of the following:
PAUSED
The rule is currently paused, meaning that it does not affect the game world in any way and does not respond to any changes in the game world. The parent rule must cause the rule to become unpaused for the rule to have any effect. It is not correct behavior for a rule to unpause itself.
COMPLETE
This flag indicates that rule has fully completed its operation (including any child rule operations) and does not affect the game world in any way. This flag is set by the rule itself, not by an outside entity such as the parent. This state should not be changed while the rule is PAUSED.
Being COMPLETE does not imply being PAUSED. In many scenarios, the parent rule may respond to a child becoming COMPLETE by causing it to also become PAUSED, however this is certainly not guaranteed in all cases, and is not guaranteed to be immediate in the cases where it does happen.
The rule may optionally respond to specific changes in the game world by de-flagging the COMPLETE status. For example, a rule which specifically implements a condition check may choose to become COMPLETE when that condition is met, but return to an incomplete state when the condition is no longer met (again, remembering that a change in either direction should only happen while the rule itself is not PAUSED).
When a rule's COMPLETE state changes, a "ScenarioBehavior", "Touch" message is sent (TBD: BY WHAT MECHANISM?) to its parent rule (if any), unless the parent is in the PAUSED state. This allows the parent to react to changes in its child rules' states. It is important to note that the message indicates a possible change which the parent may be interested in, but it is the parent rule's responsibility to determine whether the state change is worth reacting to.
WAS COMPLETE
This is typically set when a rule first becomes flagged as COMPLETE, and will stay set even if the rule later flags as incomplete. This can be used by a parent which only cares that the child rule has been flagged as COMPLETE at some point, rather than caring what its current completion status is.
DOES COMPLETE
This indicates whether the rule can ever reach the COMPLETE state. Its value is constant for a given rule configuration, and does not change based on the runtime rule state or other game state. Rules which are flagged as DOES COMPLETE are expected to become complete after some discreet operations are completed. Rules which are not flagged are expected to run indefinitely unless PAUSED by an external agent (such as the parent rule).
Initial State
All rules are initially in the PAUSED state when in Surveyor, and only become unpaused after gameplay begins in a Driver session. Since a PAUSED rule is not permitted to unpause itself, this is a stable state which will persist until the native code takes step to begin program flow.
Top Level Rules
All rules at the top level (ie. rules which are not a child of any other rule) simultaneously become unpaused after the session has loaded into Driver. Some of these rules may take initial setup steps and then become COMPLETE. Some of these rules may begin monitoring for specific game conditions before taking any further action. Some of these rules may begin taking action immediately. The rules are not sequenced against each other in any way, and it is the session author's responsibility to ensure that this does not cause any conflicts.
Scheduling
Since the script VM is a cooperatively-threaded environment, it is technically true that rule evaluation will occur in an ordered manner rather than truly simultaneously. However, no guarantees are made regarding the order in which the simultaneously-running rules will perform their checks or outcomes. Since rules may wait internally rather than fulfilling their entire purpose in an atomic operation, it is also not guaranteed that a first-running rule will fully complete before some other simultaneously-executing rule begins completion. In short, if order-of-execution is critical to the correct behavior of a session, then the order should be enforced by the session creator using the rule hierarchy rather than by making assumptions about execution order of simultaneously-running rules.
(Note: with the introduction of Asynchronous Route Streaming, this is even more true than previously. What may have been a near-instantaneous operation in older builds may now require a wait of several seconds while the necessary data is streamed in.)
It is important to note that some conditional rules may partially or completely rely on a polling behavior to evaluate their conditions. (An example of partial reliance is a rule which waits for an event, but then evaluates a condition to clarify whether the event should be acted upon.) In these cases, it is possible that the condition can rapidly become true and then become false again without the rule responding. This results in an uncertainty as to whether the conditional rule will correctly detect a given instance of the condition. This type of scenario should be avoided completely where possible, and should most definitely be limited to outcomes that do not significantly affect gameplay.
For a hypothetical example, if the player's train speeds for 0.5sec, and a speeding check rule was implemented that polled every second, then the overspeed event may or may not trigger on any given occurrence. A session creator should not use this outcome as a failure condition, since some players may be able to speed without penalty, whereas other players would be penalised on the first attempt. A sensible solution in this case might be to increment a counter while speeding is detected, and only take action if the counter reaches a certain threshold. If the only penalty was a warning to the player which had no actual gameplay impact, then it might be acceptable to forgo this check and simply accept that some players will receive a warning where others may not.
Child Rules
As with all other rules, child rules (ie. those that have parent rules in the hierarchy) start out in the PAUSED state. They do not become unpaused until the parent is ready for the children to start operating. Exactly when this occurs depends on the specific parent rule, but the general flow is the same for all rules:
1. Parent rule becomes unpaused and begins operation. 2. Parent rule configures any necessary world state changes. 3. Parent rule optionally waits for a specific condition to occur. 4. Parent rule begins executing its children. This involves unpausing one or more of the children (as discussed below in "Parent Rule Styles"). 5. Parent waits for its children to complete (as discussed below in "Parent Rule Styles") and then pauses the children. 6. Parent flags itself as COMPLETE.
This process allows program control flow to pass from a top-level rule, to its immediate children, to their immediate children, and so on. As the bottom-level children become complete, completion flows back to the higher level children until finally the top-level rules become COMPLETE.
It is worth noting that there is nothing special about a top-level rule being flagged as COMPLETE. This does not indicate an end-of-session or any other fundamental gameplay mode change. Since the pausing of rules on completion is handled by the parent rule (see step 5 above), the top-level rules never become PAUSED on completion.