Skip to content

Multiple Multiplayer Peers in the same scene tree #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
RegularTetragon opened this issue Mar 1, 2025 · 3 comments
Open

Multiple Multiplayer Peers in the same scene tree #35

RegularTetragon opened this issue Mar 1, 2025 · 3 comments

Comments

@RegularTetragon
Copy link

RegularTetragon commented Mar 1, 2025

With ENetMultiplayer my game is structured like this to allow for separation of concerns, as well as split screen multiplayer:

  • Root
    • Server (ENetMultiplayerPeer 0)
    • Clients
      • Client 1 (ENetMultiplayerPeer 1)
      • Client 2 (ENetMultiplayerPeer 2)
      • ...

Starting a server with just one player is just a simpler case of this:

  • Root
    • Server (ENetMultiplayerPeer 0)
    • Clients
      • Client 1 (ENetMultiplayerPeer 1)

Each Server/Client has its own MultiplayerAPI and Peer. This is how the server is started:

# root.gd
var peer = ENetMultiplayerPeer
var error = peer.create_server(port, max_players)
server.start(peer)
# server.gd
func start(peer: MultiplayerPeer):
	get_tree().set_multiplayer(
		multiplayer.create_default_interface(),
		get_path()
	)
	multiplayer.multiplayer_peer = peer
	multiplayer.peer_connected.connect(player_joined)
	multiplayer.peer_disconnected.connect(player_left)

This is how the client is started

# root.gd
var peer = ENetMultiplayerPeer.new()
peer.create_client(address, port)
return start_client(peer)
# client.gd
func start(peer: MultiplayerPeer) -> void:
	get_tree().set_multiplayer(
		MultiplayerAPI.create_default_interface(),
		get_path()
	)
	multiplayer.multiplayer_peer = peer
	multiplayer.connected_to_server.connect(on_connected_to_server)
	multiplayer.connection_failed.connect(on_connection_failed)
	multiplayer.server_disconnected.connect(on_server_disconnected)

This works pretty well. It means I can have split screen multiplayer and networked multiplayer simultaneously for basically nothing. It also means my server and client logic are separated into entirely different scripts (e.g. PlayerServer, PlayerClientLocal, PlayerClientRemote). Now that I'm trying to integrate with Steam Multiplayer Peer though I can't seem to figure out a way to connect multiple client Steam Multiplayer Peers in the same scene tree to a server Steam Multiplayer Peer. Is it possible to do something similar with Steam Multiplayer Peer? I.e:

Steam.createLobby(Steam.LobbyType.LOBBY_TYPE_PUBLIC, max_players)
var lobby_id = (await Steam.lobby_created[1])
await Steam.lobby_joined

var server_peer = SteamMultiplayerPeer.new()
var server_error = server_peer.create_host(0)
var server = start_server(peer, gamemode, level)

var client_peer = SteamMultiplayerPeer.new()
var client_error = client_peer.create_client(Steam.getLobbyOwner(lobby_id, 0))
var client = start_client(peer)

When I try to do that, I get the following error_string
'Can't connect'

Which is not very helpful unfortunately, but I think is caused by trying to create multiple SteamMultiplayerPeers for my current user. Is there a way I can get this to work without rearchitecting my whole game?

Note: I only use RPC's, I don't use MultiplayerSpawner or MultiplayerSynchronizer.

@scriptsengineer
Copy link
Member

I think it is possible for Steam but not for the current state of the peer, I actually created the peer without even thinking about this possibility.

I still do not rule out the possibility that Steam does not allow this to happen, but this could only be resolved by using another Steam peer (like the one in the Gramps Steamworks module).

One detail I know is that my peer does not have the ability that ENET has to create mesh connections, I do not know if this affects it in any way?

@RegularTetragon
Copy link
Author

I have an idea about how to support this, basically I'm going to add a layer of abstraction between the steam sockets and the peer which wraps the packet in a structure in something roughly like this:

struct {
  enum subtype {control, data}
  union {
    struct {
      uint64_t request_id
      enum {nack, ack, add_peer, rm_peer} control_msg;
      uint64_t peer_id; // 0 for add_peer
    } control;
    struct {
      uint64_t length;
      uint64_t peer_id;
      void* packet;
    } data;
  } contents;
};

Then by maintaining a map on the peer I should be able to control which steam socket connection owns which peers. The peers themselves will share the socket, then be differentiated . This way I'll be able to have multiple players in one game as long as they're in the same process. I also wonder if I could do this as a simple wrapper over the peer you've written, something like SteamMultiplayerMultipeer? I think this is worth a fork, so I'll give it a shot once I figure out how to get this to build on Nix.

@scriptsengineer
Copy link
Member

Yes, it's worth a fork, although I have doubts about why enet works and steam peer doesn't, since I base myself on it to make the peer, is it some new enet update perhaps?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants