Skip to content

Rollback Refactor - _HistoryBuffer Addition#397

Closed
DustieDog wants to merge 5 commits intofoxssake:epic/refactor-rollback-synchronizerfrom
DustieDog:Rollback-Refactor
Closed

Rollback Refactor - _HistoryBuffer Addition#397
DustieDog wants to merge 5 commits intofoxssake:epic/refactor-rollback-synchronizerfrom
DustieDog:Rollback-Refactor

Conversation

@DustieDog
Copy link
Copy Markdown
Collaborator

Draft PR for #385.

Currently the client is bugged, resulting in local artifacts and unintended behaviour on the host (Client player doesn't move smoothly, and moves faster than expected in Forest Brawl)

Bugged but complete RollbackSync
Data now properly sends over RPC. Introduces a bug where clients move faster than expected, and also get local artifacting.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plan to rename this PropertySnapshot prior to the rollback refactor being merged to main. Using unique name for now to avoid potential conflicts with existing property-snapshot.gd references until we're sure it's safe to switch.

@DustieDog DustieDog marked this pull request as draft February 5, 2025 14:33
@elementbound elementbound changed the base branch from epic/refactor-rollback-synchronizer to main February 5, 2025 20:19
@elementbound elementbound changed the base branch from main to epic/refactor-rollback-synchronizer February 5, 2025 20:20
Copy link
Copy Markdown
Contributor

@elementbound elementbound left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kudos for the PR! Added some comments. Feel free to leave the unit tests for the end, I'm happy to add them.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this class is already specialized to snapshots. Is the original HistoryBuffer > PropertyHistoryBuffer combo intentionally skipped?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note to self, since this is now refactored into its own class, we can now add unit tests!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this class is already specialized to snapshots. Is the original HistoryBuffer > PropertyHistoryBuffer combo intentionally skipped?

PropertyHistoryBuffer is just a dictionary. Taking your previous comment:

No, that's PropertyHistoryBuffer. The distinction is analogous to List<T> vs. List<StateSnapshot>

HistoryBuffer is just a Dictionary[String, PropertyStoreSnapshot], there's nothing I can see that can be refactored out of that which a Dictionary[String, T] doesn't already do. If a use case pops up later for something of the sort we can look at it then though.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to have a generic collection class ( HistoryBuffer ), and a specialized one extending it, that we use. Part of it is pure preference of having generic collections, part of it is that it would enable netfox to provide users a way to associate arbitrary data ( not just snapshots ) with ticks.

there's nothing I can see that can be refactored out of that which a Dictionary[String, T] doesn't already do
I believe merge is one such method that is specific to property snapshots.

extends Object
class_name PropertyStoreSnapshot

# Typed as Dictionary[String, Property]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While at it, what do you think about semantically documenting dictionaries? E.g.:

Suggested change
# Typed as Dictionary[String, Property]
# Maps property path strings to Property instances

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dictionary[String, Property] is more direct and therefore readable to me personally, perhaps both are warranted?

var input := inputs[offset] as Dictionary
if input == null:

var input := PropertyStoreSnapshot.deserialize(inputs[offset] as Dictionary)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the cast needed here?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was at one point to fix an RPC error, further testing is needed to see if it's still required.

Comment on lines +597 to +600

var input := PropertyStoreSnapshot.deserialize(inputs[offset] as Dictionary)

if input.size() == 0:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This got swapped with the original null-check. The original idea was to prevent against accidental nulls present in the received input array.

I'd recommend something like this instead:

Suggested change
var input := PropertyStoreSnapshot.deserialize(inputs[offset] as Dictionary)
if input.size() == 0:
var raw_input := inputs[offset] as Dictionary
if raw_input == null:
# We've somehow received a null input - shouldn't happen
_logger.error("Null input received for %d, full batch is %s", [input_tick, inputs])
continue
var input := PropertyStoreSnapshot.deserialize(raw_input)

Note: Couldn't comment on the unchanged parts so this is not safe to commit as-is 😄

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts of moving this into PropertyStoreSnapshot.deserialize() instead?

if not sanitized.is_empty():
var known_input := _inputs.get(input_tick)
var known_input := _inputs.get_snapshot(input_tick)
# TODO: Does this work with PropertyStoreSnapshot?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not, as these are compared by reference IIRC. A unit test would decide though 😄 I'm not aware of any way to override the == operator in Godot, so our best bet to write a .equals() method.

return


var deserialized = PropertyStoreSnapshot.deserialize(state)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var deserialized = PropertyStoreSnapshot.deserialize(state)
var deserialized := PropertyStoreSnapshot.deserialize(state)

Copy link
Copy Markdown
Collaborator Author

@DustieDog DustieDog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed @elementbound's notes, no disagreements.

Comment on lines +112 to +113
_states.clear_history()
_inputs.clear_history()
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should calls to non-dictionaries follow dictionary conventions? IE should these functions be named clear()?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, missed this during the in-depth review. I'd prefer that, especially since clearing history is a given for a history buffer.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this class is already specialized to snapshots. Is the original HistoryBuffer > PropertyHistoryBuffer combo intentionally skipped?

PropertyHistoryBuffer is just a dictionary. Taking your previous comment:

No, that's PropertyHistoryBuffer. The distinction is analogous to List<T> vs. List<StateSnapshot>

HistoryBuffer is just a Dictionary[String, PropertyStoreSnapshot], there's nothing I can see that can be refactored out of that which a Dictionary[String, T] doesn't already do. If a use case pops up later for something of the sort we can look at it then though.

_retrieved_tick = earliest_tick
return buffer[earliest_tick]
func _sanitize_by_authority(snapshot: PropertyStoreSnapshot, sender: int) -> PropertyStoreSnapshot:
var sanitized := PropertyStoreSnapshot.new()
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I read somewhere that modifying dictionaries in a for loop can cause issues, but I can't find a reference.

var input := inputs[offset] as Dictionary
if input == null:

var input := PropertyStoreSnapshot.deserialize(inputs[offset] as Dictionary)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was at one point to fix an RPC error, further testing is needed to see if it's still required.

Comment on lines +597 to +600

var input := PropertyStoreSnapshot.deserialize(inputs[offset] as Dictionary)

if input.size() == 0:
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are your thoughts of moving this into PropertyStoreSnapshot.deserialize() instead?

Comment on lines +32 to +36
func get_earliest_tick() -> int:
return _history.keys().min()

func get_latest_tick() -> int:
return _history.keys().max()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might make sense to cache these, if these methods keep being used in the final PR. Needs benchmarking.

@DustieDog
Copy link
Copy Markdown
Collaborator Author

Closing in favour of #399.

@DustieDog DustieDog closed this Feb 12, 2025
@DustieDog DustieDog deleted the Rollback-Refactor branch February 12, 2025 20:52
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.

2 participants