foxssake / netfox

Addons for building multiplayer games with Godot
https://foxssake.github.io/netfox/
MIT License
399 stars 16 forks source link

WebRTC support #188

Closed bryanmylee closed 9 months ago

bryanmylee commented 9 months ago

I'm using WebRTC for a web-based HTML5 export, so I've configured my network such that my server is technically just another peer in the network with its own peer_id.

I've dropped netfox into my codebase and set up the authority for all nodes except the input node to be the server's peer ID, but I'm not getting any network activity and state is not replicated across the network.

Any advice would be appreciated.

bryanmylee commented 9 months ago

I've realized that all of the network configuration starts from NetworkEvents, which doesn't get fired for me because I've rolled my own server-client connection with WebRTC.

I'm going to add the same hooks to my own GameClient and GameServer code and report back.

I'll also have to replace multiplayer.is_server() with my own Program.is_game_authority since every peer in WebRTC technically listens for and initiates connections.

bryanmylee commented 9 months ago

For anyone interested in doing something similar with WebRTC in the future, I resolved this by making the following edits:

# network-time-synchronizer.gd
-L121: var sync_result = await sync_time(1)
+L121: var sync_result = await sync_time(Program.game_authority_id)
# network-time.gd
-L299: if not multiplayer.is_server():
+L299: if not Program.is_game_authority:
...
-L335: if not multiplayer.is_server():
+L335: if not Program.is_game_authority:

Program.game_authority_id and Program.is_game_authority are simply variables set across the network when setting up my WebRTC mesh.

On my own WebRTC signalling and setup code, I added the following calls:

# webrtc_client.gd
func _handle_webrtc_peer_connected(new_peer_id: int) -> void:
+ if new_peer_id == Program.game_authority_id:
+   NetworkTime.start()
# webrtc_signalling.gd
func _handle_authority_webrtc_client_ready(peer_id: int) -> void:
~   Program.game_authority_id = peer_id
+   NetworkTime.start()
~   await set_game_authority_on_existing_peers(peer_id)
elementbound commented 9 months ago

Thanks for the detailed post @bryanmylee!

I've realized that all of the network configuration starts from NetworkEvents

I see you've also figured this out, so this note is mostly for anyone else reading :slightly_smiling_face: You can also manually start things by calling NetworkTime.start. NetworkEvents is a convenience wrapper over Godot's multiplayer events, but you can skip that layer.

I think I can support your use case by making NetworkTime's authority configurable, but default to 1 as it currently does. So that way you would set the NetworkTime's sync authority to Program.game_authority_id and be done with it.

By the way, do I understand it right that your WebRTC setup does not emit the usual events ( connected_to_server, peer_connected, etc. ), and that's the reason why the original NetworkEvents implementation did nothing?

bryanmylee commented 9 months ago

@elementbound As far as I know, that's correct. connected_to_server isn't emitted in a WebRTC environment since all connection nodes are peers.

peer_connected is emitted, but unlike a traditional client-server model, it's not isolated to the server only and is emitted on all peers when a new peer joins the mesh.

I've essentially replaced:

  1. connected_to_server with peer_connected(id) where id == Program.game_authority_id, and
  2. peer_connected with peer_connected where Program.is_game_authority.
elementbound commented 9 months ago

Thanks! I'll try to cater a bit to this use case, both in implementation and in docs 🙂

bryanmylee commented 9 months ago

@elementbound Actually, I think you can ignore this. I've realized that it's practically impossible to get a WebRTC client to work when it's deployed on a server, especially if it's behind a container network like Docker.

I'm basically abandoning this approach :/