high_level_multiplayer.rst 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. .. _doc_high_level_multiplayer:
  2. High-level multiplayer
  3. ======================
  4. High-level vs low-level API
  5. ---------------------------
  6. The following explains the differences of high- and low-level networking in Godot as well as some fundamentals. If you want to jump in head-first and add networking to your first nodes, skip to `Initializing the network`_ below. But make sure to read the rest later on!
  7. Godot always supported standard low-level networking via :abbr:`UDP (User Datagram Protocol)`, :abbr:`TCP (Transmission Control Protocol)` and some higher-level protocols such as :abbr:`HTTP (Hypertext Transfer Protocol)` and :abbr:`SSL (Secure Sockets Layer)`.
  8. These protocols are flexible and can be used for almost anything. However, using them to synchronize game state manually can be a large amount of work. Sometimes that work can't be avoided or is worth it, for example when working with a custom server implementation on the backend. But in most cases, it's worthwhile to consider Godot's high-level networking API, which sacrifices some of the fine-grained control of low-level networking for greater ease of use.
  9. This is due to the inherent limitations of the low-level protocols:
  10. - TCP ensures packets will always arrive reliably and in order, but latency is generally higher due to error correction.
  11. It's also quite a complex protocol because it understands what a "connection" is, and optimizes for goals that often don't suit applications like multiplayer games. Packets are buffered to be sent in larger batches, trading less per-packet overhead for higher latency. This can be useful for things like HTTP, but generally not for games. Some of this can be configured and disabled (e.g. by disabling "Nagle's algorithm" for the TCP connection).
  12. - UDP is a simpler protocol, which only sends packets (and has no concept of a "connection"). No error correction
  13. makes it pretty quick (low latency), but packets may be lost along the way or received in the wrong order.
  14. Added to that, the MTU (maximum packet size) for UDP is generally low (only a few hundred bytes), so transmitting
  15. larger packets means splitting them, reorganizing them and retrying if a part fails.
  16. In general, TCP can be thought of as reliable, ordered, and slow; UDP as unreliable, unordered and fast.
  17. Because of the large difference in performance, it often makes sense to re-build the parts of TCP wanted for games (optional reliability and packet order), while avoiding the unwanted parts (congestion/traffic control features, Nagle's algorithm, etc). Due to this, most game engines come with such an implementation, and Godot is no exception.
  18. In summary, you can use the low-level networking API for maximum control and implement everything on top of bare network protocols or use the high-level API based on :ref:`SceneTree <class_SceneTree>` that does most of the heavy lifting behind the scenes in a generally optimized way.
  19. .. note:: Most of Godot's supported platforms offer all or most of the mentioned high- and low-level networking
  20. features. As networking is always largely hardware and operating system dependent, however,
  21. some features may change or not be available on some target platforms. Most notably,
  22. the HTML5 platform currently offers WebSockets and WebRTC support but lacks some of the higher-level features, as
  23. well as raw access to low-level protocols like TCP and UDP.
  24. .. note:: More about TCP/IP, UDP, and networking:
  25. https://gafferongames.com/post/udp_vs_tcp/
  26. Gaffer On Games has a lot of useful articles about networking in Games
  27. (`here <https://gafferongames.com/categories/game-networking/>`__), including the comprehensive
  28. `introduction to networking models in games <https://gafferongames.com/post/what_every_programmer_needs_to_know_about_game_networking/>`__.
  29. .. warning:: Adding networking to your game comes with some responsibility.
  30. It can make your application vulnerable if done wrong and may lead to cheats or exploits.
  31. It may even allow an attacker to compromise the machines your application runs on
  32. and use your servers to send spam, attack others or steal your users' data if they play your game.
  33. This is always the case when networking is involved and has nothing to do with Godot.
  34. You can of course experiment, but when you release a networked application,
  35. always take care of any possible security concerns.
  36. Mid-level abstraction
  37. ---------------------
  38. Before going into how we would like to synchronize a game across the network, it can be helpful to understand how the base network API for synchronization works.
  39. Godot uses a mid-level object :ref:`MultiplayerPeer <class_MultiplayerPeer>`.
  40. This object is not meant to be created directly, but is designed so that several C++ implementations can provide it.
  41. This object extends from :ref:`PacketPeer <class_PacketPeer>`, so it inherits all the useful methods for serializing, sending and receiving data. On top of that, it adds methods to set a peer, transfer mode, etc. It also includes signals that will let you know when peers connect or disconnect.
  42. This class interface can abstract most types of network layers, topologies and libraries. By default, Godot
  43. provides an implementation based on ENet (:ref:`ENetMultiplayerPeer <class_ENetMultiplayerPeer>`),
  44. one based on WebRTC (:ref:`WebRTCMultiplayerPeer <class_WebRTCMultiplayerPeer>`), and one based on WebSocket
  45. (:ref:`WebSocketPeer <class_WebSocketPeer>`), but this could be used to implement
  46. mobile APIs (for ad hoc WiFi, Bluetooth) or custom device/console-specific networking APIs.
  47. For most common cases, using this object directly is discouraged, as Godot provides even higher level networking facilities.
  48. This object is still made available in case a game has specific needs for a lower-level API.
  49. Hosting considerations
  50. ----------------------
  51. When hosting a server, clients on your :abbr:`LAN (Local Area Network)` can
  52. connect using the internal IP address which is usually of the form
  53. ``192.168.*.*``. This internal IP address is **not** reachable by
  54. non-LAN/Internet clients.
  55. On Windows, you can find your internal IP address by opening a command prompt
  56. and entering ``ipconfig``. On macOS, open a Terminal and enter ``ifconfig``. On
  57. Linux, open a terminal and enter ``ip addr``.
  58. If you're hosting a server on your own machine and want non-LAN clients to
  59. connect to it, you'll probably have to *forward* the server port on your router.
  60. This is required to make your server reachable from the Internet since most
  61. residential connections use a `NAT
  62. <https://en.wikipedia.org/wiki/Network_address_translation>`__. Godot's
  63. high-level multiplayer API only uses UDP, so you must forward the port in UDP,
  64. not just TCP.
  65. After forwarding a UDP port and making sure your server uses that port, you can
  66. use `this website <https://icanhazip.com/>`__ to find your public IP address.
  67. Then give this public IP address to any Internet clients that wish to connect to
  68. your server.
  69. Godot's high-level multiplayer API uses a modified version of ENet which allows
  70. for full IPv6 support.
  71. Initializing the network
  72. ------------------------
  73. High-level networking in Godot is managed by the :ref:`SceneTree <class_SceneTree>`.
  74. Each node has a ``multiplayer`` property, which is a reference to the ``MultiplayerAPI`` instance configured for it
  75. by the scene tree. Initially, every node is configured with the same default ``MultiplayerAPI`` object.
  76. It is possible to create a new ``MultiplayerAPI`` object and assign it to a ``NodePath`` in the the scene tree,
  77. which will override ``multiplayer`` for the node at that path and all of its descendants.
  78. This allows sibling nodes to be configured with different peers, which makes it possible to run a server
  79. and a client simultaneously in one instance of Godot.
  80. .. tabs::
  81. .. code-tab:: gdscript GDScript
  82. # By default, these expressions are interchangeable.
  83. multiplayer # Get the MultiplayerAPI object configured for this node.
  84. get_tree().get_multiplayer() # Get the default MultiplayerAPI object.
  85. .. code-tab:: csharp
  86. // By default, these expressions are interchangeable.
  87. Multiplayer; // Get the MultiplayerAPI object configured for this node.
  88. GetTree().GetMultiplayer(); // Get the default MultiplayerAPI object.
  89. To initialize networking, a ``MultiplayerPeer`` object must be created, initialized as a server or client,
  90. and passed to the ``MultiplayerAPI``.
  91. .. tabs::
  92. .. code-tab:: gdscript GDScript
  93. # Create client.
  94. var peer = ENetMultiplayerPeer.new()
  95. peer.create_client(IP_ADDRESS, PORT)
  96. multiplayer.multiplayer_peer = peer
  97. # Create server.
  98. var peer = ENetMultiplayerPeer.new()
  99. peer.create_server(PORT, MAX_CLIENTS)
  100. multiplayer.multiplayer_peer = peer
  101. .. code-tab:: csharp
  102. // Create client.
  103. var peer = new ENetMultiplayerPeer();
  104. peer.CreateClient(IPAddress, Port);
  105. Multiplayer.MultiplayerPeer = peer;
  106. // Create server.
  107. var peer = new ENetMultiplayerPeer();
  108. peer.CreateServer(Port, MaxClients);
  109. Multiplayer.MultiplayerPeer = peer;
  110. To terminate networking:
  111. .. tabs::
  112. .. code-tab:: gdscript GDScript
  113. multiplayer.multiplayer_peer = null
  114. .. code-tab:: csharp
  115. Multiplayer.MultiplayerPeer = null;
  116. .. warning::
  117. When exporting to Android, make sure to enable the ``INTERNET``
  118. permission in the Android export preset before exporting the project or
  119. using one-click deploy. Otherwise, network communication of any kind will be
  120. blocked by Android.
  121. Managing connections
  122. --------------------
  123. Every peer is assigned a unique ID. The server's ID is always 1, and clients are assigned a random positive integer.
  124. Responding to connections or disconnections is possible by connecting to ``MultiplayerAPI``'s signals:
  125. - ``peer_connected(id: int)`` This signal is emitted with the newly connected peer's ID on each other peer, and on the new peer multiple times, once with each other peer's ID.
  126. - ``peer_disconnected(id: int)`` This signal is emitted on every remaining peer when one disconnects.
  127. The rest are only emitted on clients:
  128. - ``connected_to_server()``
  129. - ``connection_failed()``
  130. - ``server_disconnected()``
  131. To get the unique ID of the associated peer:
  132. .. tabs::
  133. .. code-tab:: gdscript GDScript
  134. multiplayer.get_unique_id()
  135. .. code-tab:: csharp
  136. Multiplayer.GetUniqueId();
  137. To check whether the peer is server or client:
  138. .. tabs::
  139. .. code-tab:: gdscript GDScript
  140. multiplayer.is_server()
  141. .. code-tab:: csharp
  142. Multiplayer.IsServer();
  143. Remote procedure calls
  144. ----------------------
  145. Remote procedure calls, or RPCs, are functions that can be called on other peers. To create one, use the ``@rpc`` annotation
  146. before a function definition. To call an RPC, use ``Callable``'s method ``rpc()`` to call in every peer, or ``rpc_id()`` to
  147. call in a specific peer.
  148. .. tabs::
  149. .. code-tab:: gdscript GDScript
  150. func _ready():
  151. if multiplayer.is_server():
  152. print_once_per_client.rpc()
  153. @rpc
  154. func print_once_per_client():
  155. print("I will be printed to the console once per each connected client.")
  156. .. code-tab:: csharp
  157. public override void _Ready()
  158. {
  159. if (Multiplayer.IsServer())
  160. {
  161. Rpc(MethodName.PrintOncePerClient);
  162. }
  163. }
  164. [Rpc]
  165. private void PrintOncePerClient()
  166. {
  167. GD.Print("I will be printed to the console once per each connected client.");
  168. }
  169. RPCs will not serialize objects or callables.
  170. For a remote call to be successful, the sending and receiving node need to have the same ``NodePath``, which means they
  171. must have the same name. When using ``add_child()`` for nodes which are expected to use RPCs, set the argument
  172. ``force_readable_name`` to ``true``.
  173. .. warning::
  174. If a function is annotated with ``@rpc`` on the client script (resp. server script),
  175. then this function must also be declared on the server script (resp. client script).
  176. Both RPCs must have the same signature which is evaluated with a checksum of **all RPCs**.
  177. All RPCs in a script are checked at once, and all RPCs must be declared on both the client
  178. scripts and the server scripts, **even functions that are currently not in use**.
  179. The signature of the RPC includes the ``@rpc()`` declaration, the function, return type,
  180. **and** the NodePath. If an RPC resides in a script attached to ``/root/Main/Node1``, then it
  181. must reside in precisely the same path and node on both the client script and the server
  182. script. Function arguments are not checked for matching between the server and client code
  183. (example: ``func sendstuff():`` and ``func sendstuff(arg1, arg2):`` **will pass** signature
  184. matching).
  185. If these conditions are not met (if all RPCs do not pass signature matching), the script may print an
  186. error or cause unwanted behavior. The error message may be unrelated to the RPC function you are
  187. currently building and testing.
  188. See further explanation and troubleshooting on `this post <https://github.com/godotengine/godot/issues/57869#issuecomment-1034215138>`__.
  189. The annotation can take a number of arguments, which have default values. ``@rpc`` is equivalent to:
  190. .. tabs::
  191. .. code-tab:: gdscript GDScript
  192. @rpc("authority", "call_remote", "unreliable", 0)
  193. .. code-tab:: csharp
  194. [Rpc(MultiplayerApi.RpcMode.Authority, CallLocal = false, TransferMode = MultiplayerPeer.TransferModeEnum.Unreliable, TransferChannel = 0)]
  195. The parameters and their functions are as follows:
  196. ``mode``:
  197. - ``"authority"``: Only the multiplayer authority can call remotely.
  198. The authority is the server by default, but can be changed per-node using
  199. :ref:`Node.set_multiplayer_authority <class_Node_method_set_multiplayer_authority>`.
  200. - ``"any_peer"``: Clients are allowed to call remotely. Useful for transferring user input.
  201. ``sync``:
  202. - ``"call_remote"``: The function will not be called on the local peer.
  203. - ``"call_local"``: The function can be called on the local peer. Useful when the server is also a player.
  204. ``transfer_mode``:
  205. - ``"unreliable"`` Packets are not acknowledged, can be lost, and can arrive at any order.
  206. - ``"unreliable_ordered"`` Packets are received in the order they were sent in. This is achieved by ignoring packets that arrive later if another that was sent after them has already been received. Can cause packet loss if used incorrectly.
  207. - ``"reliable"`` Resend attempts are sent until packets are acknowledged, and their order is preserved. Has a significant performance penalty.
  208. ``transfer_channel`` is the channel index.
  209. The first 3 can be passed in any order, but ``transfer_channel`` must always be last.
  210. The function ``multiplayer.get_remote_sender_id()`` can be used to get the unique id of an rpc sender, when used within the function called by rpc.
  211. .. tabs::
  212. .. code-tab:: gdscript GDScript
  213. func _on_some_input(): # Connected to some input.
  214. transfer_some_input.rpc_id(1) # Send the input only to the server.
  215. # Call local is required if the server is also a player.
  216. @rpc("any_peer", "call_local", "reliable")
  217. func transfer_some_input():
  218. # The server knows who sent the input.
  219. var sender_id = multiplayer.get_remote_sender_id()
  220. # Process the input and affect game logic.
  221. .. code-tab:: csharp
  222. private void OnSomeInput() // Connected to some input.
  223. {
  224. RpcId(1, MethodName.TransferSomeInput); // Send the input only to the server.
  225. }
  226. // Call local is required if the server is also a player.
  227. [Rpc(MultiplayerApi.RpcMode.AnyPeer, CallLocal = true, TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
  228. private void TransferSomeInput()
  229. {
  230. // The server knows who sent the input.
  231. int senderId = Multiplayer.GetRemoteSenderId();
  232. // Process the input and affect game logic.
  233. }
  234. Channels
  235. --------
  236. Modern networking protocols support channels, which are separate connections within the connection. This allows for multiple
  237. streams of packets that do not interfere with each other.
  238. For example, game chat related messages and some of the core gameplay messages should all be sent reliably, but a gameplay
  239. message should not wait for a chat message to be acknowledged. This can be achieved by using different channels.
  240. Channels are also useful when used with the unreliable ordered transfer mode. Sending packets of variable size with this transfer mode can
  241. cause packet loss, since packets which are slower to arrive are ignored. Separating them into multiple streams of homogeneous packets
  242. by using channels allows ordered transfer with little packet loss, and without the latency penalty caused by reliable mode.
  243. The default channel with index 0 is actually three different channels - one for each transfer mode.
  244. Example lobby implementation
  245. ----------------------------
  246. This is an example lobby that can handle peers joining and leaving, notify UI scenes through signals, and start the game after all clients
  247. have loaded the game scene.
  248. .. tabs::
  249. .. code-tab:: gdscript GDScript
  250. extends Node
  251. # Autoload named Lobby
  252. # These signals can be connected to by a UI lobby scene or the game scene.
  253. signal player_connected(peer_id, player_info)
  254. signal player_disconnected(peer_id)
  255. signal server_disconnected
  256. const PORT = 7000
  257. const DEFAULT_SERVER_IP = "127.0.0.1" # IPv4 localhost
  258. const MAX_CONNECTIONS = 20
  259. # This will contain player info for every player,
  260. # with the keys being each player's unique IDs.
  261. var players = {}
  262. # This is the local player info. This should be modified locally
  263. # before the connection is made. It will be passed to every other peer.
  264. # For example, the value of "name" can be set to something the player
  265. # entered in a UI scene.
  266. var player_info = {"name": "Name"}
  267. var players_loaded = 0
  268. func _ready():
  269. multiplayer.peer_connected.connect(_on_player_connected)
  270. multiplayer.peer_disconnected.connect(_on_player_disconnected)
  271. multiplayer.connected_to_server.connect(_on_connected_ok)
  272. multiplayer.connection_failed.connect(_on_connected_fail)
  273. multiplayer.server_disconnected.connect(_on_server_disconnected)
  274. func join_game(address = ""):
  275. if address.is_empty():
  276. address = DEFAULT_SERVER_IP
  277. var peer = ENetMultiplayerPeer.new()
  278. var error = peer.create_client(address, PORT)
  279. if error:
  280. return error
  281. multiplayer.multiplayer_peer = peer
  282. func create_game():
  283. var peer = ENetMultiplayerPeer.new()
  284. var error = peer.create_server(PORT, MAX_CONNECTIONS)
  285. if error:
  286. return error
  287. multiplayer.multiplayer_peer = peer
  288. players[1] = player_info
  289. player_connected.emit(1, player_info)
  290. func remove_multiplayer_peer():
  291. multiplayer.multiplayer_peer = null
  292. # When the server decides to start the game from a UI scene,
  293. # do Lobby.load_game.rpc(filepath)
  294. @rpc("call_local", "reliable")
  295. func load_game(game_scene_path):
  296. get_tree().change_scene_to_file(game_scene_path)
  297. # Every peer will call this when they have loaded the game scene.
  298. @rpc("any_peer", "call_local", "reliable")
  299. func player_loaded():
  300. if multiplayer.is_server():
  301. players_loaded += 1
  302. if players_loaded == players.size():
  303. $/root/Game.start_game()
  304. players_loaded = 0
  305. # When a peer connects, send them my player info.
  306. # This allows transfer of all desired data for each player, not only the unique ID.
  307. func _on_player_connected(id):
  308. _register_player.rpc_id(id, player_info)
  309. @rpc("any_peer", "reliable")
  310. func _register_player(new_player_info):
  311. var new_player_id = multiplayer.get_remote_sender_id()
  312. players[new_player_id] = new_player_info
  313. player_connected.emit(new_player_id, new_player_info)
  314. func _on_player_disconnected(id):
  315. players.erase(id)
  316. player_disconnected.emit(id)
  317. func _on_connected_ok():
  318. var peer_id = multiplayer.get_unique_id()
  319. players[peer_id] = player_info
  320. player_connected.emit(peer_id, player_info)
  321. func _on_connected_fail():
  322. multiplayer.multiplayer_peer = null
  323. func _on_server_disconnected():
  324. multiplayer.multiplayer_peer = null
  325. players.clear()
  326. server_disconnected.emit()
  327. .. code-tab:: csharp
  328. using Godot;
  329. public partial class Lobby : Node
  330. {
  331. public static Lobby Instance { get; private set; }
  332. // These signals can be connected to by a UI lobby scene or the game scene.
  333. [Signal]
  334. public delegate void PlayerConnectedEventHandler(int peerId, Godot.Collections.Dictionary<string, string> playerInfo);
  335. [Signal]
  336. public delegate void PlayerDisconnectedEventHandler(int peerId);
  337. [Signal]
  338. public delegate void ServerDisconnectedEventHandler();
  339. private const int Port = 7000;
  340. private const string DefaultServerIP = "127.0.0.1"; // IPv4 localhost
  341. private const int MaxConnections = 20;
  342. // This will contain player info for every player,
  343. // with the keys being each player's unique IDs.
  344. private Godot.Collections.Dictionary<long, Godot.Collections.Dictionary<string, string>> _players = new Godot.Collections.Dictionary<long, Godot.Collections.Dictionary<string, string>>();
  345. // This is the local player info. This should be modified locally
  346. // before the connection is made. It will be passed to every other peer.
  347. // For example, the value of "name" can be set to something the player
  348. // entered in a UI scene.
  349. private Godot.Collections.Dictionary<string, string> _playerInfo = new Godot.Collections.Dictionary<string, string>()
  350. {
  351. { "Name", "PlayerName" },
  352. };
  353. private int _playersLoaded = 0;
  354. public override void _Ready()
  355. {
  356. Instance = this;
  357. Multiplayer.PeerConnected += OnPlayerConnected;
  358. Multiplayer.PeerDisconnected += OnPlayerDisconnected;
  359. Multiplayer.ConnectedToServer += OnConnectOk;
  360. Multiplayer.ConnectionFailed += OnConnectionFail;
  361. Multiplayer.ServerDisconnected += OnServerDisconnected;
  362. }
  363. private Error JoinGame(string address = "")
  364. {
  365. if (string.IsNullOrEmpty(address))
  366. {
  367. address = DefaultServerIP;
  368. }
  369. var peer = new ENetMultiplayerPeer();
  370. Error error = peer.CreateClient(address, Port);
  371. if (error != Error.Ok)
  372. {
  373. return error;
  374. }
  375. Multiplayer.MultiplayerPeer = peer;
  376. return Error.Ok;
  377. }
  378. private Error CreateGame()
  379. {
  380. var peer = new ENetMultiplayerPeer();
  381. Error error = peer.CreateServer(Port, MaxConnections);
  382. if (error != Error.Ok)
  383. {
  384. return error;
  385. }
  386. Multiplayer.MultiplayerPeer = peer;
  387. _players[1] = _playerInfo;
  388. EmitSignal(SignalName.PlayerConnected, 1, _playerInfo);
  389. return Error.Ok;
  390. }
  391. private void RemoveMultiplayerPeer()
  392. {
  393. Multiplayer.MultiplayerPeer = null;
  394. }
  395. // When the server decides to start the game from a UI scene,
  396. // do Rpc(Lobby.MethodName.LoadGame, filePath);
  397. [Rpc(CallLocal = true,TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
  398. private void LoadGame(string gameScenePath)
  399. {
  400. GetTree().ChangeSceneToFile(gameScenePath);
  401. }
  402. // Every peer will call this when they have loaded the game scene.
  403. [Rpc(MultiplayerApi.RpcMode.AnyPeer,CallLocal = true,TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
  404. private void PlayerLoaded()
  405. {
  406. if (Multiplayer.IsServer())
  407. {
  408. _playersLoaded += 1;
  409. if (_playersLoaded == _players.Count)
  410. {
  411. GetNode<Game>("/root/Game").StartGame();
  412. _playersLoaded = 0;
  413. }
  414. }
  415. }
  416. // When a peer connects, send them my player info.
  417. // This allows transfer of all desired data for each player, not only the unique ID.
  418. private void OnPlayerConnected(long id)
  419. {
  420. RpcId(id, MethodName.RegisterPlayer, _playerInfo);
  421. }
  422. [Rpc(MultiplayerApi.RpcMode.AnyPeer,TransferMode = MultiplayerPeer.TransferModeEnum.Reliable)]
  423. private void RegisterPlayer(Godot.Collections.Dictionary<string, string> newPlayerInfo)
  424. {
  425. int newPlayerId = Multiplayer.GetRemoteSenderId();
  426. _players[newPlayerId] = newPlayerInfo;
  427. EmitSignal(SignalName.PlayerConnected, newPlayerId, newPlayerInfo);
  428. }
  429. private void OnPlayerDisconnected(long id)
  430. {
  431. _players.Remove(id);
  432. EmitSignal(SignalName.PlayerDisconnected, id);
  433. }
  434. private void OnConnectOk()
  435. {
  436. int peerId = Multiplayer.GetUniqueId();
  437. _players[peerId] = _playerInfo;
  438. EmitSignal(SignalName.PlayerConnected, peerId, _playerInfo);
  439. }
  440. private void OnConnectionFail()
  441. {
  442. Multiplayer.MultiplayerPeer = null;
  443. }
  444. private void OnServerDisconnected()
  445. {
  446. Multiplayer.MultiplayerPeer = null;
  447. _players.Clear();
  448. EmitSignal(SignalName.ServerDisconnected);
  449. }
  450. }
  451. The game scene's root node should be named Game. In the script attached to it:
  452. .. tabs::
  453. .. code-tab:: gdscript GDScript
  454. extends Node3D # Or Node2D.
  455. func _ready():
  456. # Preconfigure game.
  457. Lobby.player_loaded.rpc_id(1) # Tell the server that this peer has loaded.
  458. # Called only on the server.
  459. func start_game():
  460. # All peers are ready to receive RPCs in this scene.
  461. .. code-tab:: csharp
  462. using Godot;
  463. public partial class Game : Node3D // Or Node2D.
  464. {
  465. public override void _Ready()
  466. {
  467. // Preconfigure game.
  468. Lobby.Instance.RpcId(1, Lobby.MethodName.PlayerLoaded); // Tell the server that this peer has loaded.
  469. }
  470. // Called only on the server.
  471. public void StartGame()
  472. {
  473. // All peers are ready to receive RPCs in this scene.
  474. }
  475. }
  476. Exporting for dedicated servers
  477. -------------------------------
  478. Once you've made a multiplayer game, you may want to export it to run it on
  479. a dedicated server with no GPU available. See
  480. :ref:`doc_exporting_for_dedicated_servers` for more information.
  481. .. note::
  482. The code samples on this page aren't designed to run on a dedicated
  483. server. You'll have to modify them so the server isn't considered to be a
  484. player. You'll also have to modify the game starting mechanism so that the
  485. first player who joins can start the game.