Last active
February 20, 2025 05:54
-
-
Save jamonholmgren/246d51174c2ebbadd7fe3085056ab0ef to your computer and use it in GitHub Desktop.
*In-progress, doesn't work (yet).* A simpler sync system for Godot.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class_name JamminSync extends Node | |
# In-progress, doesn't work (yet). | |
# A simpler sync system for Godot. | |
# Add as an autoload named "Sync" or let JamminSync handle it. | |
# Usage: | |
# Sync.sync_prop_reliable(self, "prop_name") | |
# Sync.sync_prop_unreliable(self, "prop_name") | |
@export var log_syncs: bool = true | |
@export var log_missing_nodes: bool = true | |
var _synced_props: Dictionary = {} | |
# TODO: make this not run on the physics process, but rather call_deferred | |
func should_sync(obj_path: NodePath, prop: StringName, every: int = 1, sync_mode: StringName = &"on_change") -> bool: | |
if every < 1: return false | |
var frames = Engine.get_physics_frames() | |
var is_on_tick = frames % every == 0 | |
# Always sync means always (on ticks) | |
if sync_mode == &"always": return is_on_tick | |
# Initialize cached values | |
if not _synced_props.has(obj_path): _synced_props[obj_path] = { prop: { &"last_value": null } } | |
if not _synced_props[obj_path].has(prop): _synced_props[obj_path][prop] = { &"last_value": null } | |
var is_changed = _synced_props[obj_path][prop][&"last_value"] != get(prop) | |
# Don't do a sync, even though it's changed, if we're not on the tick | |
if sync_mode == &"on_change" and not is_changed: return false | |
if is_changed: | |
_synced_props[obj_path][prop][&"last_changed"] = frames | |
_synced_props[obj_path][prop][&"last_value"] = get(prop) | |
_synced_props[obj_path][prop][&"every"] = every | |
return true | |
# No change, so no sync | |
if sync_mode == &"on_change": return false | |
# Dynamic mode! | |
# In this mode, we sync when the value changes, and fairly quickly for | |
# a while, but then less and less often, until it changes again. | |
var last_changed = _synced_props[obj_path][prop][&"last_changed"] | |
var time_since_last_change = frames - last_changed | |
var should_update = time_since_last_change % _synced_props[obj_path][prop][&"every"] == 0 | |
# Gradually increase the sync delay over time | |
if should_update and _synced_props[obj_path][prop][&"every"] < every * 10: _synced_props[obj_path][prop][&"every"] += 1 | |
return should_update | |
func sync_prop_reliable(obj: Node, prop: StringName, every: int = 1, sync_mode: StringName = &"on_change"): | |
if not Lobby.is_authority(obj): return | |
# print("Syncing %s.%s" % [obj.get_path(), prop]) | |
var should = should_sync(obj.get_path(), prop, every, sync_mode) | |
if should: print("Should sync: %s.%s = %s" % [obj.get_path(), prop, should]) | |
if should: remote_sync_prop_reliable.rpc(obj.get_path(), prop, get(prop)) | |
func sync_prop_unreliable(obj: Node, prop: StringName, every: int = 1, sync_mode: StringName = &"on_change"): | |
if not Lobby.is_authority(obj): return | |
if should_sync(obj.get_path(), prop, every, sync_mode): remote_sync_prop_unreliable.rpc(obj.get_path(), prop, get(prop)) | |
@rpc("call_local", "reliable", "authority") | |
func remote_sync_prop_reliable(obj_path: NodePath, prop: StringName, value: Variant): | |
push_error("Rel Sync: %s.%s = %s" % [obj_path, prop, value]) | |
# TODO: allow optionally lerping to the new value quickly | |
var obj = get_node(obj_path) | |
if not obj: | |
if log_missing_nodes: push_error("Sync: missing node %s" % obj_path) | |
return | |
obj.set(prop, value) | |
@rpc("call_local", "unreliable", "authority") | |
func remote_sync_prop_unreliable(obj_path: NodePath, prop: StringName, value: Variant): | |
push_error("Unr Sync: %s.%s = %s" % [obj_path, prop, value]) | |
# TODO: allow optionally lerping to the new value quickly | |
var obj = get_node(obj_path) | |
if not obj: | |
if log_missing_nodes: push_error("Sync: missing node %s" % obj_path) | |
return | |
obj.set(prop, value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment