· 6 years ago · Jan 04, 2020, 05:56 PM
1// -------------------------------------------------------------------------------------------------------------------------------------------------
2// Network Prediction Plugin
3// -------------------------------------------------------------------------------------------------------------------------------------------------
4
5High level overview:
6-This is a *WIP* plugin that aims to generalize and improve our player prediction systems.
7-Long term we hope this replaces UCharacterMovementComponent and its networking system (ServerMove, ClientAck*, ClientAdjust*, etc).
8-The GameplayAbility system will be updated to interop with this as well.
9-NetworkPrediction plugin is the core generalized system that you need to enable to use anything.
10 -For now at least, new movement classes built on top of this will live here but could one day be moved out into a dedicated movement system plugin.
11-NetworkPredictionExtras plugin contains some examples that may be helpful in learning the system. It is not inteded to be used directly in shipping games.
12
13High level goals:
14-Make working with predicted gameplay systems such as character movement easier.
15-Give predictived gameplay systems the ability to interop with each other (E.g, CharacterMovement and Gameplay Abilities).
16-Provide better tooling to understand what is going on in these complex predicted gameplay systems.
17-This all leads to: ability to develop richer and more interactive gameplay systems that work in multiplayer games.
18
19What this is NOT:
20-We are not moving to a global fixed ticking system. Though these systems have huge advantages, we don't think it is a good universal solution for the engine.
21-That said, we recognize that it is the right solution for many things and we want to enable support for that. Our goal is that the user simulation code is independent of ticking being fixed or not.
22-To be clear: widely different, variable, fluctuating tick rates between clients and server is basically the root problems we are fighting here.
23-Related, we are also not all in on a "predict everything: seamlessly and automatically" solution. We still feel player prediction is best kept to a minimum (meaning: predict the minimum amount of stuff you can get away with).
24-But, we are trying to make it way easier to write predictive gameplay systems. Easier to write, to debug and reason about.
25-We do hope the new movement system will be automatic in this sense: if you write a new movement mode within the movement simulation class, things should "just work". (But we are not trying to make your entire game predicted)
26
27
28Getting Started:
29-Both NetworkPrediction and NetworkPredictionExtras are disabled by default. You will need to enable them for your project (manually edit .uproject or do so through editor plugin screen).
30-Once NetworkPredictionExtras is loaded, /NetworkPredictionExtras/Content/TestMap.umap can be loaded (must enable 'Show Engine Content' AND 'Show Plugin Content' in content browser!).
31-The "MockNetworkSimulation" can be tested anywhere just by typing "mns.Spawn".
32
33Code to look at:
34-MockNetworkSimulation.h: Good place to start to see a bare minimum example of a predicted network simulation. Both the simulation and actor component live in this header.
35-NetworkPredictionExtrasFlyingPawn.h: Starting point to see how a pawn using flying movement is setup. In NetworkPredictionExtras.
36-FlyingMovement.h: The actual implementation of the flying movement system. In NetworkPrediction.
37-NetworkSimulationModel.h: this is the lowest level guts of the system. This is what every simulation ultimately uses to stay in sync.
38-FMockAbilitySimulation::Update an example "ability system" working on top of a movement simulation
39
40Extremely high level technical overview:
41-Concept of "NetworkedSimulationModel": defining a tightly constrained (gameplay) simulation that can be run predictively on clients and authoritatively on server. E.g., a "Movement System" is a NetworkedSimulationModel.
42-The simulation has tightly defined input and output. At the lowest level we have generic buffers of structs that are synchronized:
43 Input Buffer: the data that the controlling client generates
44 Sync Buffer: the data that evolves frame to frame and is produced by ticking the simulation
45 Aux Buffer: additional data that can change, can be predicted, etc but does not necessarily get updated frame to frame (e.g, something else usually updates it).
46-Implicit correction: all prediction and correction logic is now client side. Server just tells clients what the authority state was and it is up to them to reconcile.
47-Single code path: the core network simulation has one "Update" function that does not branch based on authority/prediction/simulated etc. "Write once" is the goal for gameplay code here.
48
49UE4 Networking info (How this plugs into UE4 networking)
50-See UNetworkPredictionComponent for the glue.
51-"Replication Proxy" (FReplicationProxy): struct that points to the network simulation and has policies about how it replicates and to who. Custom NetSerialize.
52-E.g, there is a replication proxy for owner, non owners, replays, and debug. All point to the same underlying data.
53-Still using actor replication to determine when to replicate.
54
55Simulated Proxy behavior / "modes"
56-Interpolate: Sync buffer is replicated but there is no ticking of the net sim. Literally do not have to Tick the simulation.
57 -Actual interpolation will happen in some class outside of TNetworkSimulationModel (doesn't exist yet). It will look at sync buffer and interpolate over it.
58-(Sim) Extrapolation: by default if the netsim is ticked, we synthesize command and extrapolate the simulation. With basic reconciliation to absorb fluctuations in latency.
59-Forward Predict (not implemented yet): must be explicitly enabled by outside call, ties simulated proxy sim to an autonomous proxy sim. Sims will be reconciled together in step.
60
61
62TODO: Major missing elements
63-"Events" into and out of the system are not in place. That is, something happens in your movement sim that needs to call outside code/generate an event. A system is needed to track this stuff rather than just calling directly into the handler.
64-No optimizations (bandwidth, cpu) have been done.
65-[Movement specific]: must lock down SetActorLocation/Rotation API so that movement state can be changed out from underneath the system.
66
67High level focus:
68-The focus right now is on the generalized prediction system, rather than actual movement code.
69-We are exploring "forward predicting non autonomous proxies" with the parametric mover. Unclear how deep we will go with this.
70-Then Aux buffer and "events"
71-Then mixing in ability system + other scripting options
72
73-Once everything feels solid we will go deeper on movement and build out a more extendable movement system.
74-E.g, We don't intend all users of the engine to write their own network simulation model movement system.
75-We hope to provide a generalized movement system that is easy to extend in itself without knowing all the details of the NetworkPrediction plugin.
76-We intend to support RootMotion (Animation) / RootMotionSources (non anim) in the new movement system.
77
78Road Map:
79-New Movement System / Ability System integration
80
81
82// ----------------------------------------------------------------------------------------------------------
83// Glossary
84// ----------------------------------------------------------------------------------------------------------
85
86"Simulation": should always refer to use code. The simulation is what we are networking. It doesn't matter what the simulation is actually doing, we are building a system to network it like a black box.
87"System": should always refer to the TNetworkedSimulationModel code: the generic code that is doing the networking. It is distinct from the simulation.
88
89
90Reconcile: When a predicting client receives authoritative data from the server that conflicts with what they previously predicted: sorting this out is called "reconciling". Usually means you will resimulate.
91Synthesize: In general means "making shit up". For example, "synthesizing user commands" would mean "creating user commands that were not generated by a player, but instead guessing what it might be".
92
93
94Client Prediction: using new, locally created information (input cmd) to evolve the game state ahead of the authoritative server.
95(Client) Extrapolation: Taking the latest received state from the authoritative server and "guessing" how it continues to evolve. The key difference is you are not using new information that the server does not have yet.
96
97Forward Predicting: Using Client Prediction to predict ahead N frames, that is proportional to the ping time with the server.
98(Sim) Extrapolation: Using the network sim model to extrapolate frames, by (usually) synthesizing input cmds. Extrapolation is "one frame at a time" rather than "N frames based on ping".
99Interpolation: Interpolating between known states. E.g, you are never guessing at future state.
100 Note: We may technically allow interpolation past "100%", which would technically make it extrapolation (you are guessing at state you don't have yet).
101 This would still be distinct from the above "Sim" Extrapolation where the network sim is used to generate future data instead of simple "interpolation" algorithms.
102Smoothing: Taking the output of the simulation and applying an additional layer of "ease in/ease out" updates. Smoothing would use things like spring equations and "lag constants" rather than being tied to the actual simulation itself.
103
104
105// ----------------------------------------------------------------------------------------------------------
106// Release notes
107// ----------------------------------------------------------------------------------------------------------
108
109Update (12-13-19)
110-Only small updates (if any) for the rest of the year. System is going through code reviews as we plan for writing a new general movement system on top of Network Prediction.
111-Things are still liable to change but for the most part every major feature is in.
112
113Update (11-21-19)
114-Rewindable NetSimCue support added. This just about wraps up the initial implemention of NetSimCues and the "Game API / Event System".
115-Still some cleanup and debugging/logging stuff to do but will be transitioning into porting the legacy CMC movement system over soon.
116
117Update (11-15-19)
118-NetSimCues initial check in
119-These are the async/deferred events that are outputted during the network simulation tick
120-Different classification in types wrt prediction and replication
121-See FMockAbilityBlinkCue in code, "Misc Notes" at bottom of readme file
122-"Rewindable NetSimCue" api still todo
123
124Update (10-28-19)
125Renaming/cleanup on ::Update function:
126-"TSimulation::Update" is now "TSimulation::SimulationTick". Makes searching through source a bit easier, less generic name
127-Collapsed parameters of SimulationTick into 3 containers: FNetSimTimeStep, TNetSimInput, TNetSimOutput
128-Time passed into simulation is now FNetworkSimTime (instead of forcing float RealTimeSeconds conversion at callsite)
129-TSimulationTickState broken up into FSimulationTickState and TSimulationTicker.
130-::SimulationTick now also gets a reference to FSimulationTickState. This is setting us up to do timers/countdowns based on sim time within these functions.
131
132Update (10-25-19)
133-Another large refactor on the core classes. Things are feeling good!
134-"Simulation Driver" and a few other concepts have changed:
135 -"Simulation" is now implemented by a simulation class. What was previously a static method + a "driver" pointer, is now an instantiated C++ class that user code creates and passes into TNetworkedSimulationModel.
136 -The Simulation (previously driver) is now longer implemented by the owning actor/component. This simplifies a lot of things and make extending simulation much less awkward.
137 -TNetworkSimDriverInterfaceBase --> TNetworkedSimulationModelDriver. This is now distinct from "what the simulation needs to call internally" (previously defined by the driver now the simulation).
138 -TNetworkedSimulationModelDriver ("System Driver") is the set of functions the system internally needs to call.
139 -Visual logging has been refactored to be a single function of the system driver that takes all 3 states instead of single methods on the individual system types (Input,Sync,Aux). this simplifies a lot of stuff and allows you to visual sync+aux together.
140-"MockAbilitySystem" has been implemented.
141 -Simple, "hardcoded" abilities that function on top of the flying movement simulation. See notes in MockAbilitySimulation.h
142 -This code gives a good glimpse into what GameplayAbilities will look like (again, more hardcoded in this example but same techniques will be applied).
143
144
145Update (10-16-19)
146-Cleanup how we advance the simulation and unified which keyframes are accessed across the three main buffers
147-LastProcessedInputKeyframe -> PendingKeyframe. Code is simplified dealing with the "next" keyframe rather than making assumptions based on "last" keyframe.
148-Input/Sync/Aux states @ PendingKeyframe are what gets used as inputs to T::Update. This is more consistent that how it worked previously, where it was essentially (Input+1/Sync) -> (Sync+1).
149-This gets rid of awkward hacks like empty input cmd @ keyframe = 0.
150-Also got rid of InitSync/InitAuxState interface functions. The initial states can now be provided when instantiating the network sim model.
151
152Update (10-15-19)
153-Aux buffer hooked up. Still some decisions to make here, expect a few more refactors.
154-TReplicationBuffer replaced with simpler API.
155
156Update (10-1-19)
157-Forward Predict / Dependent simulation initial check in. This has some limitations and is not final
158 -Dependent Sim can't trigger reconcile yet
159 -Assumptions made about replication frequency. Dependents must replicate as same frequency as parent sim right now.
160 -General lack of precision here when running in variable tick simulation. (Sims tick at different rates on server, so hard to correlate client side).
161
162
163Update (9-26-19)
164-More refactors on general system architecture. UNetworkSimulationGlobalManager is solidifying and most boiler plate is out of the actor components (Not an exciting update but this was overdue!).
165-PostSimTick added and Interpolator refactored to be part of this step, dependent on network role (Eg., simulated proxy only when enabled).
166-Continuing to investigate dependent actor simulations. A few early prototypes did not work out but am going to try a slightly different approach.
167
168Update (8-14-19)
169-Some more cleanup. Cvar fixes and other consolidation. Now time for vacation (2 weeks).
170TODO still:
171-Improve debugger (has rotted a bit due to refactors)
172-Network fault handling, disable/enable local prediction causes some bad transitions
173-Initialization of system, specifically InitializeForNetworkRole can be much better now.
174 -Consider using templates to inline allocate all memory. Maybe make auto proxy buffers allocated on demant.
175
176Update (8-13-19)
177-Refactor really feeling good, things are falling into place. Still a few things to do and bugs to chase down.
178-Added simulated proxy notes above to detail current plan
179
180Update
181-Refactor on ticking state is taking shape and feeling much better. Some thing may have broke but will get smoothed out.
182-New goal is to support fixed ticking seamlessly. Meaning, when you instantiate or define your network sim you can opt into fix ticking and this won't affect how your simulation code works.
183-(Nice thing is that storing frame time on the user cmds is now an internal detail of the system and transparent to users. So you will not need seperate sets of input cmds for a fix tixed version of your sim)
184
185
186Update (8-9-19)
187-Big refactor underway within the NetworkSimModel. Some files are renamed or moved as well. Still a bit to do but this was a good commit point.
188-Mainly to facilitate clean implementation of interpolation/extrapolation/forward predict option for simulated proxy simulations: this was exposing weaknesses in the templated implementations.
189-Working towards improving "how and when simulation is allowed to advanced" (see TNetworkSimulationTickInfo)
190-Incomplete, but thinking about global management of active simulations: making sure reconcile and Tick are called in the right places. See notes in NetworkSimulationGlobalManager.h
191
192
193Update (8-2-19)
194-We want to take a look non player controlled simulations, such as doors, elevators, "pushers" before going deeper on ability system or movement.
195-Basic ParametricMoverment system checked in. This is also in an incomplete state. ("Pushing" the flying movement component does not work and won't be the focus for now until we are looking for closely at movement specifically).
196-The short term goal here is to be able to forward predict these even if they are simulated proxies. When/why/how is very tbd, but we want to see what it looks like.
197
198
199Current State (7-25-19)
200-Initial public check in. Core simulation model structure is in place. Lots of pieces missing but overall system is roughed in.
201-Two main examples right now: Flying Movement and 'MockNetworkSimulation' (in NetworkPredictionExtras)
202-Flying Movement: a port of FloatingPawnMovement into the new system. Essentially just a basic flying movement system.
203-MockNetworkSimulation: a very basic minimal "simulation" that demonstrates how the system works. Not tied to movement or anything physical: just a running counter/sum.
204
205
206// ----------------------------------------------------------------------------------------------------------
207// Misc Notes
208// ----------------------------------------------------------------------------------------------------------
209
210Notes on "State Transitions"
211-Detecting changes to sync/aux state in FinalizeFrame
212-Can emit events from these transitions
213-Events can't have additional data (thats not in the sync/aux state)
214-Can't really know for sure if transition "just actually happnened in the simulation" vs "I'm just finding out about it now"
215-Biggest advantage: "Reliable", doesn't require SimulationTick to run. (Interpolated proxies can still use them)
216
217Notes on "Events" --> NetSimCues
218
219Basic idea/notes:
220-"Non simulation affecting" events that are emitted during the ::SimulationTick function.
221-Cues are *handled* (by user code) during actor tick. Not during ::SimulationTick. (they are queued in a buffer)
222-Must support arbitrary data payloads. E.g, "Impact Velocity" for a "Landed" cue.
223-Fundamentally unreliable: join in progress, relevancy, temporary network disconnect = missed events.
224 -Seriously! These events should never be used to "track state" at some higher level. Even if simulation code guaruntees 1:1 On/Off events, you may miss them!
225 -Note: see "State transitions" above for how "reliable" events would be done
226
227Implementation progression:
228
2290. Niave approach is to let simulation code call directly into user code at anytime to do this stuff
230 Major issue: rollback/resimulate will double play events
231 Interpolating proxies would not invoke because they do not run the simulation
232
233[+Simple Invocation Suppressiong mechanism/context]
2341. Simplest approach would be "Weak Cues": "Cues don't replicate. Are only played on 'latest' simulation ticks (supressed during any rewind/resimulate scope)"
235 This causes/leaves two main issues/needs:
236 A. Interpolating simulated proxies (who don't run TickSimulation) will never invoke these cues.
237 (Note: could just say "yup, thats how it works" and leave replication up to the user at a higher level. Not great though!)
238 B. Predicting Client can mispredict: missed events or can play wrong events. Won't catch this / don't care.
239
240[+Cue type traits/policies, build on invocation supression above. Cue data doesn't matter yet, just local context and cue type]
241[+Time information passed to cue handler]
2422. Next simplest approach would be to add two more cues types (in addition to "Weak Cues"):
243 A. Replicated, non predicted. Always comes from server, never predicted. Everyone gets them.
244 -Downside: we won't predict them since we can't guard against double playing yet.
245 -Downside: cues can fire off "in the past" and now we must include time information to Cue handler (let them figure out how to deal with time make up)
246 B. Replicated (to sim proxy only), predicted (by auto proxy only).
247 -Allow auto proxy to treat them as Weak Cues: always play on simulate (not resimulate), always ignore server replication (so, can mispredict)
248 -Sim proxy will have them replicated explicitly
249
250[+Cue identification/uniqueness function]
2513. Next complicated approach would be to have identifying/uniqueness function to determine cues that were predicted or not. "Strong Cues"
252 Keep buffer of last N msecs of cues that were invoked locally.
253 When receiving replicated cues, look for matches that are "close enough" (hard to define!). If we already predicted, then ignore from server.
254 +This allows us to combine #2's cue types into a single type: Replicated + predicted (universal)
255 +Can also now invoke while resimulating by checking if a matching cue was already invoked during the original predicted simulate.
256
257[+Cue rollback/resimulate callbacks]
2584. Final complicated approach: full rollback/resimulate aware cue system
259 Cues are invoked with callbacks that can be subcribed to: Rewind, Resimulate, Confirmed. This is the only way to support "undoing" cues.
260 +Allows cues that "didn't actually happen" (mispredict) to be undone.
261 +Allows "all cues" to be undown on rollback and then repredicted during resimulate. (How important is this actually given everything else???)
262 -Allows us to "skip" the uniqueness test at the cost of redundantly rolling back -> redoing cues that were not mispredicted