Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion addons/netfox.extras/plugin.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion addons/netfox.internals/plugin.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion addons/netfox.noray/plugin.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 1 addition & 1 deletion addons/netfox/plugin.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -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"
64 changes: 57 additions & 7 deletions addons/netfox/servers/interpolation-server.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ 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}}

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()

Expand All @@ -32,10 +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 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):
Expand All @@ -51,13 +58,16 @@ func register(subject: Node, property: NodePath, interpolator: Interpolators.Int

## Deregister all properties for a [param subject].
func deregister(subject: Node) -> void:
if not is_server_enabled():
return

_state_from.erase_subject(subject)
_state_to.erase_subject(subject)

_properties.erase_subject(subject)
_interpolators.erase(subject)

_enabled.erase(subject)
_enabled_subjects.erase(subject)
_recording_enabled.erase(subject)
_teleporting.erase(subject)

Expand All @@ -69,23 +79,29 @@ func has_subject(subject: Node) -> bool:
## [br][br]
## See [method is_enabled].
func set_enabled(subject: Node, enabled: bool) -> void:
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]
## If the subject is enabled, it will be interpolated between ticks.
## [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 not is_server_enabled():
return

if enabled:
_recording_enabled.add(subject)
else:
Expand All @@ -105,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
Expand All @@ -117,10 +136,28 @@ 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 not is_server_enabled():
return

if not has_subject(subject):
_logger.warning("Trying to push state for unregistered subject %s", [subject])
return
Expand All @@ -140,6 +177,9 @@ func push_state(subject: Node) -> void:

## Skip interpolation for this tick.
func teleport(subject: Node) -> void:
if not is_server_enabled():
return

if is_teleporting(subject):
return
if not has_subject(subject):
Expand All @@ -158,6 +198,9 @@ func is_teleporting(subject: Node) -> bool:
## [br][br]
## Called automatically by default.
func interpolate_subject(subject: Node, factor: float) -> void:
if not is_server_enabled():
return

if not can_interpolate(subject):
return

Expand Down Expand Up @@ -192,3 +235,10 @@ 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"

func _ready() -> void:
# Disable by default if headless
_enabled = not _is_headless()
8 changes: 4 additions & 4 deletions docs/netfox/guides/interpolators.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions docs/netfox/nodes/tick-interpolator.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions docs/netfox/tutorials/interpolation-caveats.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
When 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
Expand Down
3 changes: 3 additions & 0 deletions test/netfox/servers/interpolation-server.test.gd
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading