From 2b4278d3b995b98ff64cfccd93408db0531c4124 Mon Sep 17 00:00:00 2001 From: TuysuzTavsan Date: Wed, 8 Jul 2026 21:00:50 +0300 Subject: [PATCH 1/4] add headless check --- addons/netfox/tick-interpolator.gd | 5 +++++ docs/netfox/tutorials/interpolation-caveats.md | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/addons/netfox/tick-interpolator.gd b/addons/netfox/tick-interpolator.gd index eeb3b2004..54f826578 100644 --- a/addons/netfox/tick-interpolator.gd +++ b/addons/netfox/tick-interpolator.gd @@ -32,6 +32,11 @@ var _properties := _PropertyPool.new() ## [br][br] ## Call this after any change to configuration. func process_settings(): + # Dont process if godot is running in headless mode. + # We dont have anything to display. + if DisplayServer.get_name() == "headless": + return + if not root: root = get_parent() diff --git a/docs/netfox/tutorials/interpolation-caveats.md b/docs/netfox/tutorials/interpolation-caveats.md index ff003062a..f7c0d0abe 100644 --- a/docs/netfox/tutorials/interpolation-caveats.md +++ b/docs/netfox/tutorials/interpolation-caveats.md @@ -16,6 +16,10 @@ though it's important for simulating their behavior. Since interpolation matters only for the game's visuals, it's enough to interpolate only the properties that affect the game's visuals. +!!! note + If Godot is running in headless mode, interpolation is disabled by default, + since no rendering is taking place. + ### Rotation vs. Quaternion vs. Transform Interpolating `rotation` may lead to glitchy results when an object makes a From 1e9e0e3059d1d428aec86c19cfe984ccea98ee3a Mon Sep 17 00:00:00 2001 From: TuysuzTavsan Date: Wed, 8 Jul 2026 21:50:40 +0300 Subject: [PATCH 2/4] add headless checks for interpolation server --- addons/netfox/servers/interpolation-server.gd | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/addons/netfox/servers/interpolation-server.gd b/addons/netfox/servers/interpolation-server.gd index 39cdbbe2b..c3bc8e61d 100644 --- a/addons/netfox/servers/interpolation-server.gd +++ b/addons/netfox/servers/interpolation-server.gd @@ -32,6 +32,9 @@ static var _logger := NetfoxLogger._for_netfox("InterpolationServer") ## [method set_recording] to configure the subject after registration. If the ## property is already registered for this subject, nothing happens. func register(subject: Node, property: NodePath, interpolator: Interpolators.Interpolator = null) -> void: + if _is_headless(): + return + if not _properties.has_subject(subject): # Subject wasn't registered before, setup defaults _interpolators[subject] = {} @@ -51,6 +54,9 @@ func register(subject: Node, property: NodePath, interpolator: Interpolators.Int ## Deregister all properties for a [param subject]. func deregister(subject: Node) -> void: + if _is_headless(): + return + _state_from.erase_subject(subject) _state_to.erase_subject(subject) @@ -69,6 +75,9 @@ func has_subject(subject: Node) -> bool: ## [br][br] ## See [method is_enabled]. func set_enabled(subject: Node, enabled: bool) -> void: + if _is_headless(): + return + if enabled: _enabled.add(subject) else: @@ -86,6 +95,9 @@ func is_enabled(subject: Node) -> bool: ## [br][br] ## See [method is_recording]. func set_recording(subject: Node, enabled: bool) -> void: + if _is_headless(): + return + if enabled: _recording_enabled.add(subject) else: @@ -121,6 +133,9 @@ func can_interpolate(subject: Node) -> bool: ## [br][br] ## Called automatically, unless disabled with [method set_recording]. func push_state(subject: Node) -> void: + if _is_headless(): + return + if not has_subject(subject): _logger.warning("Trying to push state for unregistered subject %s", [subject]) return @@ -140,6 +155,9 @@ func push_state(subject: Node) -> void: ## Skip interpolation for this tick. func teleport(subject: Node) -> void: + if _is_headless(): + return + if is_teleporting(subject): return if not has_subject(subject): @@ -158,6 +176,9 @@ func is_teleporting(subject: Node) -> bool: ## [br][br] ## Called automatically by default. func interpolate_subject(subject: Node, factor: float) -> void: + if _is_headless(): + return + if not can_interpolate(subject): return @@ -192,3 +213,6 @@ func _apply_target_state() -> void: func _record_next_state() -> void: for subject in _recording_enabled.values(): push_state(subject) + +func _is_headless() -> bool: + return DisplayServer.get_name() == "headless" From 2b8b203965fa8b1f542507b2db66a6bdb00dec0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20G=C3=A1lffy?= Date: Thu, 16 Jul 2026 23:16:56 +0200 Subject: [PATCH 3/4] adj --- addons/netfox/servers/interpolation-server.gd | 54 ++++++++++++++----- addons/netfox/tick-interpolator.gd | 5 -- docs/netfox/guides/interpolators.md | 8 +-- docs/netfox/nodes/tick-interpolator.md | 4 ++ .../netfox/tutorials/interpolation-caveats.md | 4 +- .../servers/interpolation-server.test.gd | 3 ++ 6 files changed, 53 insertions(+), 25 deletions(-) diff --git a/addons/netfox/servers/interpolation-server.gd b/addons/netfox/servers/interpolation-server.gd index c3bc8e61d..c124a08be 100644 --- a/addons/netfox/servers/interpolation-server.gd +++ b/addons/netfox/servers/interpolation-server.gd @@ -11,7 +11,11 @@ class_name _InterpolationServer ## This server can interpolate properties of any arbitrary type. ## See [Interpolators] for specifics on how interpolation is implemented. ## [br][br] -## +## Interpolation can be toggled globally, using [method set_server_enabled]. +## Interpolation is disabled by default when running headless, since no +## rendering is taking place. + +var _enabled := true var _properties := _PropertyPool.new() var _interpolators: Dictionary = {} # {subject Node: {property_path String: Interpolator}} @@ -19,7 +23,7 @@ var _interpolators: Dictionary = {} # {subject Node: {property_path String: I var _state_from := _Snapshot.new(0) var _state_to := _Snapshot.new(0) -var _enabled := _Set.new() +var _enabled_subjects := _Set.new() var _recording_enabled := _Set.new() var _teleporting := _Set.new() @@ -32,13 +36,13 @@ static var _logger := NetfoxLogger._for_netfox("InterpolationServer") ## [method set_recording] to configure the subject after registration. If the ## property is already registered for this subject, nothing happens. func register(subject: Node, property: NodePath, interpolator: Interpolators.Interpolator = null) -> void: - if _is_headless(): + if not is_server_enabled(): return if not _properties.has_subject(subject): # Subject wasn't registered before, setup defaults _interpolators[subject] = {} - _enabled.add(subject) + _enabled_subjects.add(subject) _recording_enabled.add(subject) if _properties.has(subject, property): @@ -54,7 +58,7 @@ func register(subject: Node, property: NodePath, interpolator: Interpolators.Int ## Deregister all properties for a [param subject]. func deregister(subject: Node) -> void: - if _is_headless(): + if not is_server_enabled(): return _state_from.erase_subject(subject) @@ -63,7 +67,7 @@ func deregister(subject: Node) -> void: _properties.erase_subject(subject) _interpolators.erase(subject) - _enabled.erase(subject) + _enabled_subjects.erase(subject) _recording_enabled.erase(subject) _teleporting.erase(subject) @@ -75,13 +79,13 @@ func has_subject(subject: Node) -> bool: ## [br][br] ## See [method is_enabled]. func set_enabled(subject: Node, enabled: bool) -> void: - if _is_headless(): + if not is_server_enabled(): return if enabled: - _enabled.add(subject) + _enabled_subjects.add(subject) else: - _enabled.erase(subject) + _enabled_subjects.erase(subject) ## Return true if the [param subject] is enabled for interpolation. ## [br][br] @@ -89,13 +93,13 @@ func set_enabled(subject: Node, enabled: bool) -> void: ## [br][br] ## See [method set_enabled]. func is_enabled(subject: Node) -> bool: - return _enabled.has(subject) + return _enabled_subjects.has(subject) ## Enable or disable automatic state recording for a [param subject]. ## [br][br] ## See [method is_recording]. func set_recording(subject: Node, enabled: bool) -> void: - if _is_headless(): + if not is_server_enabled(): return if enabled: @@ -117,6 +121,9 @@ func is_recording(subject: Node) -> bool: ## May return false for multiple reasons - subject is unknown, not enabled for ## interpolation, or is currently teleporting. func can_interpolate(subject: Node) -> bool: + if not is_server_enabled(): + # Interpolation is disabled globally + return false if not has_subject(subject): # Unknown subject, can't interpolate return false @@ -129,11 +136,26 @@ func can_interpolate(subject: Node) -> bool: return true +## Toggle interpolation globally. +## [br][br] +## If turned off, no interpolation will be done. Additionally, all other methods +## will become inert - for example, [member register] will do nothing. +## [br][br] +## See [method is_server_enabled]. +func set_server_enabled(enabled: bool) -> void: + _enabled = enabled + +## Return true if interpolation is enabled. +## [br][br] +## See [method set_server_enabled]. +func is_server_enabled() -> bool: + return _enabled + ## Record current state for interpolation. ## [br][br] ## Called automatically, unless disabled with [method set_recording]. func push_state(subject: Node) -> void: - if _is_headless(): + if not is_server_enabled(): return if not has_subject(subject): @@ -155,7 +177,7 @@ func push_state(subject: Node) -> void: ## Skip interpolation for this tick. func teleport(subject: Node) -> void: - if _is_headless(): + if not is_server_enabled(): return if is_teleporting(subject): @@ -176,7 +198,7 @@ func is_teleporting(subject: Node) -> bool: ## [br][br] ## Called automatically by default. func interpolate_subject(subject: Node, factor: float) -> void: - if _is_headless(): + if not is_server_enabled(): return if not can_interpolate(subject): @@ -216,3 +238,7 @@ func _record_next_state() -> void: func _is_headless() -> bool: return DisplayServer.get_name() == "headless" + +func _ready() -> void: + # Disable by default if headless + _enabled = not _is_headless() diff --git a/addons/netfox/tick-interpolator.gd b/addons/netfox/tick-interpolator.gd index 54f826578..eeb3b2004 100644 --- a/addons/netfox/tick-interpolator.gd +++ b/addons/netfox/tick-interpolator.gd @@ -32,11 +32,6 @@ var _properties := _PropertyPool.new() ## [br][br] ## Call this after any change to configuration. func process_settings(): - # Dont process if godot is running in headless mode. - # We dont have anything to display. - if DisplayServer.get_name() == "headless": - return - if not root: root = get_parent() diff --git a/docs/netfox/guides/interpolators.md b/docs/netfox/guides/interpolators.md index 5616d4dc9..be5b660eb 100644 --- a/docs/netfox/guides/interpolators.md +++ b/docs/netfox/guides/interpolators.md @@ -2,10 +2,10 @@ Tracks interpolators for various data types. Provided as a static class. -To smooth out motion between network ticks, [TickInterpolator] interpolates -nodes' state properties between the current and the previous tick. The type of -data to be interpolated is not known in advance, and can be any built-in or -even custom type configured by the developer. +To smooth motion between network ticks, [TickInterpolator] interpolates nodes' +state properties between the current and the previous tick. The type of data to +be interpolated is not known in advance, and can be any built-in or even custom +type configured by the developer. *Interpolators* provides methods to register interpolators for any data type, and even provides some for built-in data types. diff --git a/docs/netfox/nodes/tick-interpolator.md b/docs/netfox/nodes/tick-interpolator.md index 3447beacc..27bbcc1b3 100644 --- a/docs/netfox/nodes/tick-interpolator.md +++ b/docs/netfox/nodes/tick-interpolator.md @@ -5,6 +5,10 @@ Interpolates between network ticks to smooth out motion. Uses [Interpolators] under the hood to support various data types. To read more on best practices, see [Interpolation caveats]. +!!! note + When running in headless mode, interpolation is disabled by default, since + no rendering is taking place. + ## Configuring interpolation To use *TickInterpolator*, add it as a child to the target node, specify the diff --git a/docs/netfox/tutorials/interpolation-caveats.md b/docs/netfox/tutorials/interpolation-caveats.md index f7c0d0abe..585540140 100644 --- a/docs/netfox/tutorials/interpolation-caveats.md +++ b/docs/netfox/tutorials/interpolation-caveats.md @@ -17,8 +17,8 @@ Since interpolation matters only for the game's visuals, it's enough to interpolate only the properties that affect the game's visuals. !!! note - If Godot is running in headless mode, interpolation is disabled by default, - since no rendering is taking place. + When running in headless mode, interpolation is disabled by default, since + no rendering is taking place. ### Rotation vs. Quaternion vs. Transform diff --git a/test/netfox/servers/interpolation-server.test.gd b/test/netfox/servers/interpolation-server.test.gd index c0dcf47a2..b1fa06e71 100644 --- a/test/netfox/servers/interpolation-server.test.gd +++ b/test/netfox/servers/interpolation-server.test.gd @@ -20,6 +20,9 @@ func before_case(__): await interpolation_server.ready await test_node.ready + # Make sure to enable server, even when running headless + interpolation_server.set_server_enabled(true) + func after_case(__): interpolation_server.queue_free() test_node.queue_free() From 81f28e89f4af4ab4ab3a593a91dd2ff996f0e63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20G=C3=A1lffy?= Date: Thu, 16 Jul 2026 23:17:26 +0200 Subject: [PATCH 4/4] bv --- addons/netfox.extras/plugin.cfg | 2 +- addons/netfox.internals/plugin.cfg | 2 +- addons/netfox.noray/plugin.cfg | 2 +- addons/netfox/plugin.cfg | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/netfox.extras/plugin.cfg b/addons/netfox.extras/plugin.cfg index 733054e72..bfa8af859 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 and contributors" -version="1.47.2" +version="1.48.0" script="netfox-extras.gd" diff --git a/addons/netfox.internals/plugin.cfg b/addons/netfox.internals/plugin.cfg index f044ef674..917f50ae7 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 and contributors" -version="1.47.2" +version="1.48.0" script="plugin.gd" diff --git a/addons/netfox.noray/plugin.cfg b/addons/netfox.noray/plugin.cfg index a5c7fa1a9..9ba6e8f94 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 and contributors" -version="1.47.2" +version="1.48.0" script="netfox-noray.gd" diff --git a/addons/netfox/plugin.cfg b/addons/netfox/plugin.cfg index 7ea9f8208..c6f0f5750 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 and contributors" -version="1.47.2" +version="1.48.0" script="netfox.gd"