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
40 changes: 40 additions & 0 deletions addons/netfox/channel-manager.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
extends Node
class_name ChannelManager

var _available_channels: Array = []
var _assigned_channels: Dictionary = {}
var _channel_limit: int
var _is_enabled: bool = false

func _ready():
_channel_limit = ProjectSettings.get_setting("netfox/channel_manager/channel_limit", 16)
_init_channels()

func _init_channels() -> void:
_available_channels.resize(_channel_limit)
for i in _channel_limit:
_available_channels[i] = i + 1 # Channel 0 is default, start from 1

func is_enabled() -> bool:
return ProjectSettings.get_setting("netfox/channel_manager/enabled", false)

func get_channel() -> int:
if _available_channels.is_empty():
return -1 # No channels available
return _available_channels.pop_front()

func free_channel(channel: int) -> void:
if channel in _assigned_channels:
_assigned_channels.erase(channel)
_available_channels.append(channel)

func assign_channel(node: Node, channel: int) -> void:
_assigned_channels[channel] = node

func has_channel(channel: int) -> bool:
return channel in _assigned_channels

func set_channel_limit(limit: int) -> void:
_channel_limit = limit
_available_channels.clear()
_init_channels()
18 changes: 18 additions & 0 deletions addons/netfox/netfox.gd
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,19 @@ var SETTINGS = [
"name": "netfox/events/enabled",
"value": true,
"type": TYPE_BOOL
},
# Channel Manager settings
{
"name": "netfox/channel_manager/enabled",
"value": true,
"type": TYPE_BOOL
},
{
"name": "netfox/channel_manager/channel_limit",
"value": 50,
"type": TYPE_INT,
"hint": PROPERTY_HINT_RANGE,
"hint_string": "1,100,or_greater"
}
]

Expand All @@ -145,6 +158,10 @@ const AUTOLOADS = [
{
"name": "NetworkPerformance",
"path": ROOT + "/network-performance.gd"
},
{
"name": "ChannelManager",
"path": ROOT + "/channel-manager.gd"
}
]

Expand Down Expand Up @@ -178,6 +195,7 @@ func _enter_tree():

for type in TYPES:
add_custom_type(type.name, type.base, load(type.script), load(type.icon))


func _exit_tree():
if ProjectSettings.get_setting("netfox/general/clear_settings", false):
Expand Down
50 changes: 46 additions & 4 deletions addons/netfox/network-time-synchronizer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,52 @@ signal on_panic(offset: float)
##
## Starting multiple times has no effect.
func start():
if NetworkTime._channel_manager.is_enabled():
rpc_config("_request_ping", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_ANY_PEER
"transfer_mode": 1, # MultiplayerPeer.TRANSFER_MODE_UNRELIABLE
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
rpc_config("_respond_ping", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_ANY_PEER
"transfer_mode": 1, # MultiplayerPeer.TRANSFER_MODE_UNRELIABLE
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
rpc_config("_request_timestamp", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_ANY_PEER
"transfer_mode": 2, # MultiplayerPeer.TRANSFER_MODE_RELIABLE
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
rpc_config("_set_timestamp", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_ANY_PEER
"transfer_mode": 2, # MultiplayerPeer.TRANSFER_MODE_RELIABLE
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
else:
rpc_config("_request_ping", {
"rpc_mode": 3,
"transfer_mode": 1,
"call_local": false,
})
rpc_config("_respond_ping", {
"rpc_mode": 3,
"transfer_mode": 1,
"call_local": false,
})
rpc_config("_request_timestamp", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_ANY_PEER
"transfer_mode": 2, # MultiplayerPeer.TRANSFER_MODE_RELIABLE
"call_local": false
})
rpc_config("_set_timestamp", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_ANY_PEER
"transfer_mode": 2, # MultiplayerPeer.TRANSFER_MODE_RELIABLE
"call_local": false
})
if _active:
return

Expand Down Expand Up @@ -217,14 +263,12 @@ func _discipline_clock():

_offset = offset - nudge

@rpc("any_peer", "call_remote", "unreliable")
func _send_ping(idx: int):
var ping_received = _clock.get_time()
var sender = multiplayer.get_remote_sender_id()

_send_pong.rpc_id(sender, idx, ping_received, _clock.get_time())

@rpc("any_peer", "call_remote", "unreliable")
func _send_pong(idx: int, ping_received: float, pong_sent: float):
var pong_received = _clock.get_time()

Expand All @@ -246,12 +290,10 @@ func _send_pong(idx: int, ping_received: float, pong_sent: float):
# Discipline clock based on new sample
_discipline_clock()

@rpc("any_peer", "call_remote", "reliable")
func _request_timestamp():
_logger.debug("Requested initial timestamp @ %.4fs raw time", [_clock.get_raw_time()])
_set_timestamp.rpc_id(multiplayer.get_remote_sender_id(), _clock.get_time())

@rpc("any_peer", "call_remote", "reliable")
func _set_timestamp(timestamp: float):
_logger.debug("Received initial timestamp @ %.4fs raw time", [_clock.get_raw_time()])
_clock.set_time(timestamp)
Expand Down
23 changes: 22 additions & 1 deletion addons/netfox/network-time.gd
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,13 @@ var clock_offset: float:
var remote_clock_offset: float:
get:
return NetworkTimeSynchronizer.remote_offset


var _channel_manager: ChannelManager





## Emitted before a tick loop is run.
signal before_tick_loop()
Expand Down Expand Up @@ -492,6 +499,21 @@ func ticks_between(seconds_from: float, seconds_to: float) -> int:

func _ready():
_NetfoxLogger.register_tag(func(): return "@%d" % tick, -100)

_channel_manager = ChannelManager.new()
if _channel_manager.is_enabled():
rpc_config("_submit_sync_success", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_ANY_PEER
"transfer_mode": 2, # MultiplayerPeer.TRANSFER_MODE_RELIABLE
"call_local": true,
"channel": _channel_manager.get_channel()
})
else:
rpc_config("_submit_sync_success", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": true,
})

_tickrate_handshake = NetworkTickrateHandshake.new()
add_child(_tickrate_handshake)
Expand Down Expand Up @@ -567,7 +589,6 @@ func _notification(what):
func _is_active() -> bool:
return _state == _STATE_ACTIVE

@rpc("any_peer", "reliable", "call_local")
func _submit_sync_success():
var peer_id = multiplayer.get_remote_sender_id()

Expand Down
64 changes: 59 additions & 5 deletions addons/netfox/rollback/rollback-synchronizer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,65 @@ func _enter_tree():
if not NetworkTime.is_initial_sync_done():
# Wait for time sync to complete
await NetworkTime.after_sync

if NetworkTime._channel_manager.is_enabled():
rpc_config("_submit_inputs", {
"rpc_mode": 3,
"transfer_mode": 1,
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
rpc_config("_submit_full_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
rpc_config("_submit_diff_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
rpc_config("_ack_full_state", {
"rpc_mode": 3,
"transfer_mode": 0,
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
rpc_config("_ack_diff_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
else:
rpc_config("_submit_inputs", {
"rpc_mode": 3,
"transfer_mode": 1,
"call_local": false,
})
rpc_config("_submit_full_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
})
rpc_config("_submit_diff_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
})
rpc_config("_ack_full_state", {
"rpc_mode": 3,
"transfer_mode": 0,
"call_local": false,
})
rpc_config("_ack_diff_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
})

_connect_signals.call_deferred()
process_settings.call_deferred()

Expand Down Expand Up @@ -596,7 +655,6 @@ func _sanitize_by_authority(snapshot: Dictionary, sender: int) -> Dictionary:

return sanitized

@rpc("any_peer", "unreliable", "call_remote")
func _submit_inputs(inputs: Array, tick: int):
if not _is_initialized:
# Settings not processed yet
Expand Down Expand Up @@ -629,7 +687,6 @@ func _submit_inputs(inputs: Array, tick: int):
else:
_logger.warning("Received invalid input from %s for tick %s for %s" % [sender, tick, root.name])

@rpc("any_peer", "unreliable_ordered", "call_remote")
func _submit_full_state(state: Dictionary, tick: int):
if not _is_initialized:
# Settings not processed yet
Expand All @@ -654,7 +711,6 @@ func _submit_full_state(state: Dictionary, tick: int):
if NetworkRollback.enable_diff_states:
_ack_full_state.rpc_id(sender, tick)

@rpc("any_peer", "unreliable_ordered", "call_remote")
func _submit_diff_state(diff_state: Dictionary, tick: int, reference_tick: int):
if not _is_initialized:
# Settings not processed yet
Expand Down Expand Up @@ -693,14 +749,12 @@ func _submit_diff_state(diff_state: Dictionary, tick: int, reference_tick: int):
_ack_diff_state.rpc_id(sender, tick)
_next_diff_ack_tick = tick + diff_ack_interval

@rpc("any_peer", "reliable", "call_remote")
func _ack_full_state(tick: int):
var sender_id := multiplayer.get_remote_sender_id()
_ackd_state[sender_id] = tick

_logger.trace("Peer %d ack'd full state for tick %d", [sender_id, tick])

@rpc("any_peer", "unreliable_ordered", "call_remote")
func _ack_diff_state(tick: int):
var sender_id := multiplayer.get_remote_sender_id()
_ackd_state[sender_id] = tick
Expand Down
15 changes: 14 additions & 1 deletion addons/netfox/state-synchronizer.gd
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,20 @@ func _disconnect_signals():
func _enter_tree():
if Engine.is_editor_hint():
return

if ChannelManager.is_enabled():
rpc_config("_submit_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
else:
rpc_config("_submit_state", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
})

_connect_signals.call_deferred()
process_settings.call_deferred()
Expand All @@ -99,7 +113,6 @@ func _reprocess_settings():
_properties_dirty = false
process_settings()

@rpc("authority", "unreliable", "call_remote")
func _submit_state(state: Dictionary, tick: int):
if tick <= _last_received_tick:
return
Expand Down
14 changes: 13 additions & 1 deletion addons/netfox/time/network-tickrate-handshake.gd
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,19 @@ func stop() -> void:

func _ready() -> void:
name = "NetworkTickrateHandshake"
if NetworkTime._channel_manager.is_enabled():
rpc_config("_submit_tickrate", {
"rpc_mode": 3, # MultiplayerAPI.RPC_MODE_AUTHORITY
"transfer_mode": 2, # MultiplayerPeer.TRANSFER_MODE_RELIABLE
"call_local": false,
"channel": NetworkTime._channel_manager.get_channel()
})
else:
rpc_config("_submit_tickrate", {
"rpc_mode": 3,
"transfer_mode": 2,
"call_local": false,
})

func _handle_new_peer(peer: int):
if multiplayer.is_server():
Expand Down Expand Up @@ -89,7 +102,6 @@ func _handle_tickrate_mismatch(peer: int, tickrate: int) -> void:
SIGNAL:
on_tickrate_mismatch.emit(peer, tickrate)

@rpc("any_peer", "reliable", "call_remote")
func _submit_tickrate(tickrate: int) -> void:
var sender = multiplayer.get_remote_sender_id()
_logger.debug("Received tickrate %d from peer %d", [tickrate, sender])
Expand Down