@@ -7,7 +7,8 @@ not starting a game from scratch. Most applications will already conform to most
77## Isolate ** Game State** from ** Non-Game State**
88
99[ Backdash] ( https://github.com/lucasteles/Backdash ) will periodically request that you save and load the entire state of
10- your game. For most games, the state that needs to be saved is a tiny fraction of the entire game. Usually, the video and
10+ your game. For most games, the state that needs to be saved is a tiny fraction of the entire game. Usually, the video
11+ and
1112audio renderers, look-up tables, textures, sound data, and your code segments are either constant from frame to frame or
1213not involved in the calculation of the game state. These do not need to be saved or restored.
1314
@@ -63,7 +64,8 @@ times [Backdash](https://github.com/lucasteles/Backdash) needs to rollback to th
6364### Beware of External Time Sources (eg. random, clock time)
6465
6566Be careful if you use the current time of day in your game state calculation . This may be used for an effect on the game
66- or to derive another game state (e .g . using the timer as a seed to the random number generator ). The time on two computers
67+ or to derive another game state (e .g . using the timer as a seed to the random number generator ). The time on two
68+ computers
6769or game consoles is almost never in sync and using time in your game state calculations can lead to synchronization
6870issues . You should either eliminate the use of time in your game state or include the current time for one of the
6971players as part of the input to a frame and always use that time in your calculations .
@@ -72,8 +74,11 @@ The use of external time sources in **non-game state** calculations is fine (_e.
7274screen , or the attenuation of audio samples_ ).
7375
7476> [!INFORMATION ]
75- > We provide an implementation of a _ [Deterministic Random ](https :// lucasteles.github.io/Backdash/api/Backdash.Synchronizing.Random.IDeterministicRandom.html)_ out of the box
76- > which can be accessed directly from the _ [Rollback Session ](https :// lucasteles.github.io/Backdash/api/Backdash.INetcodeSession-1.html#Backdash_INetcodeSession_1_Random)_
77+ > We provide an implementation of a
78+ _ [Deterministic Random ](https :// lucasteles.github.io/Backdash/api/Backdash.Synchronizing.Random.IDeterministicRandom.html)_
79+ > out of the box
80+ > which can be accessed directly from the
81+ _ [Rollback Session ](https :// lucasteles.github.io/Backdash/api/Backdash.INetcodeSession-1.html#Backdash_INetcodeSession_1_Random)_
7782
7883## Beware of Dangling References
7984
@@ -85,7 +90,8 @@ reference instead of the values.
8590
8691The language your game is written in may have features that make it difficult to track down all your state . [Static
8792automatic variables in `C `](https :// www.javatpoint.com/auto-and-static-variable-in-c)
88- or [static members in `C #`](https :// learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members)
93+ or [static members in
94+ `C #`](https :// learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members)
8995are examples of this behavior . You need to track down all these locations and convert them to
9096a form that can be saved . For example , compare :
9197
@@ -129,14 +135,34 @@ public class MySessionHandler : INetcodeSessionHandler
129135}
130136```
131137
132- ## Use the [Backdash](https://github.com/lucasteles/Backdash) Sync Test Feature. A Lot.
138+ ## Use the [Backdash](https://github.com/lucasteles/Backdash) SyncTest Feature. A Lot.
133139
134- Once you 've ported your application to [Backdash](https://github.com/lucasteles/Backdash), you can us e
135- the [` CreateSyncTestSession `]( https : // lucasteles.github.io/Backdash/api/Backdash.RollbackNetcode.html#Backdash_RollbackNetcode_CreateSyncTestSession__2_System_Nullable_Backdash_Data_FrameSpan__Backdash_NetcodeOptions_Backdash_SessionServices___0___1__System_Boolean_)
136- function to help track down synchronization issues which may be the result of a leaky game state .
140+ Once you 've ported your application to [Backdash](https://github.com/lucasteles/Backdash),
141+ you can use tool called ` SyncTest ` to help track down synchronization issues which may be the result of a leaky game
142+ state .
137143
138- The sync test session is a special , single player session which is designed to find errors in your simulation 's
139- determinism . When running in a **sync -test session **, [Backdash ](https :// github.com/lucasteles/Backdash) by default will
144+ To create a sync test session use :
145+
146+ ```csharp
147+ using Backdash ;
148+
149+ var session = RollbackNetcode
150+ .WithInputType <MyGameInput >()
151+ .Configure (options =>
152+ {
153+ // ...
154+ })
155+ .ForSyncTest (options => options
156+ {
157+ // ...
158+ })
159+ .Build ();
160+ ```
161+
162+ The sync test session is a special session which is designed to find errors in your simulation 's
163+ determinism .
164+
165+ When running in a **sync -test session **, [Backdash ](https :// github.com/lucasteles/Backdash) by default will
140166execute a 1 frame rollback for every frame of your game . It compares the state of the frame when it was executed the
141167first time to the state executed during the rollback , and raises an error if they differ during your game 's execution.
142168If you set the [`LogLevel `](https :// lucasteles.github.io/Backdash/api/Backdash.Core.LogLevel.html) to at
@@ -146,56 +172,76 @@ the rollback frame to track down errors.
146172By running ** sync - test ** on developer systems continuously when writing game code , you can identify ** de - sync ** causing
147173bugs immediately after they 're introduced.
148174
149- You can also set the ** sync - test session ** to auto - generate random inputs to help find de - syncs :
175+ ### Configuring
150176
151- ```csharp
152- using Backdash ;
153- using Backdash .Sync .Input ;
177+ You can configure a wide range of options to help debug you state , like :
154178
155- var session = RollbackNetcode .CreateSyncTestSession <MyGameInput >(
156- options : new ()
157- {
158- Log = new ()
159- {
160- EnabledLevel = LogLevel .Debug ,
161- },
162- },
163- services : new ()
164- {
165- InputGenerator = new RandomInputGenerator <MyGameInput >(),
166- }
167- );
179+ ```csharp
180+ .ForSyncTest (options => options
181+ .UseJsonStateParser () // tries to display you state as json on desync
182+ .UseDesyncHandler <YourDesyncHandler >() // custom handler to deal wih a desync
183+ .UseRandomInputProvider () // generate random inputs
184+ .CheckDistance (4 ) // the forced rollback check distance in frames
185+ )
168186```
169187
170- If you want a meaningful string representation of your state in the sync - test , you need to implement
171- the method `GetStateString ` in your handler .
188+ If you need better meaningful string representation of your state in the sync - test , it is recommended to implement
189+ the optional method `INetcodeSessionHandler .CreateState `. This will be used to materialize previous saved states
190+ before passing them to the `DesyncHandler `.
172191
173- ```csharp
174- // print the state as json
175- public string GetStateString (in Frame frame , ref readonly BinaryBufferReader reader )
176- {
177- GameState state = new ();
192+ > [!NOTE ]
193+ > The `.UseJsonStateParser ()` will only work if `INetcodeSessionHandler .CreateState ` returns a JSON serializable object .
178194
179- state . LoadState ( in reader ); // read and fill the state properties
195+ #### Implement a DesyncHandler
180196
181- return JsonSerializer .Serialize (state , jsonOptions );
182- }
197+ a `DesyncHandler ` will help you to handle whenever a desync happens in a `SyncTest ` session .
183198
184- static readonly JsonSerializerOptions jsonOptions = new ()
185- {
186- WriteIndented = true ,
187- IncludeFields = true ,
188- };
199+ As example , a `DesyncHandler ` that uses [DiffPlex ](https :// github.com/mmanela/diffplex) to print on the console
200+ the state diff when a desync occurs :
189201
190- ```
202+ ```csharp
203+ using Backdash ;
204+ using Backdash .Synchronizing .State ;
205+ using DiffPlex .DiffBuilder ;
206+ using DiffPlex .DiffBuilder .Model ;
191207
192- > [! NOTE ]
193- > For better debugging the `RollbackNetcode .WithInputType <>().ForSyncTest (.. .)` accepts an implementation of the `IStateDesyncHandler `
194- > which is called whenever a state desync happens in the test .
195- >
196- > You can use it for enhanced state logging or showing semantic diffs .
208+ sealed class DiffPlexDesyncHandler : IStateDesyncHandler
209+ {
210+ public void Handle (INetcodeSession session , in StateSnapshot previous , in StateSnapshot current )
211+ {
212+ var diff = InlineDiffBuilder .Diff (previous .Value , current .Value );
213+
214+ var savedColor = Console .ForegroundColor ;
197215
216+ foreach (var line in diff .Lines )
217+ {
218+ switch (line .Type )
219+ {
220+ case ChangeType .Inserted :
221+ Console .ForegroundColor = ConsoleColor .Green ;
222+ Console .Write (" + " );
223+ break ;
224+ case ChangeType .Deleted :
225+ Console .ForegroundColor = ConsoleColor .Red ;
226+ Console .Write (" - " );
227+ break ;
228+ default :
229+ Console .ForegroundColor = ConsoleColor .Gray ;
230+ Console .Write (" " );
231+ break ;
232+ }
233+
234+ Console .WriteLine (line .Text );
235+ }
236+
237+ Console .ForegroundColor = savedColor ;
238+ }
239+ }
240+ ```
198241
242+ > [! TIP ]
243+ > You can see this implementation working with JSON in
244+ > the [SpaceWar ](https :// github.com/lucasteles/Backdash/tree/master/samples/SpaceWar) sample.
199245
200246## Where to Go from Here
201247
0 commit comments