Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add diff state compression #264

Closed

Conversation

TheYellowArchitect
Copy link
Contributor

@TheYellowArchitect TheYellowArchitect commented Aug 30, 2024

Followup to #254 (yes, it supports serialization)

Before a state is sent, it compares with the properties it had in the previous tick, and if they have the same value, these properties are not sent.

On receiving, the state is reconstructed accurately, because the very first state always sent is full state (no diffs), so all future diff states build off this initial state.

As for serialization, I added an integer (u_32) to the header which supports up to 32 variables/properties.
It's basically a bitfield, which on deserialization says which of the state_props indexes are contained in the serialization. And if contained, it deserializes the following value based on its type.


There is a very rare use-case this PR bugs: If the packet of the very first state (full state, no diffs) is lost,(its possible because the RPC is "unreliable_ordered") then all future states received will bug too for that player's rewindable, because they build off the first state.
The solution is to make a new RPC where the full state is sent, "RELIABLE" so this will never bug, like so:

@rpc("any_peer", "reliable", "call_remote")
func _submit_full_state_reliable(received_state: Dictionary, tick: int):
	_submit_state(received_state, tick)

@rpc("any_peer", "unreliable_ordered", "call_remote")
func _submit_state(received_state: Dictionary, tick: int):

This simple solution however causes a race condition. What if the diff state arrives before reliable state 🫠
So the code must become more complicated with either of the solutions:

  1. A caching mechanism for "unreliable_ordered" _submit_state to store the states and run them only when the full state is received. Basically an array of dictionaries is added, and an if check if received full/initial state to run logic on them. Implemented in Diff states fix for missing first full state #267
  2. Submitting states continues to be unreliable, regardless if they are full or diff. The authority sends full states until the client receives at least one full state, and then sends an ack to the authority. Once the authority receives this, it sends diff states. Update: Added diff states (no binary serialization, no static typing) - includes fix for the first-state problem. #278
  3. Introduce a new project setting variable (int), named send_initial_full_states where the default value is 40, and the first 40 states are sent fully instead of diff. Would end up in less bloated code than the first recommendation, but then again, needs 40 because a lagspike can otherwise cause problems
    After all, if you send the next 10 states fully (instead of 40), and the player is on wifi and passes under a bridge or something so all internet connection is lost for 3 seconds, this won't work. But will "reliable" solution above work, or will it timeout?

I can do any once this is merged. Honestly, practically, this works no problems. A packet loss is rare, and a packet loss to happen at the very first state of a rewindable is very very very rare.


This PR is tested and confirmed to work for rollback-synchronizer.gd but I haven't tested my port to state-synchronizer.gd, I may do later as I'm after another PR.
Whoever feels like testing state-synchronizer.gd with 3 players, high latency, and serialized and non-serialized, it would be helpful to ensure this PR works as intended.

After this is merged, and users confirm it works properly, I suggest for serialization, to always have diffs, since the point of serialization is small size, which this achieves. Also serialization methods contain diff parameters. So the boolean only applies to default state sending (aka dictionaries)

@TheYellowArchitect TheYellowArchitect marked this pull request as ready for review August 31, 2024 15:21
@TheYellowArchitect
Copy link
Contributor Author

Closing in favour of #278 which is far cleaner, has no dependencies, and also has implemented the 2nd fix recommended in the OP

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant