b0c83af092
Complete replacement of the tactical-shooter project with the netfox-cs-sample (MIT) — a CS 1.6 inspired multiplayer FPS built with Godot 4 and netfox. ## What's new - Full CS-style gameplay: teams (T/CT), rounds, economy, buy menu - 6 weapons: Knife, Glock, USP, AK-47, M4A1, AWP - Bomb plant/defuse with 2 bombsites - Flashbang & smoke grenades - Proper netfox rollback netcode at 64 tick - Network popup UI for host/join - HUD, crosshair, round timer, scoreboard - All netfox singletons registered as autoloads (works in exported builds) ## Architecture - Listen-server (host from client, no dedicated server binary) - Multiplayer-fps game lives at examples/multiplayer-fps/ - Netfox addons registered as autoloads for exported build compat - Godot 4.7 with Forward+ renderer ## Removed - Old headless-server architecture (client_main, server_main, player.gd, etc.) - Custom netfox bootstrap with ENet fallback - Old ChaffGames FPS template (2,420 lines, 844 KB) - SimulationServer GDExtension stub - Godot-jolt physics (netfox sample uses default Godot physics) - Duplicate weapon_data.gd, anti_cheat.gd, round_manager.gd, etc. - Server browser API Python venv (87 MB) - test_range map and modular assets ## Preserved - Git history - Server config at config/default_server_config.cfg - Windows export preset - Build directory (gitignored) Co-authored-by: naxIO <naxIO@users.noreply.github.com>
95 lines
3.7 KiB
Markdown
95 lines
3.7 KiB
Markdown
# NetworkCommandServer
|
|
|
|
Implements a simpler, lightweight alternative to RPCs. Provided as an autoload.
|
|
|
|
Commands consist of a single byte for ID, and the raw binary data. The ID lets
|
|
the receiving peer decide what to execute, with the binary data serving as the
|
|
input.
|
|
|
|
Being a simpler construct makes commands a good fit for regular, fundamental
|
|
operations. For example, commands internally are used for time synchronization,
|
|
or synchronizing state and input between peers.
|
|
|
|
Commands are, by default, transmitted over regular RPCs. To use less data,
|
|
commands can also be transmitted as raw packets, using
|
|
[SceneMultiplayer.send_bytes()]. This is an opt-in feature - if the game is
|
|
already using [SceneMultiplayer.send_bytes()], it needs to be aware of
|
|
commands, and must check each packet whether it's a command or one of its own
|
|
packets. To check if a packet is a command, use `is_command_packet()`.
|
|
|
|
## Implementing custom commands
|
|
|
|
Custom commands can be registered with the *NetworkCommandServer*, using
|
|
`register_command()`. This returns a *Command* object that provides a
|
|
convenient interface.
|
|
|
|
During registration, a callback must be provided, that will be ran when the
|
|
command is received.
|
|
|
|
Commands can be sent using its `send()` method.
|
|
|
|
```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())
|
|
```
|
|
|
|
!!!tip
|
|
It is recommended to setup commands once, at game start. When registering
|
|
commands from autoloads, make sure they run *after* netfox's autoloads.
|
|
|
|
## Differences compared to RPCs
|
|
|
|
Commands are a fundamentally simpler constructs compared to RPCs.
|
|
|
|
### Maximum 256 commands
|
|
|
|
Commands are limited to 256 indices - make sure to not register more than that.
|
|
Some commands are registered by netfox on startup as well.
|
|
|
|
This limitation also makes commands a poor fit for registering dynamically.
|
|
Dynamic registrations often mean registering commands as certain nodes or
|
|
objects are created. This, in turn, makes it difficult to place an upper bound
|
|
on the number of commands needed, which can conflict with this limitation.
|
|
|
|
### Commands are not tied to any node
|
|
|
|
Commands do not refer to any specific node or object in their content. They
|
|
only contain a command index. Even though the API encapsulates this into
|
|
*Command* objects, it is completely feasible to have different nodes handle the
|
|
same command on different peers ( if the game is built as different Godot
|
|
projects ).
|
|
|
|
### Commands do not track authority
|
|
|
|
Any peer can send any command to any other peer. It is the receiving peer's
|
|
responsibility to check whether the sender is allowed to send such a command or
|
|
not.
|
|
|
|
### Commands do not have arguments
|
|
|
|
To stay lightweight and to give maximum control, commands contain raw bytes
|
|
only, no arguments.
|
|
|
|
In general, this can be worked around by wrapping the arguments in an array and
|
|
converting it using [var_to_bytes()] and [bytes_to_var()].
|
|
|
|
However, for cases where bandwidth matters, this allows users to encode data in
|
|
a way that fits best.
|
|
|
|
## Settings
|
|
|
|
netfox ▸ General ▸ Use Raw Commands
|
|
|
|
: When enabled, netfox will transmit commands as raw packets, instead of RPCs.
|
|
|
|
|
|
[SceneMultiplayer.send_bytes()]: https://docs.godotengine.org/en/stable/classes/class_scenemultiplayer.html#class-scenemultiplayer-method-send-bytes
|
|
[var_to_bytes()]: https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#class-globalscope-method-var-to-bytes
|
|
[bytes_to_var()]: https://docs.godotengine.org/en/stable/classes/class_%40globalscope.html#class-globalscope-method-bytes-to-var
|