Skip to content

Commit fe788ea

Browse files
Unified serialized input and raw input, under the same logic. Both for validation and for input batch count. Also makes raw input logic more intuitive and to match the _inputs. That is [tick][properties] instead of [properties][tick]
1 parent 64db09d commit fe788ea

2 files changed

Lines changed: 69 additions & 78 deletions

File tree

addons/netfox/rollback/rollback-synchronizer.gd

Lines changed: 56 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@ var _states: Dictionary = {} #<tick, Dictionary<String, Variant>>
2525
var _inputs: Dictionary = {} #<tick, Dictionary<String, Variant>>
2626
var _serialized_inputs: Dictionary = {} #<tick, PackedByteArray>
2727
var _serialized_states: Dictionary = {} #<tick, PackedByteArray>
28-
var _serialized_inputs_to_send: Array[PackedByteArray] = []
28+
29+
var _serialized_inputs_to_broadcast: Array[PackedByteArray] = []
30+
var _inputs_to_broadcast: Dictionary = {}
31+
2932
var _latest_state: int = -1
3033
var _earliest_input: int
3134
var _sent_full_state_to_peer_ids: Array[int] = []
@@ -210,31 +213,29 @@ func _after_tick(_delta: float, _tick: int):
210213
_inputs[_tick] = local_input
211214

212215
if (NetworkRollback.enable_input_serialization):
213-
var serialized_current_input: PackedByteArray = PropertiesSerializer.serialize_input_properties(_auth_input_props, _tick)
216+
var serialized_current_input: PackedByteArray = PropertiesSerializer.serialize_input_properties(_auth_input_props)
214217
_serialized_inputs[_tick] = serialized_current_input
215218

216-
if (_serialized_inputs_to_send.size() == NetworkRollback.input_redundancy):
217-
_serialized_inputs_to_send.remove_at(0)
218-
_serialized_inputs_to_send.append(serialized_current_input)
219+
if (_serialized_inputs_to_broadcast.size() == NetworkRollback.input_redundancy):
220+
_serialized_inputs_to_broadcast.remove_at(0)
221+
_serialized_inputs_to_broadcast.append(serialized_current_input)
219222

220-
if (_serialized_inputs_to_send.is_empty() == false):
221-
var merged_serialized_inputs: PackedByteArray
222-
merged_serialized_inputs.resize(0)
223-
for picked_serialized_input in _serialized_inputs_to_send:
224-
merged_serialized_inputs.append_array(picked_serialized_input)
225-
226-
_attempt_submit_serialized_inputs(merged_serialized_inputs)
223+
#The inputs will be deserialized from the tick of the first input to be broadcasted
224+
#Since they are sorted into that order (lowest towards highest)
225+
var tick_of_first_input_to_broadcast: int = _tick - _serialized_inputs_to_broadcast.size() + 1
226+
var merged_serialized_inputs: PackedByteArray
227+
merged_serialized_inputs.resize(4)
228+
merged_serialized_inputs.encode_u32(0, tick_of_first_input_to_broadcast)
229+
for picked_serialized_input in _serialized_inputs_to_broadcast:
230+
merged_serialized_inputs.append_array(picked_serialized_input)
231+
232+
_attempt_submit_serialized_inputs(merged_serialized_inputs)
227233
else:
228-
#Send the last n inputs for each property
229-
var inputs = {}
230-
for i in range(0, NetworkRollback.input_redundancy):
231-
var tick_input: Dictionary = _inputs.get(_tick - i, {})
232-
for property in tick_input:
233-
if not inputs.has(property):
234-
inputs[property] = []
235-
inputs[property].push_back(tick_input[property])
236-
237-
_attempt_submit_raw_input(inputs)
234+
if (_inputs_to_broadcast.size() == NetworkRollback.input_redundancy):
235+
_inputs_to_broadcast.erase(_inputs_to_broadcast.keys().min())
236+
_inputs_to_broadcast[_tick] = local_input
237+
238+
_attempt_submit_raw_input(_inputs_to_broadcast)
238239

239240
history_cleanup()
240241
func history_cleanup() -> void:
@@ -327,67 +328,58 @@ func _get_history(buffer: Dictionary, tick: int) -> Dictionary:
327328
@rpc("any_peer", "unreliable", "call_remote")
328329
func _submit_serialized_inputs(serialized_inputs: PackedByteArray):
329330
var sender: int = multiplayer.get_remote_sender_id()
331+
var tick_timestamp: int = serialized_inputs.decode_u32(0)
330332

331-
#TODO: Security check to ensure no other client sent this (when enable_input_broadcast == false), see sanitization in submit_raw_inputs
332-
333-
var picked_tick: int
333+
var received_properties_deserialized: Dictionary = {}
334+
335+
var picked_tick: int = tick_timestamp
334336
var picked_input_values_size: int #The size of the serialized input containing all properties (excluding tick timestamp[0,1,2,3] and the size itself on byte[4])
335337
var picked_single_input: PackedByteArray
336-
var picked_byte_index: int = 0
338+
var picked_byte_index: int = 4
337339
while (picked_byte_index < serialized_inputs.size()):
338-
picked_tick = serialized_inputs.decode_u32(picked_byte_index)
339-
picked_byte_index += 4
340340
picked_input_values_size = serialized_inputs.decode_u8(picked_byte_index)
341341
picked_byte_index += 1
342342

343343
if (_inputs.has(picked_tick) == false): #New input!
344344
picked_single_input = serialized_inputs.slice(picked_byte_index, picked_byte_index + picked_input_values_size)
345-
var received_properties: Dictionary
346345
if (_auth_input_props.is_empty()):
347-
received_properties = PropertiesSerializer.deserialize_input_properties(picked_single_input, _record_input_props)
346+
received_properties_deserialized[picked_tick] = PropertiesSerializer.deserialize_input_properties(picked_single_input, _record_input_props)
348347
else:
349-
received_properties = PropertiesSerializer.deserialize_input_properties(picked_single_input, _auth_input_props)
350-
351-
_earliest_input = min(_earliest_input, picked_tick)
352-
353-
if (_inputs.has(picked_tick) == false):
354-
_inputs[picked_tick] = received_properties
355-
else:
356-
for picked_property_path in received_properties:
357-
_inputs[picked_tick][picked_property_path] = received_properties[picked_property_path]
358-
348+
received_properties_deserialized[picked_tick] = PropertiesSerializer.deserialize_input_properties(picked_single_input, _auth_input_props)
359349

360350
picked_byte_index += picked_input_values_size
351+
picked_tick += 1
361352

362-
@rpc("any_peer", "unreliable", "call_remote")
363-
func _submit_raw_input(input: Dictionary, tick: int):
353+
submit_input(received_properties_deserialized, tick_timestamp, multiplayer.get_remote_sender_id())
354+
355+
@rpc("any_peer", "unreliable", "call_local")
356+
func _submit_raw_input(inputs: Dictionary, tick: int):
364357
var sender: int = multiplayer.get_remote_sender_id()
358+
submit_input(inputs, tick, sender)
365359

360+
func submit_input(inputs: Dictionary, tick: int, sender: int):
366361
var sanitized = {}
367-
for property in input:
368-
var pe: PropertyEntry = _property_cache.get_entry(property)
369-
var value = input[property]
370-
var input_owner: int = pe.node.get_multiplayer_authority()
371-
372-
if input_owner != sender:
373-
_logger.warning("Received input for node owned by %s from %s, sender has no authority!" \
374-
% [input_owner, sender])
375-
continue
376-
377-
sanitized[property] = value
362+
for picked_tick in inputs:
363+
sanitized[picked_tick] = {}
364+
for property in inputs[picked_tick]:
365+
var pe: PropertyEntry = _property_cache.get_entry(property)
366+
var value = inputs[picked_tick][property]
367+
var input_owner: int = pe.node.get_multiplayer_authority()
368+
369+
if input_owner != sender:
370+
_logger.warning("Received input for node owned by %s from %s, sender has no authority!" \
371+
% [input_owner, sender])
372+
continue
373+
374+
sanitized[picked_tick][property] = value
378375

379376
if sanitized.size() > 0:
380-
for property in sanitized:
381-
for i in range(0, sanitized[property].size()):
382-
var t = tick - i
383-
var old_input = _inputs.get(t, {}).get(property)
384-
var new_input = sanitized[property][i]
385-
386-
if old_input == null:
387-
# We received an array of current and previous inputs, merge them into our history.
388-
_inputs[t] = _inputs.get(t, {})
389-
_inputs[t][property] = new_input
390-
_earliest_input = min(_earliest_input, t)
377+
for picked_tick in sanitized:
378+
var old_input: Dictionary = _inputs.get(picked_tick, {})
379+
if (old_input.is_empty()): #New input! Merge it into our history
380+
_inputs[picked_tick] = sanitized[picked_tick]
381+
_earliest_input = min(_earliest_input, picked_tick)
382+
391383
else:
392384
_logger.warning("Received invalid input from %s for tick %s for %s" % [sender, tick, root.name])
393385

addons/netfox/serialization/properties-serializer.gd

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
extends Object
22
class_name PropertiesSerializer
33

4-
static func deserialize_input_properties(serialized_properties: PackedByteArray, auth_properties_template: Array[PropertyEntry]) -> Dictionary:
4+
static func deserialize_input_properties(serialized_properties: PackedByteArray, property_entries: Array[PropertyEntry]) -> Dictionary:
55
var reconstructed_dictionary: Dictionary = {} #Exclusively for that tick
66

7-
#print("Deserializing multiple properties of serialized input %s and array %s" % [serialized_properties, auth_properties_template])
87
var property_type: Variant.Type
98
var property_type_byte_size: int
109
var serialized_property: PackedByteArray
1110
var input_byte_index: int = 0
12-
for picked_property_entry in auth_properties_template:
11+
for picked_property_entry in property_entries:
1312
property_type = picked_property_entry.type
1413
property_type_byte_size = ValueToBytes.get_byte_size(property_type)
1514
serialized_property = serialized_properties.slice(input_byte_index, input_byte_index + property_type_byte_size)
@@ -18,26 +17,26 @@ static func deserialize_input_properties(serialized_properties: PackedByteArray,
1817
input_byte_index += property_type_byte_size
1918

2019
#print("path is %s and value is %s" % [picked_property_entry._path, picked_value])
21-
reconstructed_dictionary[picked_property_entry._path] = picked_value
20+
reconstructed_dictionary[picked_property_entry.to_string()] = picked_value
2221
return reconstructed_dictionary
2322

2423
## PropertySnapshot.extract() but in serialized format!
25-
static func serialize_input_properties(properties: Array[PropertyEntry], tick_timestamp: int) -> PackedByteArray:
24+
static func serialize_input_properties(property_entries: Array[PropertyEntry]) -> PackedByteArray:
25+
var result: PackedByteArray
26+
result.resize(1)
27+
2628
var value_bytes: PackedByteArray
2729
value_bytes.resize(0)
2830
var picked_value: Variant
2931
var picked_serialized_value: PackedByteArray
30-
for picked_property in properties:
31-
picked_value = picked_property.get_value()
32-
picked_serialized_value = ValueToBytes.serialize(picked_value)
32+
for picked_property_entry in property_entries:
33+
picked_value = picked_property_entry.get_value()
34+
picked_serialized_value = ValueToBytes.serialize_type(picked_value, picked_property_entry.type)
3335
value_bytes.append_array(picked_serialized_value)
34-
35-
var result: PackedByteArray
36-
result.resize(5)
37-
result.encode_u32(0, tick_timestamp)
38-
result.encode_u8(4, value_bytes.size())
39-
result.append_array(value_bytes)#now that the header is added, add the values
4036

37+
result.encode_u8(0, value_bytes.size())
38+
result.append_array(value_bytes)#now that the header is added, add the values
39+
4140
return result
4241

4342
## PropertySnapshot.extract() but in serialized format!

0 commit comments

Comments
 (0)