Skip to content

Commit afaec89

Browse files
Recommended fix.
1 parent 64db09d commit afaec89

1 file changed

Lines changed: 90 additions & 37 deletions

File tree

addons/netfox/rollback/rollback-synchronizer.gd

Lines changed: 90 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -171,9 +171,11 @@ func _record_tick(tick: int):
171171
_latest_state = max(_latest_state, tick)
172172
_states[tick] = PropertySnapshot.merge(_states.get(tick, {}), full_state_to_broadcast)
173173

174-
var properties_to_include: Array[String] = []
175-
var diff_state_to_broadcast = {}
174+
176175
if (NetworkRollback.enable_state_diffs):
176+
var properties_to_include: Array[String] = []
177+
var diff_state_to_broadcast = {}
178+
177179
if (_states.has(tick - 1)):
178180
for picked_property_path in full_state_to_broadcast:
179181
if (_states[tick - 1].has(picked_property_path) == false):
@@ -187,7 +189,9 @@ func _record_tick(tick: int):
187189
for picked_property in properties_to_include:
188190
diff_state_to_broadcast[picked_property] = full_state_to_broadcast[picked_property]
189191

190-
_attempt_submit_serialized_states(tick, full_state_to_broadcast, diff_state_to_broadcast, properties_to_include)
192+
_attempt_submit_diff_states(tick, full_state_to_broadcast, diff_state_to_broadcast, properties_to_include)
193+
else:
194+
_attempt_submit_full_states(tick, full_state_to_broadcast)
191195

192196
# Record state for specified tick ( current + 1 )
193197
if not _record_state_props.is_empty() and tick > _latest_state:
@@ -276,32 +280,38 @@ func _attempt_submit_serialized_inputs(serialized_inputs: PackedByteArray):
276280
elif not multiplayer.is_server():
277281
_submit_serialized_inputs.rpc_id(1, serialized_inputs)
278282

279-
func _attempt_submit_serialized_states(tick: int, full_state_to_broadcast: Dictionary, diff_state_to_broadcast: Dictionary, properties_to_include: Array[String]):
283+
func _attempt_submit_diff_states(tick: int, full_state_to_broadcast: Dictionary, diff_state_to_broadcast: Dictionary, properties_to_include: Array[String]):
280284
if (NetworkRollback.enable_state_serialization):
281285
var serialized_current_state: PackedByteArray = PropertiesSerializer.serialize_state_properties(tick, diff_state_to_broadcast, properties_to_include, _auth_state_props)
282286
_serialized_states[tick] = serialized_current_state
283287

284288
# Broadcast as new state
285289
for picked_peer_id in multiplayer.get_peers():
286-
if (_sent_full_state_to_peer_ids.has(picked_peer_id) && diff_state_to_broadcast.is_empty() == false):
290+
if (_sent_full_state_to_peer_ids.has(picked_peer_id)):
287291
_submit_serialized_state.rpc_id(picked_peer_id, serialized_current_state)
288292
else: #First state must be the full state
289-
var all_properties: Array[String] = []
290-
for picked_property_path in full_state_to_broadcast:
291-
all_properties.append(picked_property_path)
292-
293-
var serialized_full_state: PackedByteArray = PropertiesSerializer.serialize_state_properties(tick, full_state_to_broadcast, all_properties, _auth_state_props)
294-
_submit_serialized_state.rpc_id(picked_peer_id, serialized_full_state)
293+
var serialized_full_state: PackedByteArray = PropertiesSerializer.serialize_state_properties(tick, full_state_to_broadcast, state_properties, _auth_state_props)
294+
_submit_serialized_reliable_state.rpc_id(picked_peer_id, serialized_full_state)
295295
_sent_full_state_to_peer_ids.append(picked_peer_id)
296296
else:
297+
#print("At tick %s properties to include for %s are %s" % [tick, root.name ,properties_to_include])
297298
# Broadcast as new state
298299
for picked_peer_id in multiplayer.get_peers():
299-
if (_sent_full_state_to_peer_ids.has(picked_peer_id) && diff_state_to_broadcast.is_empty() == false):
300-
_submit_state.rpc_id(picked_peer_id, diff_state_to_broadcast, tick)
301-
else: #First state must be full
302-
_submit_state.rpc_id(picked_peer_id, full_state_to_broadcast, tick)
300+
if (_sent_full_state_to_peer_ids.has(picked_peer_id)):
301+
_submit_raw_state.rpc_id(picked_peer_id, diff_state_to_broadcast, tick)
302+
else:#Send the very first state
303+
_submit_reliable_state.rpc_id(picked_peer_id, full_state_to_broadcast, tick)
303304
_sent_full_state_to_peer_ids.append(picked_peer_id)
304305

306+
func _attempt_submit_full_states(tick: int, full_state_to_broadcast: Dictionary):
307+
if (NetworkRollback.enable_state_serialization):
308+
var serialized_full_state: PackedByteArray = PropertiesSerializer.serialize_state_properties(tick, full_state_to_broadcast, state_properties, _auth_state_props)
309+
for picked_peer_id in multiplayer.get_peers():
310+
_submit_serialized_state.rpc_id(picked_peer_id, serialized_full_state)
311+
else:
312+
for picked_peer_id in multiplayer.get_peers():
313+
_submit_raw_state.rpc_id(picked_peer_id, full_state_to_broadcast, tick)
314+
305315
func _get_history(buffer: Dictionary, tick: int) -> Dictionary:
306316
if buffer.has(tick):
307317
return buffer[tick]
@@ -391,36 +401,48 @@ func _submit_raw_input(input: Dictionary, tick: int):
391401
else:
392402
_logger.warning("Received invalid input from %s for tick %s for %s" % [sender, tick, root.name])
393403

404+
@rpc("any_peer", "reliable", "call_remote")
405+
func _submit_serialized_reliable_state(serialized_state: PackedByteArray):
406+
_submit_serialized_state(serialized_state)
407+
apply_cached_states()
408+
394409
@rpc("any_peer", "unreliable_ordered", "call_remote")
395410
func _submit_serialized_state(serialized_state: PackedByteArray):
396411
var received_tick: int = serialized_state.decode_u32(0)
397412
var state_values_size: int = serialized_state.decode_u16(4)
398413
var header_property_indexes_contained: int = serialized_state.decode_u16(6)
399414
var serialized_state_values: PackedByteArray = serialized_state.slice(10, 10 + state_values_size)
400-
var deserialized_state_of_this_tick: Dictionary
415+
var deserialized_state_of_this_tick: Dictionary = PropertiesSerializer.deserialize_state_properties(\
416+
serialized_state_values, _record_state_props, header_property_indexes_contained)
401417

402-
deserialized_state_of_this_tick = PropertiesSerializer.deserialize_state_properties(serialized_state_values, _record_state_props, header_property_indexes_contained)
403-
404-
_submit_state(deserialized_state_of_this_tick, received_tick)
418+
_submit_state(deserialized_state_of_this_tick, received_tick, multiplayer.get_remote_sender_id())
419+
420+
var initial_state: Dictionary = {}
421+
var cached_diff_states: Dictionary = {}
422+
423+
@rpc("any_peer", "reliable", "call_remote")
424+
func _submit_reliable_state(received_state: Dictionary, tick: int):
425+
_submit_state(received_state, tick, multiplayer.get_remote_sender_id())
426+
apply_cached_states()
405427

406428
@rpc("any_peer", "unreliable_ordered", "call_remote")
407-
func _submit_state(received_state: Dictionary, tick: int):
408-
if tick > NetworkTime.tick:
409-
# This used to be weird, but is now expected due to estimating remote time
410-
# push_warning("Received state from the future %s / %s - adding nonetheless" % [tick, NetworkTime.tick])
411-
pass
412-
429+
func _submit_raw_state(received_state: Dictionary, tick: int):
430+
_submit_state(received_state, tick, multiplayer.get_remote_sender_id())
431+
432+
@rpc("any_peer", "unreliable_ordered", "call_remote")
433+
func _submit_state(received_state: Dictionary, tick: int, sender: int):
413434
if tick < NetworkTime.tick - NetworkRollback.history_limit and _latest_state >= 0:
414435
# State too old!
415436
_logger.error("Received state for %s, rejecting because older than %s frames" % [tick, NetworkRollback.history_limit])
416437
return
417438

418-
var sender: int = multiplayer.get_remote_sender_id()
419439
var sanitized = {}
440+
var properties_included: int = 0
420441
for property in received_state:
421442
var pe: PropertyEntry = _property_cache.get_entry(property)
422443
var value = received_state[property]
423444
var state_owner: int = pe.node.get_multiplayer_authority()
445+
properties_included += 1
424446

425447
if state_owner != sender:
426448
_logger.warning("Received state for node owned by %s from %s, sender has no authority!" \
@@ -429,20 +451,51 @@ func _submit_state(received_state: Dictionary, tick: int):
429451

430452
sanitized[property] = value
431453

432-
#Missing properties means they didn't change from previous tick
433-
#so, set it as the previous one (extrapolation)
434-
for picked_property_path in state_properties:
435-
if (sanitized.has(picked_property_path) == false):
436-
if (_states.has(tick - 1)):
437-
if (_states[tick-1].has(picked_property_path)):
438-
sanitized[picked_property_path] = _states[tick - 1][picked_property_path]
454+
if (NetworkRollback.enable_state_diffs):
455+
var full_properties_count: int = state_properties.size()
456+
#print("At tick %s for %s properties included are: %s and total size is %s" % [tick, root.name, properties_included, full_properties_count])
457+
if (initial_state.is_empty()):
458+
if (properties_included < full_properties_count):
459+
if (sanitized.is_empty() == false):
460+
cached_diff_states[tick] = sanitized
461+
_logger.warning("Cached at tick %s " % tick)
462+
return
439463
else:
440-
_logger.error("Diff states error, _states of previous tick %s, doesn't have property %s" % [tick -1, picked_property_path])
464+
_logger.error("When initial state doesn't exist, received invalid state from %s for tick %s" % [sender, tick])
441465
else:
442-
_logger.error("Diff states error, current tick is %s and _states doesn't have previous tick %s" % [tick, tick - 1])
443-
466+
initial_state = sanitized
467+
468+
if (properties_included < full_properties_count):
469+
sanitized = reconstruct_state_from_missing_state(sanitized, tick)
470+
471+
#if (root.name != "Brawler #1"):
472+
#print("[%s]At tick %s for %s properties are: %s" % [multiplayer.get_unique_id(), tick, root.name, sanitized])
444473
if sanitized.size() > 0:
445474
_states[tick] = PropertySnapshot.merge(_states.get(tick, {}), sanitized)
446475
_latest_state = tick
447-
else:
476+
elif (not NetworkRollback.enable_state_diffs):
448477
_logger.warning("Received invalid state from %s for tick %s" % [sender, tick])
478+
479+
## In the case we have received diff states, but not the full state (yet), this function applies the diff states atop the full state
480+
func apply_cached_states():
481+
var picked_full_state: Dictionary
482+
for picked_tick in cached_diff_states:
483+
picked_full_state = reconstruct_state_from_missing_state(cached_diff_states[picked_tick], picked_tick)
484+
485+
_states[picked_tick] = PropertySnapshot.merge(_states.get(picked_tick, {}), picked_full_state)
486+
_latest_state = max(picked_tick, _latest_state)
487+
488+
func reconstruct_state_from_missing_state(diff_state: Dictionary, diff_state_tick: int) -> Dictionary:
489+
#Missing properties means they didn't change from previous tick
490+
#so, set it as the previous one (accurate extrapolation)
491+
for picked_property_path in state_properties:
492+
if (diff_state.has(picked_property_path) == false):
493+
if (_states.has(diff_state_tick - 1)):
494+
if (_states[diff_state_tick-1].has(picked_property_path)):
495+
diff_state[picked_property_path] = _states[diff_state_tick - 1][picked_property_path]
496+
else:
497+
_logger.error("Diff states error, _states of previous tick %s, doesn't have property %s" % [diff_state_tick -1, picked_property_path])
498+
else:
499+
_logger.error("Diff states error, current tick is %s and _states doesn't have previous tick %s" % [diff_state_tick, diff_state_tick - 1])
500+
501+
return diff_state

0 commit comments

Comments
 (0)