### Start a Noray Host Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.noray/guides/noray.md Start listening on Noray's local port to host a game. Noray handles the rest of the connection process. ```gdscript var peer = ENetMultiplayerPeer.new() var err = peer.create_server(Noray.local_port) if err != OK: return false # Failed to listen on port ``` -------------------------------- ### Install Dependencies with Bun Source: https://github.com/foxssake/netfox/blob/main/sh/refdoc/README.md Use this command to install project dependencies using Bun. ```bash bun install ``` -------------------------------- ### Display State Change Example (Puml) Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.extras/guides/rewindable-state-machine.md Demonstrates a scenario where player state changes during rollback, affecting the display state and potentially triggering visual updates. ```puml @startuml concise "Player" as P @5 P is Moving @8 P is Idle @9 P is Jumping @enduml ``` -------------------------------- ### Start NetworkTime Tick Loop Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-time.md Start the network tick loop by calling the NetworkTime.start() coroutine. On servers, it starts immediately. On clients, it synchronizes time with the server before starting. ```gdscript NetworkTime.start() ``` -------------------------------- ### GDScript Netfox Example Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/netfox-sharp.md This is a snippet of GDScript code demonstrating netfox signal connections and rollback tick handling. ```gdscript func _ready(): NetworkTime.before_tick_loop.connect(_gather) func _gather(): # Input gathering here pass func _rollback_tick(delta, tick, is_fresh): # Rollback logic here pass ``` -------------------------------- ### Notify Resimulation Start Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-rollback.md During the 'before_loop' phase, nodes must notify NetworkRollback about the earliest tick from which resimulation should begin by calling this method. ```csharp NetworkRollback.notify_resimulation_start ``` -------------------------------- ### Implement Idle State with Transitions Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.extras/guides/rewindable-state-machine.md This example shows how to implement an idle state that transitions to other states based on player input. Ensure the 'input' variable is correctly exported and linked to your player input state machine. ```gdscript extends RewindableState @export var input: PlayerInputStateMachine func tick(delta, tick, is_fresh): if input.movement != Vector3.ZERO: state_machine.transition(&"Move") elif input.jump: state_machine.transition(&"Jump") ``` -------------------------------- ### Register and Send Custom Command Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-command-server.md Register a custom command with a callback and send it using its send method. Ensure commands are set up once at game start, and autoloads run after netfox's. ```gdscript @onready var cmd_message := NetworkCommandServer.register_command(handle_message, MultiplayerPeer.TRANSFER_MODE_UNRELIABLE) func handle_message(sender: int, data: PackedByteArray) -> void: var message := data.get_string_from_utf8() print("#%d: %s" % [sender, message]) func _ready() -> void: cmd_message.send("Hello, world!".to_utf8_buffer()) ``` -------------------------------- ### Registering a Network Schema Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-schemas.md Use the `set_schema()` method on a synchronizer to define the properties and their corresponding serializers for network transmission. This example shows how to register transform, velocity, speed, mass, and input properties. ```gdscript rollback_synchronizer.set_schema({ ":transform": NetworkSchemas.transform3f32(), ":velocity": NetworkSchemas.vec3f32(), ":speed": NetworkSchemas.float32(), ":mass": NetworkSchemas.float32(), "Input:movement": NetworkSchemas.vec3f32(), "Input:aim": NetworkSchemas.vec3f32() }) ``` -------------------------------- ### Custom Node Serializer Example Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-schemas.md Extends NetworkSchemaSerializer to encode a node's path. Implement encode() and decode() methods. ```gdscript class ExampleNodeSerializer extends NetworkSchemaSerializer: func encode(value): # Ensure value is a Node, otherwise return null if not value is Node: return null # Encode the node's path return var_to_bytes(value.get_path()) func decode(bytes): # Decode the path from bytes var path = bytes_to_var(bytes) # Get the node from the path return get_tree().get_first_node_in_group(path) ``` -------------------------------- ### C# Netfox Sharp Equivalent Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/netfox-sharp.md This C# code is functionally identical to the GDScript netfox example. It shows how to connect to signals and handle rollback ticks using Netfox Sharp's static members and PascalCase conventions. ```csharp public override void _Ready() { // All netfox autoloads like NetworkTime are accessed through static members // in NetfoxSharp, to save on GetNode() calls and reduce clutter in the // project settings. // All members like BeforeTickLoop are in PascalCase, similar to Godot's C# NetfoxSharp.NetworkTime.BeforeTickLoop += Gather; } // As Gather is linked to a signal, it can be any naming convention. private void Gather() { // Input gathering here } // Since _rollback_tick isn't connected to a signal and is instead handled by // netfox internally, netfox's naming convention must be followed. public void _rollback_tick(double delta, long tick, bool isFresh) { // Rollback logic here } ``` -------------------------------- ### Get All Input Submissions Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-rollback.md Retrieves a dictionary containing all tracked nodes and their respective latest input submission ticks. This provides a comprehensive overview of input status across the system. ```csharp NetworkRollback.get_input_submissions() ``` -------------------------------- ### Server Notification for Client Sync Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-time.md On the server, use the NetworkTime.after_client_sync(peer_id) signal to get notified once a client has successfully synchronized its time and started the tick loop. ```gdscript NetworkTime.after_client_sync.connect(on_client_sync) ``` -------------------------------- ### Run Project with Bun Source: https://github.com/foxssake/netfox/blob/main/sh/refdoc/README.md Use this command to run the main TypeScript file of the project using Bun. ```bash bun run main.ts ``` -------------------------------- ### Export RewindableAction Node Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/nodes/rewindable-action.md Demonstrates how to get a reference to a RewindableAction node from a script using @onready or @export. ```gdscript @onready var rewindable_action := $RewindableAction as RewindableAction @export var rewindable_action: RewindableAction ``` -------------------------------- ### Instantiate and Log Messages Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/logging.md Create a logger instance with a module and name, then log messages at different levels. Use this for general debugging and diagnostic output. ```gdscript var logger := NetfoxLogger.new("my-game", "Player") logger.trace("Detailed message") logger.debug("Something happened") logger.info("Hi!") logger.warning("Couldn't connect") logger.error("Game missing?") ``` -------------------------------- ### Connect and Register with Noray Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.noray/guides/noray.md Connect to a Noray host, register the current host's ID, and then register the remote address for incoming connections. This process also sets the local port for receiving traffic. ```gdscript var host = "some.noray.host" var port = 8890 var err = OK # Connect to noray err = await Noray.connect_to_host(host, port) if err != OK: return err # Failed to connect # Register host Noray.register_host() await Noray.on_pid # Register remote address # This is where noray will direct traffic err = await Noray.register_remote() if err != OK: return err # Failed to register ``` -------------------------------- ### Get Latest Input Tick Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-rollback.md Queries the latest tick for which a root node has submitted input. This is useful for delaying irreversible actions until their outcome is certain. ```csharp NetworkRollback.get_latest_input_tick(root_node) ``` -------------------------------- ### Implement Input Prediction Logic Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/predicting-input.md This snippet demonstrates how to implement input prediction by connecting to the `after_prepare_tick` signal. It checks if prediction is needed, gathers input, calculates a confidence score based on input age, and modulates the movement vector accordingly. Use this when you need to predict player actions based on historical input. ```gdscript extends BaseNetInput var movement: Vector3 var confidence: float = 1. @onready var _rollback_synchronizer := "../RollbackSynchronizer" as RollbackSynchronizer func _ready(): ssuper() # Predict on `after_prepare_tick` NetworkRollback.after_prepare_tick.connect(_predict) func _gather(): # Gather input movement = Vector3( Input.get_axis("move_east", "move_west"), Input.get_action_strength("move_jump"), Input.get_axis("move_south", "move_north") ) func _predict(_t): if not _rollback_synchronizer.is_predicting(): # Not predicting, nothing to do confidence = 1. return if not _rollback_synchronizer.has_input(): # Can't predict without input confidence = 0. return # Decay input over a short time var decay_time := NetworkTime.seconds_to_ticks(.15) var input_age := _rollback_synchronizer.get_input_age() # **ALWAYS** cast either side to float, otherwise the integer-integer # division yields either 1 or 0 confidence confidence = input_age / float(decay_time) confidence = clampf(1. - confidence, 0., 1.) # Modulate input based on confidence movement *= confidence ``` -------------------------------- ### Connect as a Client to Noray Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.noray/guides/noray.md Initiate a connection to a game host using Noray. Supports both NAT punchthrough and relay methods. ```gdscript var oid = "abcd1234" # Connect using NAT punchthrough Noray.connect_nat(oid) # Or connect using relay Noray.connect_relay(oid) ``` -------------------------------- ### Gather Player Input with BaseNetInput Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/responsive-player-movement.md Utilize `BaseNetInput` for a more concise way to gather player input. This class handles some boilerplate, allowing direct implementation of the `_gather` method. ```gdscript extends BaseNetInput class_name PlayerInput var movement: Vector3 = Vector3.ZERO func _gather(): movement = Vector3( Input.get_axis("move_west", "move_east"), Input.get_action_strength("move_jump"), Input.get_axis("move_north", "move_south") ) ``` -------------------------------- ### Register Custom Interpolator Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/interpolators.md Register a custom interpolator for a specific data type by providing a condition function and an interpolation function. This can also override built-in interpolators. The interpolation function receives the start value, end value, and interpolation factor. ```gdscript Interpolators.register( func(a): return a is float, # Condition func(a, b, f): return lerpf(a, b, f * f) # Interpolation ) ``` -------------------------------- ### Player State Transitions (Puml) Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.extras/guides/rewindable-state-machine.md Illustrates a sequence of player state changes over game ticks, including Idle, Moving, and Jumping, as part of a rollback simulation. ```puml @startuml concise "Player" as P @0 P is Idle @1 P is Moving @3 P is Jumping @5 P is Moving @8 P is Idle @enduml ``` -------------------------------- ### Sample Continuous Player Input Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/input-gathering-tips-and-tricks.md Samples player input on every _process() frame and averages the samples before each tick loop. This ensures all inputs are accounted for, especially with differing tick rates and FPS. ```gdscript --8<-- "examples/snippets/input-gathering-tutorial/continuous-sampled-input.gd" ``` -------------------------------- ### Implement Property Discovery in Editor Scripts Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/configuring-properties-from-code.md Implement these methods in `@tool` scripts to automatically discover and add properties to Netfox nodes. Results are added to the node configuration when the scene is opened, changed, or saved. ```gdscript func _get_interpolated_properties(): # Specify a list of properties return ["position", "quaternion"] ``` ```gdscript func _get_synchronized_state_properties() -> Array: # Specify inherited properties and more return super() + [ "health", "name", [weapon, "ammo"], # Specify a property on another node ["Hand/Weapon", "ammo"] # Specify node by path ] ``` ```gdscript func _get_rollback_state_properties() -> Array: return [ "transform", # Specify a property on self [weapon, "ammo"] # Specify a property on another node ] ``` ```gdscript func _get_rollback_input_propertes() -> Array: # Specify a list of properties return ["movement", "is_jumping"] ``` -------------------------------- ### Client Connection Handling with Noray Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.noray/guides/noray.md Connect to a remote host using NAT punchthrough or a relay. Ensure the local port is always specified for the client to maintain connectivity. ```gdscript func _ready(): Noray.on_connect_nat.connect(_handle_connect) Noray.on_connect_relay.connect(_handle_connect) func _handle_connect(address: String, port: int) -> Error: # Do a handshake var udp = PacketPeerUDP.new() udp.bind(Noray.local_port) udp.set_dest_address(address, port) var err = await PacketHandshake.over_packet_peer(udp) udp.close() if err != OK: return err # Connect to host var peer = ENetMultiplayerPeer.new() err = peer.create_client(address, port, 0, 0, 0, Noray.local_port) if err != OK: return err return OK ``` -------------------------------- ### Gather Player Input Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/responsive-player-movement.md Connect to the `NetworkTime.before_tick_loop` signal to gather player input. Ensure input properties are only updated if the node has multiplayer authority. ```gdscript func _ready(): NetworkTime.before_tick_loop.connect(_gather) func _gather(): if not is_multiplayer_authority(): return movement = Vector3( Input.get_axis("move_west", "move_east"), Input.get_action_strength("move_jump"), Input.get_axis("move_north", "move_south") ) ``` -------------------------------- ### Handshake Process Diagram Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.noray/guides/packet-handshake.md This PlantUML diagram illustrates the packet handshake process, including initial denied packets, router updates, successful packet exchange, and final connection establishment. ```puml @startuml actor "Player A" as A entity "Router A" as RA boundary Internet as Net entity "Router B" as RB actor "Player B" as B A ->x RB : $-w- note over RB: Packet denied B ->x RA : $-w- note over RA: Packet denied note over RA, RB: NAT table updated on both routers A -> B: $-w- note over RB: Packed allowed B -> A: $-w- note over RB: Packed allowed A -> B: $rwx B -> A: $rwx note over Net #lightgreen: Connection established @enduml ``` -------------------------------- ### Implement Rollback Lifecycle Methods Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/spawning-and-despawning-in-rollback.md Implement these methods on a node to make it liveness aware. `_rollback_despawn` is called when a node needs to be deactivated before simulating a tick, and `_rollback_spawn` is called when it needs to be activated. ```gdscript func _rollback_spawn() -> void: pass func _rollback_despawn() -> void: pass ``` -------------------------------- ### Implement BaseNetInput _gather Method Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.extras/guides/base-net-input.md Override the _gather method to collect input for rollback simulations. Ensure variables configured in RollbackSynchronizer are set within your implementation. ```gdscript extends BaseNetInput var movement: Vector3 = Vector3.ZERO func _gather(): movement = Vector3( Input.get_axis("move_west", "move_east"), 0, Input.get_axis("move_north", "move_south") ) ``` -------------------------------- ### Initialize RewindableRandomNumberGenerator with a hashed node path Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.extras/guides/rewindable-random-number-generator.md Initialize the generator by hashing the node's path. This ensures consistency for dynamically spawned nodes, provided they maintain the same path across all peers. ```gdscript var rng := RewindableRandomNumberGenerator.new(hash(get_path())) ``` -------------------------------- ### Host Connection Handling with Noray Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.noray/guides/noray.md Handle incoming connection requests for NAT punchthrough. The host handshake assumes the target is responsive and sends packets without expecting immediate replies. ```gdscript func _ready(): Noray.on_connect_nat.connect(_handle_connect) Noray.on_connect_relay.connect(_handle_connect) func _handle_connect(address: String, port: int) -> Error: var peer = get_tree().get_multiplayer().multiplayer_peer as ENetMultiplayerPeer var err = await PacketHandshake.over_enet(peer.host, address, port) if err != OK: return err return OK ``` -------------------------------- ### Connect to NetworkTime's on_tick Event Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-time.md Connect a handler function to the NetworkTime.on_tick signal to execute game logic during each network tick. This ensures game updates run at a consistent rate, independent of frame rates. ```gdscript extends Node3D @export var speed = 4.0 func _ready(): NetworkTime.on_tick.connect(_tick) func _tick(delta, tick): # Move forward position += basis.z * delta * speed ``` -------------------------------- ### Notify Node Simulation Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-rollback.md In 'on_prepare_tick' handlers, nodes should register themselves as being simulated for the current rollback tick by calling this method. This is useful for other nodes to track simulation status. ```csharp NetworkRollback.notify_simulated ``` -------------------------------- ### Network Tick Loop Visualization Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-time.md This diagram illustrates the flow of the network tick loop, showing the sequence of events before, during, and after each tick, and the loop's condition for continuation. ```puml @startuml start :before_tick_loop; while (Ticks to simulate) is (>0) :before_tick; :on_tick; :after_tick; endwhile (0) :after_tick_loop; stop @enduml ``` -------------------------------- ### Requesting Node Despawn with Synchronizer Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/spawning-and-despawning-in-rollback.md Find the synchronizer managing the node and call its `despawn()` method to request that the node be despawned. This ensures all managed nodes despawn and spawn in unison. ```gdscript extends Node3D # [...] @onready var synchronizer := $PredictiveSynchronizer as PredictiveSynchronizer # [...] func _rollback_tick(dt: float, _t: int, _if: bool) -> void: # [...] if distance_left < 0: synchronizer.despawn() return ``` -------------------------------- ### Initialize RewindableRandomNumberGenerator with a hashed string seed Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.extras/guides/rewindable-random-number-generator.md Initialize the generator using a hash of a consistent string identifier. This is useful for singletons or objects with stable names. ```gdscript var rng := RewindableRandomNumberGenerator.new(hash("Exit beacon")) ``` -------------------------------- ### Configure Rollback Synchronizer Ownership Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/tutorials/responsive-player-movement.md Set multiplayer authority for player nodes, ensuring the server owns most nodes and the client owns the input node. Call `process_settings` on `RollbackSynchronizer` after any ownership changes. ```gdscript @onready var rollback_synchronizer = $RollbackSynchronizer var peer_id = 0 func _ready(): # Wait a frame so peer_id is set await get_tree().process_frame # Set owner set_multiplayer_authority(1) input.set_multiplayer_authority(peer_id) rollback_synchronizer.process_settings() ``` -------------------------------- ### Play Sound After Action Confirmation Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/nodes/rewindable-action.md Use `has_confirmed()` to play a sound effect only when an action has been confirmed since the last tick loop. Connect to `NetworkTime.after_tick_loop` to ensure this check runs at the appropriate time. ```gdscript @onready var fire_action := "Fire Action" as RewindableAction func _ready(): NetworkTime.after_tick_loop.connect(_after_loop) # ... func _after_loop(): if fire_action.has_confirmed(): sound.play() ``` -------------------------------- ### Add a Visibility Filter Callback Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/visibility-management.md Register a callback function to dynamically filter peers. This filter runs before per-peer overrides. The callback receives the peer ID and should return a boolean indicating visibility. ```gdscript filter.add_visibility_filter(func(peer: int): # Forbidden trick to halve your bandwidth :P return (peer % 2) == 0 ) ``` -------------------------------- ### Network Rollback Loop Diagram Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-rollback.md This diagram illustrates the isolated network rollback loop, detailing the sequence of events and signal handlers involved in preparing, processing, and recording ticks during resimulation. ```puml @startuml start :before_loop; while(Rollback) :on_prepare_tick; :after_prepare_tick; :on_process_tick; :on_record_tick; endwhile :after_loop; stop @enduml ``` -------------------------------- ### Simulate Projectile from Fired Tick Source: https://github.com/foxssake/netfox/blob/main/docs/netfox.extras/guides/network-weapon.md Use this in `_after_fire` to simulate projectile movement from the time it was fired to the current tick, compensating for latency. Ensure to check if the projectile is queued for deletion before simulating. ```gdscript func _after_fire(projectile: Node3D): last_fire = get_fired_tick() for t in range(get_fired_tick(), NetworkTime.tick): if projectile.is_queued_for_deletion(): break projectile._tick(NetworkTime.ticktime, t) ``` -------------------------------- ### Using Custom Serializer in Schema Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-schemas.md Sets a custom serializer for the 'Input:target' schema using RollbackSynchronizer. ```gdscript rollback_synchronizer.set_schema({ "Input:target": ExampleNodeSerializer.new() }) ``` -------------------------------- ### Network Tick Loop with Rollback Source: https://github.com/foxssake/netfox/blob/main/docs/netfox/guides/network-rollback.md This diagram shows the network rollback loop integrated within the broader network tick loop, highlighting that rollback operations occur before user code connected to the after_tick_loop signal. ```puml @startuml start :NetworkTime.before_tick_loop; while (Ticks to simulate) is (>0) :NetworkTime.before_tick; :NetworkTime.on_tick; :NetworkTime.after_tick; endwhile :NetworkRollback.before_loop; while(Rollback) :NetworkRollback.on_prepare_tick; :NetworkRollback.after_prepare_tick; :NetworkRollback.on_process_tick; :NetworkRollback.on_record_tick; endwhile :NetworkRollback.after_loop; :NetworkTime.after_tick_loop; stop @enduml ```