Преглед изворни кода

Add RFCs accepted Oct 5th 2021

Signed-off-by: Pip Potter <[email protected]>
Pip Potter пре 3 година
родитељ
комит
193b8a1fac

+ 18 - 1
rfcs/readme.md

@@ -1 +1,18 @@
-# O3DE RFCs - This is where RFCs that have been accepted are kept
+# Sig-Network RFCs
+This is where RFCs that have been accepted are kept
+
+## RFC Naming
+All rfcs follow the pattern of:
+
+```rfc-net-(YYYYMMDD)-(X)-(friendlyname)```
+
+#### Notes
+* RFC should be referred to as ```rfc-net-YYYYMMDD-X```, friendly name is to just to aid discovery.
+* ```YYYYMMDD```: Replace with the date of the sig-network meeting that accepted the RFC.
+* ```X``` is a counter for the day, if there are multiple RFCs accepted in a single meeting.
+
+## Recent Accepted RFCS
+
+### October 2021
+* [rfc-net-20211005-1-inputscripting](rfc-net-20211005-1-inputscripting.md)
+* [rfc-net-20211005-2-entityhierarchies](rfc-net-20211005-2-entityhierarchies.md)

+ 136 - 0
rfcs/rfc-net-20211005-1-inputscripting.md

@@ -0,0 +1,136 @@
+# rfc-net-20211005-1
+* Intake issue: https://github.com/o3de/sig-network/issues/6
+
+### Summary:  
+A key feature built into Multiplayer Components is the concept of Network Input.  Network Input helps digest player input (the human and therefore most unpredictable aspect of the network simulation), in order to keep the simulation on clients and servers in sync.
+
+Currently, Network Input is only available to developers writing C++ components; this RFC proposes exposing Network Input to script (Lua and Script Canvas).
+
+  
+
+### What is the relevance of this feature?
+
+Scriptable Network Input will help save iteration time and allow developers who have limited experience with networking and C++ to build a multiplayer game.
+
+Since scripting doesn't require C++ knowledge or time to recompile, adjustments to the multiplayer controllers can be iterated upon quickly.  For example, when receiving keyboard WASD controller input, a designer might decide to add an acceleration curve so that tapping the keyboard moves the character less than it would from holding it down.  These types of gameplay edits aren't an exact science and requires dozens of little modification in order to create "good feel".  Without scripting support, a designer hoping to modify how keyboard input affects the character controller will need to (1) modify C++ game logic, (2) close down the Editor and Asset Processor, (3) recompile C++, (4) reopen the Editor, and finally (5) reopen and run the test level in order to see the results of their code.  If each of these small modifications requires C++, then hours are lost over the course of game development due to recompiling and testing code.
+
+If, alternatively, designers can make changes via script then the **time and steps taken are reduced**: (1) modify the game logic script, (2) rerun the level to test the change.
+
+In addition to saving time, scripting is typically easier to learn than C++ and thus is easier for studios to find/maintain a scripting dev.
+
+  
+
+### Feature design description:
+
+Multiplayer auto-components with NetworkInputs will have the option to enable or disable exposing any of their NetworkInputs to script via an "ExposeToScript" flag.  If any NetworkInputs are enabled for scripting, the multiplayer auto-component should generate 2 new methods inside of behavior context. 
+
+The first method will be an event which returns a result called _CreateInput:_
+
+*   Scripters will listen for this _CreateInput_ event in Script Canvas or Lua and return a struct containing the values for each exposed network input.  For example, the network player controller may want the ability to move a player forward and backward using the W and S keys respectively.  The scripter would then listen for the _CreateInput_ callback and return 1.0 if W is currently being pressed, - 1.0 if S is currently pressed, or 0.0 if neither W or S is being pressed.  The networking backend will do the heavy lifting ensuring the input is sent to the authoritative server and making sure_ProcessInput_ is called on the other side.
+
+![image](rfc-net-20211005-1-inputscripting/132406649-2108be3e-c0f1-49bb-a1df-315f928c903c.png)
+  
+
+The second method will be an event called _ProcessInput:_
+
+*   Scripters will listen for _ProcessInput_ on the Autonomous player or the Authority in order to read the input and drive the simulation.  Using the last example from _CreateInput_, if _ProcessInput_ says the player's movement is -1.0, then the designer could send a request to the NetworkCharacterController to decrease the player velocity so that they slow down or move backwards. 
+
+  
+
+### Technical design description:
+
+Step 1: Update Auto-Component XML NetworkInputs to Include "ExposeToScript" Attribute
+
+*   MutliplayerComponent XMLs should add a new NetworkInput attribute called "ExposeToscript"
+*   Example:  <NetworkInput Type="StickAxis" Name="ForwardAxis" Init="0.0f" "**ExposeToScript"="true"**/>
+*   .\\Gems\\Multiplayer\\Code\\Source\\AutoGen\\AutoComponent\_Header.jinja and AutoComponent\_Source.jinja will look for NetworkInputs using the new "ExposeToScript" attribute and expose new Behavior Context methods for the multiplayer component
+
+Step 2: Expose Auto-Component Class {{ ComponentName }}NetworkInput to Behavior Context
+
+*   In order for CreateInput event to return the input and ProcessInput to read the input, the {{ ComponentName }}NetworkInput class will need to be exposed to behavior context
+*   Create a ReflectToBehaviorContext() method inside of the  {{ ComponentName }}NetworkInput class and make sure it's called from somewhere (suggestion: the base component's Reflect() method) so it's reflected
+*   Loop through each network input and create a behavior context property.  Remember to only expose them if their "ExposeToScript" attribute is true.
+*   Create a behavior context constructor so scripters can create/store an instance of the {{ ComponentName }}NetworkInput class
+*   Make sure to name all the classes, properties, and constructor parameters so scripter can see what they are editing.  For example, if there's a boolean input called "IsJumping", make sure any Script Canvas nodes referencing "IsJumping" says "IsJumping" instead of displaying the default "boolean"
+
+Step 3: Expose StickAxis and MouseAxis to Behavior Context
+
+*   The existing MultiplayerSample game uses a type called StickAxis and MouseAxis for network inputs, these will be commonly used for network input.  These types are just wrappers around ranged floats (for example: -1.0 to 1.0) that are nicely packed into a single byte to reduce network traffic. 
+*   StickAxis and MouseAxis should be exposed to behavior context.
+*   StickAxis and MouseAxis should be moved out of MultiplayerSample and into the MultiplayerGem as these seem common enough to be used for lots of multiplayer games.
+
+Step 4: Add CreateInput and ProcessInput Event to Multiplayer Auto Component BehaviorContext
+
+*   Multiplayer components which have network inputs with "ExposeToScript"="true" need to add the CreateInput and ProcessInput events to behavior context 
+*   CreateInput should provide a deltaTime and allow handlers to return a struct containing all of the network input variables that are exposed to script
+*   ProcessInput should provide deltaTime, but does not need to return anything
+*   The auto-component's CreateInput() and ProcessInput() base methods should broadcast the CreateInput and ProcessInput event respectively.  The returned result of the CreateInput event should be passed back to the Multiplayer::NetworkInput& input parameter which a MultiplayerInputDriver service (such as [LocalPredictionPlayerInputComponent](https://github.com/o3de/o3de/pull/3157/files#diff-6b2bda3718623b21b5f22c8d587cb1d7cde51094be183707916208d3b41d5af1 "Gems/Multiplayer/Code/Include/Multiplayer/Components/LocalPredictionPlayerInputComponent.h")) will use to send and receive input over the network. 
+*   This should all be implemented using jinja inside .\\Gems\\Multiplayer\\Code\\Source\\AutoGen\\AutoComponent\_Header.jinja and AutoComponent\_Source.jinja
+
+  
+
+After implementing all 4 steps, users will be able to create auto-components with network inputs that are exposed to Lua and Script Canvas and receive event callbacks during the game runtime when it's time to create and process input.
+
+  
+### What are the advantages of the feature?  
+Using script instead of C++ for creating and processing network input will saves time since users don't need to recompile.  Also, those less familiar with C++ can still enjoy the ease of sending/receiving user input across the network.
+
+  
+
+### What are the disadvantages of the feature?
+
+While creating network input is fairly straight forward, scripters who may not have experience with networking could accidently mess up the network simulation if they incorrectly process network input.
+
+For example, if you're creating a first-person-shooter where animations need to stay in sync in order to properly detect where a bullet hit (body, arms, head, etc) then the scripter would need to know to how to update animation in a way that is network safe as opposed to using the existing actor events they've used in the past.  At the time of writing this document, there isn't a generic networking solution for EMotion Fx, so scripters will need to know specifically what is and isn't safe to update.
+
+  
+
+### How will this be implemented or integrated into the O3DE environment?
+
+There are no special steps to integrate this feature into your O3DE environment. As long as you have the MultiplayerGem enabled, you'll receive the ability to enable network input for scripting.
+
+### Are there any alternatives to this feature?
+
+There aren't any alternative features, but there are some additional features which could make the user experience a lot better.  Each of the following bonus features requires an RFC, and is a general Script Canvas feature which would be handled by sig-content.  This could greatly improve the script canvas user experience in general.
+
+Bonus Feature 1: Auto-Exploded Script Canvas Variables
+
+* The CreateInput node will need to return a struct containing all the inputs, it would be great if script canvas automatically allowed users to explode the struct in order to show each property separately.
+![image](rfc-net-20211005-1-inputscripting/132406706-646af6f6-493e-48b2-a295-459d670c3e7f.png)
+
+Bonus Feature 2: New Script Canvas Event Result UI
+
+* The current Script Canvas node for returning a result is a bit clunky, requiring users to pass in the result on the left hand side of the node.
+![image](rfc-net-20211005-1-inputscripting/132406725-db98e1d4-dbdd-47e6-90b2-5889f9249e62.png)
+  
+* Instead it would make more sense to produce 2 nodes: the event node and a result node that is data connected (and can't be disconnected).  The user would be expected to connect the execution slot at the end of the of the operation.
+![image](rfc-net-20211005-1-inputscripting/132406749-52783b78-098d-4bd5-8ee9-19d8b78964ab.png)
+* More notes regarding implementation:
+    *   A significant (4 day?) spike would be needed to investigate the Script Canvas front-end UI work required to get the 2 nodes appearing on screen.
+    *   The UX team would be needed for designing a nice visual for showing users when the 2 nodes are correctly connected, or when they are unconnected (ex: highlight a dotted-line between the event and result node)
+    *   After the GUI part is done, the parser would need to be updated so that return node is properly passed back to the event, but this shouldn't take long (couple days at most?)
+    *   Existing event result nodes would need an upgrade path. For example, when we automatically generate the 2nd node (the return node) where exactly should it spawn?
+
+  
+
+### How will users learn this feature?
+
+Anyone familiar with Lua or Script Canvas could quickly pick up how to use network input for their multiplayer game.
+
+Teaching Opportunity 1:
+
+Fortunately, Script Canvas is quite nice for discovering new features.  I often discover new features just by using the search feature in the Script Canvas node palette.  Network input is no exception, by searching the multiplayer component the 2 new methods for CreateInput and ProcessInput will appear in the node palette.  These nodes are fairly simple to grasp; CreateInput requires users to return inputs and ProcessInput provides input values for which designers can use to drive the simulation.
+
+Teaching Opportunity 2:
+
+A small video tutorial using Script Canvas to drive a simple network character around using WASD would also be a valuable learning tool.
+
+### Are there any open questions?
+
+Script Canvas Networking Context
+
+When writing any multiplayer gameplay logic, whether using C++ or Script Canvas, developers need to be careful to keep the network simulation in sync.  However, when it comes to Script Canvas there's an opportunity to limit what is exposed; it is possible to create a new scripting context for networking specifically where only certain methods are exposed to designers when writing networked scripts.  Instead of adding a ScriptCanvas component, designers would add a NetworkScriptCanvas component in order to enable the network specific context.
+
+This could help mitigate the potential harm done by bad design logic; however, it's a double edged sword that could severely nerf what's possible via script. 
+
+For example, a multiplayer character controller may want to use the Physics API to drive the player; they could use Physics::Character::AddVelocity and Physics::Character::ApplyRequestedVelocity to accurately move the player around based on deltaTime.  By using a network context script we could hide the Physics API and instead limit Script Canvas access to the NetworkCharacterComponent (a component which was designed specifically for moving characters on the network).  This topic would need further discussion about which Script Canvas nodes should and shouldn't be exposed to a network context.

BIN
rfcs/rfc-net-20211005-1-inputscripting/132406649-2108be3e-c0f1-49bb-a1df-315f928c903c.png


BIN
rfcs/rfc-net-20211005-1-inputscripting/132406706-646af6f6-493e-48b2-a295-459d670c3e7f.png


BIN
rfcs/rfc-net-20211005-1-inputscripting/132406725-db98e1d4-dbdd-47e6-90b2-5889f9249e62.png


BIN
rfcs/rfc-net-20211005-1-inputscripting/132406749-52783b78-098d-4bd5-8ee9-19d8b78964ab.png


+ 136 - 0
rfcs/rfc-net-20211005-2-entityhierarchies.md

@@ -0,0 +1,136 @@
+# rfc-net-20211005-2
+* Intake issue: https://github.com/o3de/sig-network/issues/5
+
+### Summary:
+
+Game designers often create game and player objects out of several entities, often in a parent-child relationship, such as a player object consisting of a main player body entity with child entities representing various player abilities and weapons. Entities of these game objects are often dependent on one another, based on transform or game logic.
+
+In multiplayer environment, group of such entities need to do the following:
+- spawn in the correct order on multiplayer clients, with parent entities starting first, and child entities spawning last
+- and process player input together as a single unit, instead of processing and sending their inputs as separate entities.
+
+
+### What is the relevance of this feature?
+
+#### Challenge: Parent – Child Activation Order of Network Entities
+
+On a server, prefabs spawn entities from parent to child entities. On clients, however, these entities may be created out of order as network packets come in. For example, a child entity may be activated before the parent entity; this may lead to child entities having incorrect world positions. Due to the random nature of network packet ordering, this will be a source of hard to reproduce and identify bugs.
+
+Given the following prefab structure:
+- Test Entity 1 is the root parent 
+- Test Child 2 is the child of Test Entity 1 
+- Test Child 3 is the child of Test Child 2
+
+These entities may be created on a multiplayer client out of order. Here is an example output of a debug component that prints activation order to the log:
+```
+(DebugNetwork) - entity [Test Child 2] activated
+(DebugNetwork) - entity [Test Entity 1] activated 
+(DebugNetwork) - entity [Test Child 3] activated
+```
+
+Whereas the correct order should be Test Entity 1, followed by Test Child 2 and then Test Child 3.
+
+**Goal**: O3DE Networking provides a solution to spawn parents and child entities in the right order, where parents are activated before their child entities.
+
+#### Challenge: Combined Input Processing of Entities
+
+Once entities are created in the correct order another, more advanced, challenge will appear on both server and clients: a player entity often needs to process player input commands (such as movement commands). In the context of a multiplayer game, that means saving processed inputs to a buffer. This buffer acts as a recent history of player inputs and is used by features such as local prediction for player movements and actions. Let us suppose that soon after, a game designer adds a child entity that represents a jetpack and describes the amount of fuel left in the jetpack.
+
+Now it would be convenient to store processed player input for both the main player entity and the child jetpack entity; this way if a network feature needs to know the player's state a few moments in the past, it can find the state of both the main player entity and the jetpack entity. For example, a player may have moved using the jetpack. If one only considers the movement, bugs will be introduced if the game logic does not take into account the state of the jetpack's fuel across time.
+
+_Bug Example 1_: a player moves forward using a jetpack; the jetpack fuel amount is now at half. However, the locally predicted forward movement was incorrect and so the server sends the client a correction. The client receives the correction and re-calculates using the stored player input data but ignores the state of the jetpack entity. This ends up using the remaining jetpack fuel while re-calculating the player’s movement after the correction. The bug is now a sudden drop in jetpack fuel at seemingly random times.
+
+_Bug Example 2_: one of the player abilities is a shield that is drained when attacked by other players. On a server, whenever a player firing action is received the world is rolled back in time to when the shot occurred on the player's application. The target player is moved back into the position, but their shield strength was not rolled back, as it was not included in input processing by mistake. Now, the damage calculation may be incorrect if shield amount is different between these two moments in time.  
+
+**Goal**: O3DE Networking provides a solution where a defined hierarchy of entities are processing input for multiplayer systems as one group.
+
+### Feature and Technical design description:
+
+As mentioned earlier, there are two challenges to solve: 
+1. the order of entity activation on clients and 
+2. combined input processing for the entire entity hierarchy.
+
+
+There is one additional variation that spans both challenges: dynamic versus edit-time prefabs. That is, a user might wish define their hierarchy in the Editor to get started but will soon find a reason to modify that hierarchy at runtime by attaching new entities or removing exists entities. For example, this could be a game spawning a new entity or a prefab describing a player weapon and attaching it to player’s root entity, or it could removing some vehicle parts due to in-game damage. Either way, in practice, both dynamic and bake time hierarchy ought to be supported.
+
+![image](rfc-net-20211005-2-entityhierarchies/130847200-4749f761-6911-426e-8d74-bd07cfa35e75.png)
+
+The above diagram is a visual example of a prefab that contains 4 entities: 1 of them is the root, and 3 others are children. Because Hierarchy Child entities are linked via their Transform component as children of Hierarchy Root, that are visually shown to belong to Hierarchy Root. (This UI already exists in the Editor.)
+
+At activation time, the hierarchical root entities are activated before hierarchical child entities. Therefore, these child entities can notify their root of their activation.
+
+By the time the entire prefab has been activated, Hierarchy Root component will have all its relevant children listed. That means, when the root entity is processing its input (_NetBindComponent::ProcessInput_), it can also invoke processing on all attached hierarchical children. Specifically, a Network Binding component at the top-level root of the hierarchy does the following 3 steps in the descending order:
+
+1.	Process inputs of the components attached to the hierarchy root entity
+2.	Then process inputs of Network Binding components to any hierarchical children
+3.	And finally process inputs of any hierarchical lower level roots in the hierarchy
+
+(There will be some limit to the supported levels of recursions. Likely specified by a AZ_CVar.)
+Here is a visual diagram of the order. _Component (1)_ gets its input processed first and _component (10)_ last.
+
+![image](rfc-net-20211005-2-entityhierarchies/130847258-3179f704-7a82-417b-868a-861b1a9df18a.png)
+
+Note: an attached root is a root that was once autonomous but later attached to another hierarchy. It is more likely to occur at runtime than Editor time, for example, when attaching a player to a vehicle. In such a case, a player prefab would be created with a hierarchy root component at the top entity and be autonomous on its own during the game but occasionally attaching itself to an autonomous vehicle prefab when needed.
+
+![image](rfc-net-20211005-2-entityhierarchies/130847284-f32ac721-e4c5-43ef-b81c-be37e5c2f00a.png)
+
+When a hierarchical child is activated on the server, it can find its immediate parent by looking up its transform parent. Network Transform component on the same entity will transmit this information to clients, where this entity’s activation will be delayed until its hierarchical parent has been activated on the parent. Once activated on a client, Hierarchical Child component may need to walk up multiple transform parents to find its root and connect itself for input processing.
+Hierarchical component will have Editor component toggles and API for disable/enable of hierarchy and input processing. This way users can customize their structures and it allows for helpful debugging while developing and testing this solution.
+In summary, two new components will be introduced:
+
+- Hierarchical Root component 
+no network properties (depends on the data in Network Transform component)
+- Hierarchical Child component 
+"include in input processing" toggle
+no network properties (depends on the data in Network Transform component)
+
+Network Binding component will be modified to reference these components when inspecting child entities, as connected via Transform components.
+
+
+
+#### Additional Background / Terminology
+
+**Terminology**
+- Parent entity – in O3DE it commonly means an entity whose transform determines other entities’ transforms
+- Child entity – an entity whose transform depends on a transform of its parent entity
+- Hierarchy of entities – a group of entities united by their parent-child relationships
+- Transform – a combination of a position, orientation, and scale
+- Local prediction – a technique in multiplayer to predict player actions without waiting on the server to tell the player how they will move
+- Server correction – when local prediction on a client goes wrong, the server will let the client know of the correct result
+
+
+### What are the advantages of the feature?
+
+- The best presentation of hierarchy in O3DE engine is found in the Editor and in prefab is that of Transform components. A transform component can have a parent and/or children entities, all link by their Transform components. This solution leverages and builds on this UI by using Transform parent-child relationship as the basis for hierarchical entities.
+- This is a straightforward hierarchical implementation using components and existing parent-child relationship of Transform component.
+
+### Implications / Considerations
+
+- This is a straightforward hierarchical implementation using components and existing parent-child relationship of Transform component.
+- All hierarchical connections point from transform children up to transform parents. This way we avoid the danger of creating loops of hierarchical structures by using battle-tested checks within Transform component.
+- Whenever an entity is detached, destroyed or moved to a new entity, this change can be detected by hierarchical components and adjusted accordingly.
+- In practice, the root of a prefab will be the root of hierarchy as well. If a user wishes to add a new child to this hierarchy, they will add their new entities and/or prefabs to the root entity or one of its child entities that have either hierarchical root or child components.
+- In order to support multiple levels of hierarchy, Network Hierarchy Child component needs to keep track of two entities: its immediate parent and its top-level root. The root can be calculated at the activation of entities, by walking up the parent-child relationship until the root is found. Its immediate parent can be looked up in Network Transform component on the same entity.
+- Care must be made to avoid unnecessary walking up the entities on game ticks. It should be limited to entity activation only, or when an entity is moved or removed.
+- The most critical path is **ProcessInput** method of NetBindComponent class. **ProcessInput** gets called every time any player input is detected on clients and is executed on server when applying player input. In practice it occurs every frame on clients, and multiple times a frame on servers. It is a hot execution path in multiplayer code. The calculation of hierarchical members must be kept outside of this logic. Given that parent changes can be signed up for via AZ::Events, hierarchy updates would occur only when a parent-child relationship changes in the hierarchy.
+
+
+### How will this be implemented or integrated into the O3DE environment?
+The implementation will be placed inside Multiplayer gem. The logic will reside primarily in 2 new components outlined above: Network Hierarchy Root and Child components.
+
+### Are there any alternatives to this feature?
+Another possible approach is *Arbitrary Hierarchy of Entities*.
+
+This approach is largely the same as the recommended solution but one major difference: instead of depending on Transform component, it allows for full freedom of assigning of parent entities within a hierarchy. In this case, it is possible to either skip Transform parents or parent to an entity from a different prefab and avoid Transform parent-child structure entirely, even potentially going in the opposite direction of Transform parent-child connections.
+
+#### Rationale for NOT Recommending This Solution
+
+- This much flexibility makes it too easy to get oneself in a bad state, especially at runtime, where one could form a loop within a hierarchy while attaching multiple hierarchies.
+- It would be unclear what should be activated first on clients. Likely, it would require an additional activation pass over involved entities, which would add too much complexity to the logic in multiple other related network systems.
+- The relationship would be hard to visualize in the Editor since the current UI can only be based on Transform parent-child relationship.
+
+### How will users learn this feature?
+- This feature will be discoverable through Network Hierarchy components and documentation.
+
+### Are there any open questions?
+- If a filtering system filters out a hierarchical root, should the entire hierarchy be filtered out?

BIN
rfcs/rfc-net-20211005-2-entityhierarchies/130847200-4749f761-6911-426e-8d74-bd07cfa35e75.png


BIN
rfcs/rfc-net-20211005-2-entityhierarchies/130847258-3179f704-7a82-417b-868a-861b1a9df18a.png


BIN
rfcs/rfc-net-20211005-2-entityhierarchies/130847284-f32ac721-e4c5-43ef-b81c-be37e5c2f00a.png