diff --git a/addons/netfox.extras/plugin.cfg b/addons/netfox.extras/plugin.cfg index 9c9b8062..2b5189fa 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" -version="1.2.0" +version="1.3.0" script="netfox-extras.gd" diff --git a/addons/netfox.internals/plugin.cfg b/addons/netfox.internals/plugin.cfg index 38fb3be0..cac6e55a 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" -version="1.2.0" +version="1.3.0" script="plugin.gd" diff --git a/addons/netfox.noray/plugin.cfg b/addons/netfox.noray/plugin.cfg index 0b84cfd5..31df0ca8 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" -version="1.2.0" +version="1.3.0" script="netfox-noray.gd" diff --git a/addons/netfox/icons/state-synchronizer.svg b/addons/netfox/icons/state-synchronizer.svg new file mode 100644 index 00000000..757438d4 --- /dev/null +++ b/addons/netfox/icons/state-synchronizer.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + diff --git a/addons/netfox/icons/state-synchronizer.svg.import b/addons/netfox/icons/state-synchronizer.svg.import new file mode 100644 index 00000000..18f73f08 --- /dev/null +++ b/addons/netfox/icons/state-synchronizer.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ogbi1hffcoyh" +path="res://.godot/imported/state-synchronizer.svg-9cb9447ba79f114a58e468a24d17b860.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/netfox/icons/state-synchronizer.svg" +dest_files=["res://.godot/imported/state-synchronizer.svg-9cb9447ba79f114a58e468a24d17b860.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/addons/netfox/netfox.gd b/addons/netfox/netfox.gd index 220f2957..76bd6b13 100644 --- a/addons/netfox/netfox.gd +++ b/addons/netfox/netfox.gd @@ -104,6 +104,12 @@ const TYPES = [ "script": ROOT + "/rollback/rollback-synchronizer.gd", "icon": ROOT + "/icons/rollback-synchronizer.svg" }, + { + "name": "StateSynchronizer", + "base": "Node", + "script": ROOT + "/state-synchronizer.gd", + "icon": ROOT + "/icons/state-synchronizer.svg" + }, { "name": "TickInterpolator", "base": "Node", diff --git a/addons/netfox/plugin.cfg b/addons/netfox/plugin.cfg index 3faeef24..d9f3061e 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" -version="1.2.0" +version="1.3.0" script="netfox.gd" diff --git a/addons/netfox/state-synchronizer.gd b/addons/netfox/state-synchronizer.gd new file mode 100644 index 00000000..f8927581 --- /dev/null +++ b/addons/netfox/state-synchronizer.gd @@ -0,0 +1,45 @@ +extends Node +class_name StateSynchronizer + +## Synchronizes state from authority. + +@export var root: Node +@export var properties: Array[String] + +var _property_cache: PropertyCache +var _props: Array[PropertyEntry] + +var _last_received_tick: int = -1 +var _last_received_state: Dictionary = {} + +## Process settings. +## +## Call this after any change to configuration. +func process_settings(): + _property_cache = PropertyCache.new(root) + _props = [] + + for property in properties: + var pe = _property_cache.get_entry(property) + _props.push_back(pe) + +func _ready(): + process_settings() + NetworkTime.after_tick.connect(_after_tick) + +func _after_tick(_dt, tick): + if is_multiplayer_authority(): + # Submit snapshot + var state = PropertySnapshot.extract(_props) + rpc("_submit_state", state, tick) + else: + # Apply last received state + PropertySnapshot.apply(_last_received_state, _property_cache) + +@rpc("authority", "unreliable", "call_remote") +func _submit_state(state: Dictionary, tick: int): + if tick <= _last_received_tick: + return + + _last_received_state = state + _last_received_tick = tick diff --git a/docs/netfox/assets/state-synchronizer-config.png b/docs/netfox/assets/state-synchronizer-config.png new file mode 100644 index 00000000..f09ddfd5 Binary files /dev/null and b/docs/netfox/assets/state-synchronizer-config.png differ diff --git a/docs/netfox/nodes/state-synchronizer.md b/docs/netfox/nodes/state-synchronizer.md new file mode 100644 index 00000000..c4146891 --- /dev/null +++ b/docs/netfox/nodes/state-synchronizer.md @@ -0,0 +1,74 @@ +# StateSynchronizer + +Synchronizes state from the node's authority to other peers. + +Similar to Godot's [MultiplayerSynchronizer], but is tied to the [network tick +loop]. Works well with [TickInterpolator]. + +One way to use this node is to synchronize logic that runs only on the server, +for example NPC's in your games. The NPC's are controlled fully by the server, +and their state is synchronized to the clients by the *StateSynchronizer* +nodes. + +## Configuring state + +To use *StateSynchronizer*, add it as a child to the target node, specify the +root node, and configure which properties to synchronize: + +![StateSynchronizer configuration](../assets/state-synchronizer-config.png) + +*Root* specifies the root node for resolving properties. Best practice dictates +to add *StateSynchronizer* under its target, so *Root* will most often be the +*StateSynchronizer*'s parent node. + +*Properties* are recorded for each tick on the node's authority ( usually the +server ), and broadcast to other peers. These are analogous to +[RollbackSynchronizer]'s *state properties*. + +See [Property paths] on how to specify properties. + +## Changing configuration + +*StateSynchronizer* has to do some setup work whenever the state or the +input properties change. + +By default, this work is done upon instantiation. If you need to change +properties during runtime, make sure to call `process_settings()`, otherwise +*StateSynchronizer* won't apply the changes. + +You can change the node's authority without calling `process_settings()` again. +Make sure that the authority is changed the same way on all peers, to avoid +discrepancies. + +## When to use StateSynchronizer and MultiplayerSynchronizer + +Part of the design philosophy of netfox is to build *on top of* Godot's +networking tools, instead of *replacing* them. + +Both [MultiplayerSynchronizer] and StateSynchronizer can be used to synchronize +state from authority to the rest of the peers. + +[MultiplayerSynchronizer] uses its own timer, and is independent of netfox's +[network tick loop]. It can also do delta updates, and manage visibility per +peer. Since it is not tied to netfox's tick loop, it does not work with +[TickInterpolator]. + +StateSynchronizer records all the properties specified and broadcasts them +as-is to all peers. This does not include visiblity or delta updates. The +broadcast happens on every network tick. This node is explicitly designed to +work with [TickInterpolator]. + +--- + +You can use StateSynchronizer for properties that you want to be interpolated, +like position, rotation, or any other visual properties. + +You can use [MultiplayerSynchronizer] for properties that either don't need +interpolation ( e.g. a unit's HP ), or specifically need one of +[MultiplayerSynchronizer]'s features. + +[MultiplayerSynchronizer]: https://docs.godotengine.org/en/stable/classes/class_multiplayersynchronizer.html +[network tick loop]: ../guides/network-time.md +[TickInterpolator]: ./tick-interpolator.md +[RollbackSynchronizer]: ./rollback-synchronizer.md +[Property paths]: ../guides/property-paths.md diff --git a/mkdocs.yml b/mkdocs.yml index 53336071..c9db0804 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,7 @@ nav: - Nodes: - 'netfox/nodes/tick-interpolator.md' - 'netfox/nodes/rollback-synchronizer.md' + - 'netfox/nodes/state-synchronizer.md' - netfox.noray: - Guides: - 'netfox.noray/guides/noray.md'