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.43.5"
version="1.44.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.43.5"
version="1.44.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.43.5"
version="1.44.0"
script="netfox-noray.gd"
4 changes: 4 additions & 0 deletions addons/netfox/netfox.gd
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ const AUTOLOADS: Array[Dictionary] = [
{
"name": "RollbackLivenessServer",
"path": ROOT + "/servers/rollback-liveness-server.gd"
},
{
"name": "InterpolationServer",
"path": ROOT + "/servers/interpolation-server.gd"
}
]

Expand Down
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.43.5"
version="1.44.0"
script="netfox.gd"
131 changes: 131 additions & 0 deletions addons/netfox/servers/interpolation-server.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
extends Node
class_name _InterpolationServer

# @public class

## Manages interpolation between network ticks
##
## Handles interpolation for multiple TickInterpolator nodes, storing snapshots
## and applying interpolation based on the network tick factor.

class InterpolationGroup:
var root: Node
var properties: Array[String]
var enabled: bool = true
var enable_recording: bool = true

var property_cache: PropertyCache
var property_entries: Array[PropertyEntry] = []
var interpolators: Dictionary = {}

var state_from: _PropertySnapshot
var state_to: _PropertySnapshot
var is_teleporting: bool = false

func _init():
state_from = _PropertySnapshot.new()
state_to = _PropertySnapshot.new()

var _groups: Dictionary = {} # int (group_id) to InterpolationGroup

static var _logger := NetfoxLogger._for_netfox("InterpolationServer")

## Register an interpolation group for a TickInterpolator node
func register_interpolation_group(group_id: int, root: Node, properties: Array[String], enabled: bool, enable_recording: bool) -> void:

if _groups.has(group_id):
deregister_interpolation_group(group_id)

var group := InterpolationGroup.new()
group.root = root
group.properties = properties.duplicate()
group.enabled = enabled
group.enable_recording = enable_recording

group.property_cache = PropertyCache.new(root)
group.property_entries.clear()
group.interpolators.clear()

for property in properties:
var property_entry = group.property_cache.get_entry(property)
group.property_entries.push_back(property_entry)
group.interpolators[property] = Interpolators.find_for(property_entry.get_value())

_groups[group_id] = group

## Deregister an interpolation group
func deregister_interpolation_group(group_id: int) -> void:
_groups.erase(group_id)

## Check if an interpolation group can interpolate
func can_interpolate(group_id: int) -> bool:
var group = _groups.get(group_id)
if not group:
return false

return group.enabled and not group.properties.is_empty() and not group.is_teleporting

## Record current state for interpolation
func push_state(group_id: int) -> void:
var group = _groups.get(group_id)
if not group:
_logger.warning("Trying to push state for unregistered group %d", [group_id])
return

group.state_from = group.state_to
group.state_to = _PropertySnapshot.extract(group.property_entries)

## Record current state and transition without interpolation
func teleport(group_id: int) -> void:
var group = _groups.get(group_id)
if not group:
_logger.warning("Trying to teleport unregistered group %d", [group_id])
return

if group.is_teleporting:
return

group.state_from = _PropertySnapshot.extract(group.property_entries)
group.state_to = group.state_from
group.is_teleporting = true

## Interpolate properties for a group
func interpolate(group_id: int, factor: float) -> void:
var group = _groups.get(group_id)
if not group:
return

if not group.enabled or group.is_teleporting:
return

for i in group.property_entries.size():
var property_entry = group.property_entries[i]
var property_path = property_entry.to_string()

if not group.state_from.has(property_path) or not group.state_to.has(property_path):
continue

var a = group.state_from.get_value(property_path)
var b = group.state_to.get_value(property_path)
var interpolate_fn = group.interpolators[property_path]

property_entry.set_value(interpolate_fn.call(a, b, factor))

func _ready() -> void:
if Engine.is_editor_hint():
return

NetworkTime.before_tick_loop.connect(_before_tick_loop)
NetworkTime.after_tick_loop.connect(_after_tick_loop)

func _before_tick_loop() -> void:
for group in _groups.values():
group.is_teleporting = false
group.state_to.apply(group.property_cache)

func _after_tick_loop() -> void:
for group in _groups.values():
if group.enable_recording and not group.is_teleporting:
group.state_from = group.state_to
group.state_to = _PropertySnapshot.extract(group.property_entries)
group.state_from.apply(group.property_cache)
1 change: 1 addition & 0 deletions addons/netfox/servers/interpolation-server.gd.uid
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
uid://d4773e5aa61b42caaf70f66632321e69
71 changes: 11 additions & 60 deletions addons/netfox/tick-interpolator.gd
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,17 @@ class_name TickInterpolator
## whenever properties are updated.
@export var enable_recording: bool = true

var _state_from: _PropertySnapshot
var _state_to: _PropertySnapshot
var _property_entries: Array[PropertyEntry] = []
var _group_id: int
var _properties_dirty: bool = false
var _interpolators: Dictionary = {}
var _is_teleporting: bool = false

var _property_cache: PropertyCache

## Process settings.
## [br][br]
## Call this after any change to configuration.
func process_settings():
_property_cache = PropertyCache.new(root)
_property_entries.clear()
_interpolators.clear()

_state_from = _PropertySnapshot.new()
_state_to = _PropertySnapshot.new()
if not root:
root = get_parent()

for property in properties:
var property_entry = _property_cache.get_entry(property)
_property_entries.push_back(property_entry)
_interpolators[property] = Interpolators.find_for(property_entry.get_value())
InterpolationServer.register_interpolation_group(_group_id, root, properties, enabled, enable_recording)

## Add a property to interpolate.
## [br][br]
Expand All @@ -69,25 +56,19 @@ func add_property(node: Variant, property: String):
## Even if it's enabled, no interpolation will be done if there are no
## properties to interpolate.
func can_interpolate() -> bool:
return enabled and not properties.is_empty() and not _is_teleporting
return InterpolationServer.can_interpolate(_group_id)

## Record current state for interpolation.
## [br][br]
## Note that this will rotate the states, so the previous target becomes the new
## starting point for the interpolation. This is automatically called if
## [code]enable_recording[/code] is true.
func push_state() -> void:
_state_from = _state_to
_state_to = _PropertySnapshot.extract(_property_entries)
InterpolationServer.push_state(_group_id)

## Record current state and transition without interpolation.
func teleport() -> void:
if _is_teleporting:
return

_state_from = _PropertySnapshot.extract(_property_entries)
_state_to = _state_from
_is_teleporting = true
InterpolationServer.teleport(_group_id)

func _notification(what) -> void:
if what == NOTIFICATION_EDITOR_PRE_SAVE:
Expand All @@ -106,20 +87,13 @@ func _get_configuration_warnings() -> PackedStringArray:
add_property(node, prop)
)

func _connect_signals() -> void:
NetworkTime.before_tick_loop.connect(_before_tick_loop)
NetworkTime.after_tick_loop.connect(_after_tick_loop)

func _disconnect_signals() -> void:
NetworkTime.before_tick_loop.disconnect(_before_tick_loop)
NetworkTime.after_tick_loop.disconnect(_after_tick_loop)

func _enter_tree() -> void:
_group_id = get_instance_id()

if Engine.is_editor_hint():
return

process_settings.call_deferred()
_connect_signals.call_deferred()

# Wait a frame for any initial setup before recording first state
if record_first_state:
Expand All @@ -130,40 +104,17 @@ func _exit_tree() -> void:
if Engine.is_editor_hint():
return

_disconnect_signals()
InterpolationServer.deregister_interpolation_group(_group_id)

func _process(_delta: float) -> void:
if Engine.is_editor_hint():
return

_interpolate(_state_from, _state_to, NetworkTime.tick_factor)
InterpolationServer.interpolate(_group_id, NetworkTime.tick_factor)

func _reprocess_settings() -> void:
if not _properties_dirty or Engine.is_editor_hint():
return

_properties_dirty = false
process_settings()

func _before_tick_loop() -> void:
_is_teleporting = false
_state_to.apply(_property_cache)

func _after_tick_loop() -> void:
if enable_recording and not _is_teleporting:
push_state()
_state_from.apply(_property_cache)

func _interpolate(from: _PropertySnapshot, to: _PropertySnapshot, f: float) -> void:
if not can_interpolate():
return

for property in from.properties():
if not to.has(property): continue

var property_entry := _property_cache.get_entry(property)
var a := from.get_value(property)
var b := to.get_value(property)
var interpolate = _interpolators[property] as Callable

property_entry.set_value(interpolate.call(a, b, f))
1 change: 1 addition & 0 deletions project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ NetworkSynchronizationServer="*res://addons/netfox/servers/network-synchronizati
NetworkIdentityServer="*res://addons/netfox/servers/network-identity-server.gd"
NetworkCommandServer="*res://addons/netfox/servers/network-command-server.gd"
RollbackLivenessServer="*res://addons/netfox/servers/rollback-liveness-server.gd"
InterpolationServer="*res://addons/netfox/servers/interpolation-server.gd"

[display]

Expand Down
Loading
Loading