diff --git a/addons/netfox.extras/plugin.cfg b/addons/netfox.extras/plugin.cfg index ceda79ac..51541e16 100644 --- a/addons/netfox.extras/plugin.cfg +++ b/addons/netfox.extras/plugin.cfg @@ -3,5 +3,5 @@ name="netfox.extras" description="Game-specific utilities for Netfox" author="Tamas Galffy" -version="1.6.1" +version="1.7.0" script="netfox-extras.gd" diff --git a/addons/netfox.internals/plugin.cfg b/addons/netfox.internals/plugin.cfg index 03ade4a3..9e820387 100644 --- a/addons/netfox.internals/plugin.cfg +++ b/addons/netfox.internals/plugin.cfg @@ -3,5 +3,5 @@ name="netfox.internals" description="Shared internals for netfox addons" author="Tamas Galffy" -version="1.6.1" +version="1.7.0" script="plugin.gd" diff --git a/addons/netfox.noray/plugin.cfg b/addons/netfox.noray/plugin.cfg index da426251..cda78a42 100644 --- a/addons/netfox.noray/plugin.cfg +++ b/addons/netfox.noray/plugin.cfg @@ -3,5 +3,5 @@ name="netfox.noray" description="Bulletproof your connectivity with noray integration for netfox" author="Tamas Galffy" -version="1.6.1" +version="1.7.0" script="netfox-noray.gd" diff --git a/addons/netfox/network-time.gd b/addons/netfox/network-time.gd index 08cd7152..ea018a54 100644 --- a/addons/netfox/network-time.gd +++ b/addons/netfox/network-time.gd @@ -196,7 +196,7 @@ var ticktime: float: var tick_factor: float: get: if not sync_to_physics: - return 1.0 - clampf(_next_tick * tickrate, 0, 1) + return 1.0 - clampf((_next_tick_time - _last_process_time) * tickrate, 0, 1) else: return Engine.get_physics_interpolation_fraction() set(v): @@ -257,11 +257,13 @@ signal after_sync() signal after_client_sync(peer_id: int) var _tick: int = 0 -var _next_tick: float = 0 var _active: bool = false var _initial_sync_done = false var _process_delta: float = 0 +var _next_tick_time: float = 0 +var _last_process_time: float = 0. + var _remote_rtt: float = 0 var _remote_tick: int = 0 var _local_tick: int = 0 @@ -302,12 +304,14 @@ func start(): _local_tick = _remote_tick _initial_sync_done = true _active = true + _next_tick_time = _get_os_time() after_sync.emit() rpc_id(1, "_submit_sync_success") else: _active = true _initial_sync_done = true + _next_tick_time = _get_os_time() after_sync.emit() # Remove clients from the synced cache when disconnected @@ -359,26 +363,34 @@ func _ready(): NetworkTimeSynchronizer.on_sync.connect(_handle_sync) func _process(delta): + # Use OS delta to determine if the game's paused from editor, or through the SceneTree + var os_delta = _get_os_time() - _last_process_time + var is_delta_mismatch = os_delta / delta > 4. and os_delta > .5 + + # Adjust next tick time if the game is paused, so we don't try to "catch up" after unpausing + if (is_delta_mismatch and Engine.is_editor_hint()) or get_tree().paused: + _next_tick_time += os_delta + _process_delta = delta + _last_process_time += os_delta + # Run tick loop if needed if _active and not sync_to_physics: - _next_tick -= delta - var ticks_in_loop = 0 - while _next_tick < 0 and ticks_in_loop < max_ticks_per_frame: + while _next_tick_time < _last_process_time and ticks_in_loop < max_ticks_per_frame: if ticks_in_loop == 0: before_tick_loop.emit() _run_tick() ticks_in_loop += 1 - _next_tick += ticktime + _next_tick_time += ticktime if ticks_in_loop > 0: after_tick_loop.emit() func _physics_process(delta): - if _active and sync_to_physics: + if _active and sync_to_physics and not get_tree().paused: # Run a single tick every physics frame before_tick_loop.emit() _run_tick() @@ -393,6 +405,9 @@ func _run_tick(): _remote_tick +=1 _local_tick += 1 +func _get_os_time() -> float: + return Time.get_ticks_msec() / 1000. + func _handle_sync(server_time: float, server_tick: int, rtt: float): _remote_tick = server_tick _remote_rtt = rtt diff --git a/addons/netfox/plugin.cfg b/addons/netfox/plugin.cfg index 60f73bb7..783562b2 100644 --- a/addons/netfox/plugin.cfg +++ b/addons/netfox/plugin.cfg @@ -3,5 +3,5 @@ name="netfox" description="Shared internals for netfox addons" author="Tamas Galffy" -version="1.6.1" +version="1.7.0" script="netfox.gd" diff --git a/docs/netfox/guides/network-time.md b/docs/netfox/guides/network-time.md index 1609f112..6285f2c1 100644 --- a/docs/netfox/guides/network-time.md +++ b/docs/netfox/guides/network-time.md @@ -63,6 +63,29 @@ To get notified when a client successfully syncs their time and starts the tick loop, use the `NetworkTime.after_client_sync(peer_id)` signal. This is fired once per client, and only on the server. +## Pausing + +*NetworkTime* also supports pausing the game, if needed. There's two cases +where pauses are considered. + +When running ( and pausing ) the game from the editor, the network tick loop +is automatically paused. As there's currently no API to detect the editor +pausing the game, *NetworkTime* checks if Godot's `_process` delta and actual +delta is mismatching, and if so, considers the game paused. In some cases, this +can result in false positives when the game simply hangs for a bit, e.g. when +loading resources. + +This pause detection only happens when the game is run from the editor, to +avoid false positives in production builds. + +The other supported case is pausing the game from the engine itself. Whenever +`SceneTree.paused` is set to true, *NetworkTime* won't run the tick loop. + +> *Note* that pausing the tick loop can cause desynchronization between peers, +and could lead to clients fast-forwarding ticks to catch up, or time +recalibrations. If the game is paused via SceneTree, make sure it is paused and +unpaused at the same time on all peers. + ## Time synchronization *NetworkTime* runs a time synchronization loop on clients, in the background.