├── Godot4MultiplayerMVPClient ├── .godot │ ├── .gdignore │ ├── editor │ │ ├── favorites.Node │ │ ├── recent_dirs │ │ ├── create_recent.Node │ │ ├── filesystem_update4 │ │ ├── NetworkingClient.gd-folding-2b49d5740c9df0058e75f7de6fc91fed.cfg │ │ ├── NetworkingClient.tscn-folding-db6bc7cdfc97dc30471557a80338ec26.cfg │ │ ├── script_editor_cache.cfg │ │ ├── filesystem_cache8 │ │ ├── project_metadata.cfg │ │ ├── editor_layout.cfg │ │ └── NetworkingClient.tscn-editstate-db6bc7cdfc97dc30471557a80338ec26.cfg │ ├── global_script_class_cache.cfg │ ├── uid_cache.bin │ └── imported │ │ ├── icon.svg-218a8f2b3041327d8a5756f3a245f83b.md5 │ │ └── icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex ├── project.godot ├── NetworkingClient.tscn ├── icon.svg.import ├── NetworkingClient.gd └── icon.svg ├── Godot4MultiplayerMVPServer ├── .godot │ ├── .gdignore │ ├── editor │ │ ├── favorites │ │ ├── favorites.Node │ │ ├── recent_dirs │ │ ├── create_recent.Node │ │ ├── Server.gd-folding-2b2621a7b090c62b8f0ac723b752e775.cfg │ │ ├── filesystem_update4 │ │ ├── NetworkingServer.gd-folding-3fd6ff85ef556bad8ca8fdf00b6645d4.cfg │ │ ├── server.tscn-folding-c89a2950482f3a432bab03a0591e8d28.cfg │ │ ├── NetworkingServer.tscn-folding-67ca15145d9bad4a4322ef1520d60e36.cfg │ │ ├── script_editor_cache.cfg │ │ ├── filesystem_cache8 │ │ ├── project_metadata.cfg │ │ ├── editor_layout.cfg │ │ ├── server.tscn-editstate-c89a2950482f3a432bab03a0591e8d28.cfg │ │ └── NetworkingServer.tscn-editstate-67ca15145d9bad4a4322ef1520d60e36.cfg │ ├── global_script_class_cache.cfg │ ├── uid_cache.bin │ └── imported │ │ ├── icon.svg-218a8f2b3041327d8a5756f3a245f83b.md5 │ │ └── icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex ├── NetworkingServer.tscn ├── project.godot ├── icon.svg.import ├── NetworkingServer.gd └── icon.svg ├── .gitignore ├── README.md └── LICENSE /Godot4MultiplayerMVPClient/.godot/.gdignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/.gdignore: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/favorites: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/favorites.Node: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/favorites.Node: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/recent_dirs: -------------------------------------------------------------------------------- 1 | res:// 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/recent_dirs: -------------------------------------------------------------------------------- 1 | res:// 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/create_recent.Node: -------------------------------------------------------------------------------- 1 | Node 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/global_script_class_cache.cfg: -------------------------------------------------------------------------------- 1 | list=[] 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/global_script_class_cache.cfg: -------------------------------------------------------------------------------- 1 | list=[] 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/create_recent.Node: -------------------------------------------------------------------------------- 1 | Button 2 | Node2D 3 | Node 4 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/filesystem_update4: -------------------------------------------------------------------------------- 1 | res://NetworkingClient.tscn 2 | res://NetworkingClient.gd 3 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/Server.gd-folding-2b2621a7b090c62b8f0ac723b752e775.cfg: -------------------------------------------------------------------------------- 1 | [folding] 2 | 3 | sections_unfolded=PackedStringArray() 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Godot-specific ignores 3 | .import/ 4 | export.cfg 5 | export_presets.cfg 6 | 7 | # Mono-specific ignores 8 | .mono/ 9 | data_*/ 10 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/uid_cache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zwometer/Godot4MultiplayerMVP/HEAD/Godot4MultiplayerMVPClient/.godot/uid_cache.bin -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/filesystem_update4: -------------------------------------------------------------------------------- 1 | res://Server.gd 2 | res://server.tscn 3 | res://NetworkingServer.tscn 4 | res://NetworkingServer.gd 5 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/uid_cache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zwometer/Godot4MultiplayerMVP/HEAD/Godot4MultiplayerMVPServer/.godot/uid_cache.bin -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/NetworkingClient.gd-folding-2b49d5740c9df0058e75f7de6fc91fed.cfg: -------------------------------------------------------------------------------- 1 | [folding] 2 | 3 | sections_unfolded=PackedStringArray() 4 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/NetworkingServer.gd-folding-3fd6ff85ef556bad8ca8fdf00b6645d4.cfg: -------------------------------------------------------------------------------- 1 | [folding] 2 | 3 | sections_unfolded=PackedStringArray() 4 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/server.tscn-folding-c89a2950482f3a432bab03a0591e8d28.cfg: -------------------------------------------------------------------------------- 1 | [folding] 2 | 3 | node_unfolds=[] 4 | resource_unfolds=[] 5 | nodes_folded=[] 6 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/NetworkingClient.tscn-folding-db6bc7cdfc97dc30471557a80338ec26.cfg: -------------------------------------------------------------------------------- 1 | [folding] 2 | 3 | node_unfolds=[] 4 | resource_unfolds=[] 5 | nodes_folded=[] 6 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.md5: -------------------------------------------------------------------------------- 1 | source_md5="5ddad973d6395e772f8dfeaeed15f0a0" 2 | dest_md5="070132ccfe7233bfc44d6742a65b3739" 3 | 4 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/NetworkingServer.tscn-folding-67ca15145d9bad4a4322ef1520d60e36.cfg: -------------------------------------------------------------------------------- 1 | [folding] 2 | 3 | node_unfolds=[] 4 | resource_unfolds=[] 5 | nodes_folded=[] 6 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.md5: -------------------------------------------------------------------------------- 1 | source_md5="5ddad973d6395e772f8dfeaeed15f0a0" 2 | dest_md5="070132ccfe7233bfc44d6742a65b3739" 3 | 4 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zwometer/Godot4MultiplayerMVP/HEAD/Godot4MultiplayerMVPClient/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zwometer/Godot4MultiplayerMVP/HEAD/Godot4MultiplayerMVPServer/.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/NetworkingServer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://c5c1usc5scxln"] 2 | 3 | [ext_resource type="Script" path="res://NetworkingServer.gd" id="1_acvag"] 4 | 5 | [node name="Networking" type="Node"] 6 | script = ExtResource("1_acvag") 7 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/script_editor_cache.cfg: -------------------------------------------------------------------------------- 1 | [res://NetworkingServer.gd] 2 | 3 | state={ 4 | "bookmarks": PackedInt32Array(), 5 | "breakpoints": PackedInt32Array(), 6 | "column": 34, 7 | "folded_lines": [], 8 | "h_scroll_position": 0, 9 | "row": 5, 10 | "scroll_position": 0.0, 11 | "selection": false, 12 | "syntax_highlighter": "GDScript" 13 | } 14 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/script_editor_cache.cfg: -------------------------------------------------------------------------------- 1 | [res://NetworkingClient.gd] 2 | 3 | state={ 4 | "bookmarks": PackedInt32Array(), 5 | "breakpoints": PackedInt32Array(), 6 | "column": 36, 7 | "folded_lines": [], 8 | "h_scroll_position": 0, 9 | "row": 44, 10 | "scroll_position": 16.0, 11 | "selection": false, 12 | "syntax_highlighter": "GDScript" 13 | } 14 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/filesystem_cache8: -------------------------------------------------------------------------------- 1 | 0b7126e0c6e86d3ec295a72f5aa6e0bd 2 | ::res://::1674841528 3 | icon.svg::CompressedTexture2D::4915867829030184995::1674833617::1674833621::1::::<><>:: 4 | NetworkingServer.gd::GDScript::-1::1674841450::0::1::::<>Node<>:: 5 | server.tscn::PackedScene::6884250739622452943::1674841528::0::1::::<><>::res://NetworkingServer.gd 6 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/filesystem_cache8: -------------------------------------------------------------------------------- 1 | 0b7126e0c6e86d3ec295a72f5aa6e0bd 2 | ::res://::1675057476 3 | icon.svg::CompressedTexture2D::6373733551754743652::1674840975::1674840979::1::::<><>:: 4 | NetworkingClient.gd::GDScript::-1::1674845757::0::1::::<>Node<>:: 5 | NetworkingClient.tscn::PackedScene::656575761024644187::1674848215::0::1::::<><>::res://NetworkingClient.gd 6 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/project_metadata.cfg: -------------------------------------------------------------------------------- 1 | [editor_metadata] 2 | 3 | executable_path="C:/Progs/Dropbox/Torben-JP/Godot/Godot_v4.0-beta15_win64.exe" 4 | 5 | [debug_options] 6 | 7 | run_live_debug=true 8 | run_reload_scripts=true 9 | 10 | [script_setup] 11 | 12 | last_selected_language="GDScript" 13 | 14 | [recent_files] 15 | 16 | scripts=["Node", "SceneTree", "MultiplayerAPI", "Array", "MultiplayerPeer", "res://Server.gd"] 17 | scenes=["res://server.tscn"] 18 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/project_metadata.cfg: -------------------------------------------------------------------------------- 1 | [editor_metadata] 2 | 3 | executable_path="C:/Progs/Dropbox/Torben-JP/Godot/Godot_v4.0-beta16_win64.exe" 4 | use_advanced_connections=false 5 | 6 | [debug_options] 7 | 8 | run_live_debug=true 9 | run_reload_scripts=true 10 | 11 | [recent_files] 12 | 13 | scenes=["res://NetworkingClient.tscn"] 14 | scripts=["@GDScript", "MultiplayerPeer", "Object", "res://NetworkingClient.gd"] 15 | 16 | [script_setup] 17 | 18 | last_selected_language="GDScript" 19 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Godot 4 Multiplayer Client" 14 | run/main_scene="res://NetworkingClient.tscn" 15 | config/features=PackedStringArray("4.0", "GL Compatibility") 16 | config/icon="res://icon.svg" 17 | 18 | [rendering] 19 | 20 | renderer/rendering_method="gl_compatibility" 21 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Godot 4 Multiplayer Server" 14 | run/main_scene="res://NetworkingServer.tscn" 15 | config/features=PackedStringArray("4.0", "GL Compatibility") 16 | config/icon="res://icon.svg" 17 | 18 | [rendering] 19 | 20 | renderer/rendering_method="gl_compatibility" 21 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/editor_layout.cfg: -------------------------------------------------------------------------------- 1 | [docks] 2 | 3 | dock_filesystem_split=0 4 | dock_filesystem_display_mode=0 5 | dock_filesystem_file_sort=0 6 | dock_filesystem_file_list_display_mode=1 7 | dock_split_2=0 8 | dock_split_3=0 9 | dock_hsplit_1=0 10 | dock_hsplit_2=270 11 | dock_hsplit_3=-270 12 | dock_hsplit_4=0 13 | dock_3="Scene,Import" 14 | dock_4="FileSystem" 15 | dock_5="Inspector,Node,History" 16 | 17 | [EditorNode] 18 | 19 | open_scenes=["res://NetworkingClient.tscn"] 20 | 21 | [ScriptEditor] 22 | 23 | open_scripts=["res://NetworkingClient.gd"] 24 | open_help=[] 25 | script_split_offset=70 26 | list_split_offset=0 27 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/editor_layout.cfg: -------------------------------------------------------------------------------- 1 | [docks] 2 | 3 | dock_filesystem_split=0 4 | dock_filesystem_display_mode=0 5 | dock_filesystem_file_sort=0 6 | dock_filesystem_file_list_display_mode=1 7 | dock_split_2=0 8 | dock_split_3=0 9 | dock_hsplit_1=0 10 | dock_hsplit_2=270 11 | dock_hsplit_3=-270 12 | dock_hsplit_4=0 13 | dock_3="Scene,Import" 14 | dock_4="FileSystem" 15 | dock_5="Inspector,Node,History" 16 | 17 | [EditorNode] 18 | 19 | open_scenes=["res://NetworkingServer.tscn"] 20 | 21 | [ScriptEditor] 22 | 23 | open_scripts=["res://NetworkingServer.gd"] 24 | open_help=["Array", "MultiplayerAPI", "MultiplayerPeer"] 25 | script_split_offset=70 26 | list_split_offset=0 27 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/NetworkingClient.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://jmbybekwvead"] 2 | 3 | [ext_resource type="Script" path="res://NetworkingClient.gd" id="1_rlsr6"] 4 | 5 | [node name="Networking" type="Node"] 6 | script = ExtResource("1_rlsr6") 7 | 8 | [node name="Lobby" type="Node2D" parent="."] 9 | 10 | [node name="ConnectBtn" type="Button" parent="Lobby"] 11 | offset_left = 437.0 12 | offset_top = 166.0 13 | offset_right = 619.0 14 | offset_bottom = 243.0 15 | text = "Connect" 16 | 17 | [node name="DisconnectBtn" type="Button" parent="Lobby"] 18 | offset_left = 437.0 19 | offset_top = 261.0 20 | offset_right = 619.0 21 | offset_bottom = 338.0 22 | text = "Disconnect" 23 | 24 | [connection signal="pressed" from="Lobby/ConnectBtn" to="." method="_on_connect_btn_pressed"] 25 | [connection signal="pressed" from="Lobby/DisconnectBtn" to="." method="_on_disconnect_btn_pressed"] 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Godot4MultiplayerMVP 2 | A minimal working prototype for Godot 4 multiplayer using ENetMultiplayerPeer. 3 | 4 | This project is using a separate client and server. 5 | There is no implementation of player characters, names, or other things. Only setting up the connection and syncing the player list. 6 | 7 | ## Server Output
8 | ![image](https://user-images.githubusercontent.com/16819588/215179638-147f1e49-3e88-433b-bed9-9e07e817cb9c.png)
9 | ## Client Output
10 | ![image](https://user-images.githubusercontent.com/16819588/215179740-4bf42197-ab2c-447c-8757-3532d898751d.png)
11 | ## Client UI
12 | ![image](https://user-images.githubusercontent.com/16819588/215179863-285a8418-cda4-43cc-9dd7-ee8501029c8b.png)
13 | 14 | 15 |
16 |
17 | 18 | 19 | Resources I used: 20 | - https://godotengine.org/article/multiplayer-changes-godot-4-0-report-2/ 21 | - https://github.com/TheGodojo/Godot-4-Networking-Demonstration-Complete/ 22 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cw2qpwg50jusy" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/lossy_quality=0.7 20 | compress/hdr_compression=1 21 | compress/bptc_ldr=0 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://ccbjvioeil0v4" 6 | path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/lossy_quality=0.7 20 | compress/hdr_compression=1 21 | compress/bptc_ldr=0 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 zwometer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/NetworkingClient.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | const DEV = true 4 | 5 | @onready var connect_btn = $Lobby/ConnectBtn 6 | @onready var disconnect_btn = $Lobby/DisconnectBtn 7 | 8 | var multiplayer_peer = ENetMultiplayerPeer.new() 9 | var url : String = "your-prod.url" 10 | const PORT = 9009 11 | 12 | var connected_peer_ids = [] 13 | 14 | 15 | func _ready(): 16 | if DEV == true: 17 | url = "127.0.0.1" 18 | update_connection_buttons() 19 | multiplayer.server_disconnected.connect(_on_server_disconnected) 20 | 21 | @rpc 22 | func sync_player_list(updated_connected_peer_ids): 23 | connected_peer_ids = updated_connected_peer_ids 24 | multiplayer_peer.get_unique_id() 25 | update_connection_buttons() 26 | print("Currently connected Players: " + str(connected_peer_ids)) 27 | 28 | 29 | func _on_connect_btn_pressed() -> void: 30 | print("Connecting ...") 31 | multiplayer_peer.create_client(url, PORT) 32 | multiplayer.multiplayer_peer = multiplayer_peer 33 | update_connection_buttons() 34 | 35 | 36 | func _on_disconnect_btn_pressed(): 37 | multiplayer_peer.close() 38 | update_connection_buttons() 39 | print("Disconnected.") 40 | 41 | 42 | func _on_server_disconnected(): 43 | multiplayer_peer.close() 44 | update_connection_buttons() 45 | print("Connection to server lost.") 46 | 47 | 48 | func update_connection_buttons() -> void: 49 | if multiplayer_peer.get_connection_status() == multiplayer_peer.CONNECTION_DISCONNECTED: 50 | connect_btn.disabled = false 51 | disconnect_btn.disabled = true 52 | if multiplayer_peer.get_connection_status() == multiplayer_peer.CONNECTION_CONNECTING: 53 | connect_btn.disabled = true 54 | disconnect_btn.disabled = true 55 | if multiplayer_peer.get_connection_status() == multiplayer_peer.CONNECTION_CONNECTED: 56 | connect_btn.disabled = true 57 | disconnect_btn.disabled = false 58 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/NetworkingServer.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | const DEV = true 4 | 5 | var multiplayer_peer = ENetMultiplayerPeer.new() 6 | var url : String = "your-prod.url" 7 | const PORT = 9009 8 | 9 | var connected_peer_ids = [] 10 | 11 | 12 | func _ready(): 13 | if DEV == true: 14 | url = "127.0.0.1" 15 | multiplayer_peer.create_server(PORT) 16 | multiplayer.multiplayer_peer = multiplayer_peer 17 | multiplayer_peer.peer_connected.connect(_on_peer_connected) 18 | multiplayer_peer.peer_disconnected.connect(_on_peer_disconnected) 19 | print("Server is up and running.") 20 | 21 | 22 | func _on_peer_connected(new_peer_id : int) -> void: 23 | print("Player " + str(new_peer_id) + " is joining...") 24 | # The connect signal fires before the client is added to the connected 25 | # clients in multiplayer.get_peers(), so we wait for a moment. 26 | await get_tree().create_timer(1).timeout 27 | add_player(new_peer_id) 28 | 29 | 30 | func add_player(new_peer_id : int) -> void: 31 | connected_peer_ids.append(new_peer_id) 32 | print("Player " + str(new_peer_id) + " joined.") 33 | print("Currently connected Players: " + str(connected_peer_ids)) 34 | rpc("sync_player_list", connected_peer_ids) 35 | 36 | 37 | func _on_peer_disconnected(leaving_peer_id : int) -> void: 38 | # The disconnect signal fires before the client is removed from the connected 39 | # clients in multiplayer.get_peers(), so we wait for a moment. 40 | await get_tree().create_timer(1).timeout 41 | remove_player(leaving_peer_id) 42 | 43 | 44 | func remove_player(leaving_peer_id : int) -> void: 45 | var peer_idx_in_peer_list : int = connected_peer_ids.find(leaving_peer_id) 46 | if peer_idx_in_peer_list != -1: 47 | connected_peer_ids.remove_at(peer_idx_in_peer_list) 48 | print("Player " + str(leaving_peer_id) + " disconnected.") 49 | rpc("sync_player_list", connected_peer_ids) 50 | 51 | 52 | @rpc 53 | func sync_player_list(_updated_connected_peer_ids): 54 | pass # only implemented in client (but still has to exist here) 55 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/server.tscn-editstate-c89a2950482f3a432bab03a0591e8d28.cfg: -------------------------------------------------------------------------------- 1 | [editor_states] 2 | 3 | Anim={ 4 | "visible": false 5 | } 6 | 2D={ 7 | "grid_offset": Vector2(0, 0), 8 | "grid_snap_active": false, 9 | "grid_step": Vector2(8, 8), 10 | "grid_visibility": 1, 11 | "ofs": Vector2(-165, -110), 12 | "primary_grid_steps": 8, 13 | "show_edit_locks": true, 14 | "show_guides": true, 15 | "show_helpers": false, 16 | "show_origin": true, 17 | "show_rulers": true, 18 | "show_transformation_gizmos": true, 19 | "show_viewport": true, 20 | "show_zoom_control": true, 21 | "smart_snap_active": false, 22 | "snap_guides": true, 23 | "snap_node_anchors": true, 24 | "snap_node_center": true, 25 | "snap_node_parent": true, 26 | "snap_node_sides": true, 27 | "snap_other_nodes": true, 28 | "snap_pixel": true, 29 | "snap_relative": false, 30 | "snap_rotation": false, 31 | "snap_rotation_offset": 0.0, 32 | "snap_rotation_step": 0.261799, 33 | "snap_scale": false, 34 | "snap_scale_step": 0.1, 35 | "zoom": 1.0 36 | } 37 | 3D={ 38 | "fov": 70.01, 39 | "gizmos_status": { 40 | "AudioListener3D": 0, 41 | "AudioStreamPlayer3D": 0, 42 | "CPUParticles3D": 0, 43 | "CSGShape3D": 0, 44 | "Camera3D": 0, 45 | "CollisionObject3D": 0, 46 | "CollisionPolygon3D": 0, 47 | "CollisionShape3D": 0, 48 | "Decal": 0, 49 | "FogVolume": 0, 50 | "GPUParticles3D": 0, 51 | "GPUParticlesCollision3D": 0, 52 | "Joint3D": 0, 53 | "Light3D": 0, 54 | "LightmapGI": 0, 55 | "LightmapProbe": 0, 56 | "Marker3D": 0, 57 | "NavigationLink3D": 0, 58 | "NavigationRegion3D": 0, 59 | "OccluderInstance3D": 0, 60 | "Path3D": 0, 61 | "PhysicalBone3D": 0, 62 | "RayCast3D": 0, 63 | "ReflectionProbe": 0, 64 | "ShapeCast3D": 0, 65 | "Skeleton3D": 0, 66 | "SoftBody3D": 0, 67 | "SpringArm3D": 0, 68 | "VehicleWheel3D": 0, 69 | "VisibleOnScreenNotifier3D": 0, 70 | "VoxelGI": 0 71 | }, 72 | "local_coords": false, 73 | "preview_sun_env": { 74 | "environ_ao_enabled": false, 75 | "environ_enabled": true, 76 | "environ_energy": 1.0, 77 | "environ_gi_enabled": false, 78 | "environ_glow_enabled": true, 79 | "environ_ground_color": Color(0.2, 0.169, 0.133, 1), 80 | "environ_sky_color": Color(0.385, 0.454, 0.55, 1), 81 | "environ_tonemap_enabled": true, 82 | "sun_color": Color(1, 1, 1, 1), 83 | "sun_enabled": true, 84 | "sun_energy": 1.0, 85 | "sun_max_distance": 100.0, 86 | "sun_rotation": Vector2(-1.0472, 2.61799) 87 | }, 88 | "rotate_snap": 15.0, 89 | "scale_snap": 10.0, 90 | "show_grid": true, 91 | "show_origin": true, 92 | "snap_enabled": false, 93 | "translate_snap": 1.0, 94 | "viewport_mode": 1, 95 | "viewports": [{ 96 | "auto_orthogonal": false, 97 | "auto_orthogonal_enabled": true, 98 | "cinematic_preview": false, 99 | "display_mode": 20, 100 | "distance": 4.0, 101 | "doppler": false, 102 | "frame_time": false, 103 | "gizmos": true, 104 | "half_res": false, 105 | "information": false, 106 | "listener": true, 107 | "position": Vector3(0, 0, 0), 108 | "use_environment": false, 109 | "use_orthogonal": false, 110 | "view_type": 0, 111 | "x_rotation": 0.5, 112 | "y_rotation": -0.5 113 | }, { 114 | "auto_orthogonal": false, 115 | "auto_orthogonal_enabled": true, 116 | "cinematic_preview": false, 117 | "display_mode": 20, 118 | "distance": 4.0, 119 | "doppler": false, 120 | "frame_time": false, 121 | "gizmos": true, 122 | "half_res": false, 123 | "information": false, 124 | "listener": false, 125 | "position": Vector3(0, 0, 0), 126 | "use_environment": false, 127 | "use_orthogonal": false, 128 | "view_type": 0, 129 | "x_rotation": 0.5, 130 | "y_rotation": -0.5 131 | }, { 132 | "auto_orthogonal": false, 133 | "auto_orthogonal_enabled": true, 134 | "cinematic_preview": false, 135 | "display_mode": 20, 136 | "distance": 4.0, 137 | "doppler": false, 138 | "frame_time": false, 139 | "gizmos": true, 140 | "half_res": false, 141 | "information": false, 142 | "listener": false, 143 | "position": Vector3(0, 0, 0), 144 | "use_environment": false, 145 | "use_orthogonal": false, 146 | "view_type": 0, 147 | "x_rotation": 0.5, 148 | "y_rotation": -0.5 149 | }, { 150 | "auto_orthogonal": false, 151 | "auto_orthogonal_enabled": true, 152 | "cinematic_preview": false, 153 | "display_mode": 20, 154 | "distance": 4.0, 155 | "doppler": false, 156 | "frame_time": false, 157 | "gizmos": true, 158 | "half_res": false, 159 | "information": false, 160 | "listener": false, 161 | "position": Vector3(0, 0, 0), 162 | "use_environment": false, 163 | "use_orthogonal": false, 164 | "view_type": 0, 165 | "x_rotation": 0.5, 166 | "y_rotation": -0.5 167 | }], 168 | "zfar": 4000.01, 169 | "znear": 0.05 170 | } 171 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPServer/.godot/editor/NetworkingServer.tscn-editstate-67ca15145d9bad4a4322ef1520d60e36.cfg: -------------------------------------------------------------------------------- 1 | [editor_states] 2 | 3 | Anim={ 4 | "visible": false 5 | } 6 | 2D={ 7 | "grid_offset": Vector2(0, 0), 8 | "grid_snap_active": false, 9 | "grid_step": Vector2(8, 8), 10 | "grid_visibility": 1, 11 | "ofs": Vector2(-165, -110), 12 | "primary_grid_steps": 8, 13 | "show_edit_locks": true, 14 | "show_guides": true, 15 | "show_helpers": false, 16 | "show_origin": true, 17 | "show_rulers": true, 18 | "show_transformation_gizmos": true, 19 | "show_viewport": true, 20 | "show_zoom_control": true, 21 | "smart_snap_active": false, 22 | "snap_guides": true, 23 | "snap_node_anchors": true, 24 | "snap_node_center": true, 25 | "snap_node_parent": true, 26 | "snap_node_sides": true, 27 | "snap_other_nodes": true, 28 | "snap_pixel": true, 29 | "snap_relative": false, 30 | "snap_rotation": false, 31 | "snap_rotation_offset": 0.0, 32 | "snap_rotation_step": 0.261799, 33 | "snap_scale": false, 34 | "snap_scale_step": 0.1, 35 | "zoom": 1.0 36 | } 37 | 3D={ 38 | "fov": 70.01, 39 | "gizmos_status": { 40 | "AudioListener3D": 0, 41 | "AudioStreamPlayer3D": 0, 42 | "CPUParticles3D": 0, 43 | "CSGShape3D": 0, 44 | "Camera3D": 0, 45 | "CollisionObject3D": 0, 46 | "CollisionPolygon3D": 0, 47 | "CollisionShape3D": 0, 48 | "Decal": 0, 49 | "FogVolume": 0, 50 | "GPUParticles3D": 0, 51 | "GPUParticlesCollision3D": 0, 52 | "Joint3D": 0, 53 | "Light3D": 0, 54 | "LightmapGI": 0, 55 | "LightmapProbe": 0, 56 | "Marker3D": 0, 57 | "NavigationLink3D": 0, 58 | "NavigationRegion3D": 0, 59 | "OccluderInstance3D": 0, 60 | "Path3D": 0, 61 | "PhysicalBone3D": 0, 62 | "RayCast3D": 0, 63 | "ReflectionProbe": 0, 64 | "ShapeCast3D": 0, 65 | "Skeleton3D": 0, 66 | "SoftBody3D": 0, 67 | "SpringArm3D": 0, 68 | "VehicleWheel3D": 0, 69 | "VisibleOnScreenNotifier3D": 0, 70 | "VoxelGI": 0 71 | }, 72 | "local_coords": false, 73 | "preview_sun_env": { 74 | "environ_ao_enabled": false, 75 | "environ_enabled": true, 76 | "environ_energy": 1.0, 77 | "environ_gi_enabled": false, 78 | "environ_glow_enabled": true, 79 | "environ_ground_color": Color(0.2, 0.169, 0.133, 1), 80 | "environ_sky_color": Color(0.385, 0.454, 0.55, 1), 81 | "environ_tonemap_enabled": true, 82 | "sun_color": Color(1, 1, 1, 1), 83 | "sun_enabled": true, 84 | "sun_energy": 1.0, 85 | "sun_max_distance": 100.0, 86 | "sun_rotation": Vector2(-1.0472, 2.61799) 87 | }, 88 | "rotate_snap": 15.0, 89 | "scale_snap": 10.0, 90 | "show_grid": true, 91 | "show_origin": true, 92 | "snap_enabled": false, 93 | "translate_snap": 1.0, 94 | "viewport_mode": 1, 95 | "viewports": [{ 96 | "auto_orthogonal": false, 97 | "auto_orthogonal_enabled": true, 98 | "cinematic_preview": false, 99 | "display_mode": 20, 100 | "distance": 4.0, 101 | "doppler": false, 102 | "frame_time": false, 103 | "gizmos": true, 104 | "half_res": false, 105 | "information": false, 106 | "listener": true, 107 | "position": Vector3(0, 0, 0), 108 | "use_environment": false, 109 | "use_orthogonal": false, 110 | "view_type": 0, 111 | "x_rotation": 0.5, 112 | "y_rotation": -0.5 113 | }, { 114 | "auto_orthogonal": false, 115 | "auto_orthogonal_enabled": true, 116 | "cinematic_preview": false, 117 | "display_mode": 20, 118 | "distance": 4.0, 119 | "doppler": false, 120 | "frame_time": false, 121 | "gizmos": true, 122 | "half_res": false, 123 | "information": false, 124 | "listener": false, 125 | "position": Vector3(0, 0, 0), 126 | "use_environment": false, 127 | "use_orthogonal": false, 128 | "view_type": 0, 129 | "x_rotation": 0.5, 130 | "y_rotation": -0.5 131 | }, { 132 | "auto_orthogonal": false, 133 | "auto_orthogonal_enabled": true, 134 | "cinematic_preview": false, 135 | "display_mode": 20, 136 | "distance": 4.0, 137 | "doppler": false, 138 | "frame_time": false, 139 | "gizmos": true, 140 | "half_res": false, 141 | "information": false, 142 | "listener": false, 143 | "position": Vector3(0, 0, 0), 144 | "use_environment": false, 145 | "use_orthogonal": false, 146 | "view_type": 0, 147 | "x_rotation": 0.5, 148 | "y_rotation": -0.5 149 | }, { 150 | "auto_orthogonal": false, 151 | "auto_orthogonal_enabled": true, 152 | "cinematic_preview": false, 153 | "display_mode": 20, 154 | "distance": 4.0, 155 | "doppler": false, 156 | "frame_time": false, 157 | "gizmos": true, 158 | "half_res": false, 159 | "information": false, 160 | "listener": false, 161 | "position": Vector3(0, 0, 0), 162 | "use_environment": false, 163 | "use_orthogonal": false, 164 | "view_type": 0, 165 | "x_rotation": 0.5, 166 | "y_rotation": -0.5 167 | }], 168 | "zfar": 4000.01, 169 | "znear": 0.05 170 | } 171 | -------------------------------------------------------------------------------- /Godot4MultiplayerMVPClient/.godot/editor/NetworkingClient.tscn-editstate-db6bc7cdfc97dc30471557a80338ec26.cfg: -------------------------------------------------------------------------------- 1 | [editor_states] 2 | 3 | Anim={ 4 | "visible": false 5 | } 6 | 2D={ 7 | "grid_offset": Vector2(0, 0), 8 | "grid_snap_active": false, 9 | "grid_step": Vector2(8, 8), 10 | "grid_visibility": 1, 11 | "ofs": Vector2(-318.687, -96.8157), 12 | "primary_grid_steps": 8, 13 | "show_edit_locks": true, 14 | "show_guides": true, 15 | "show_helpers": false, 16 | "show_origin": true, 17 | "show_rulers": true, 18 | "show_transformation_gizmos": true, 19 | "show_viewport": true, 20 | "show_zoom_control": true, 21 | "smart_snap_active": false, 22 | "snap_guides": true, 23 | "snap_node_anchors": true, 24 | "snap_node_center": true, 25 | "snap_node_parent": true, 26 | "snap_node_sides": true, 27 | "snap_other_nodes": true, 28 | "snap_pixel": true, 29 | "snap_relative": false, 30 | "snap_rotation": false, 31 | "snap_rotation_offset": 0.0, 32 | "snap_rotation_step": 0.261799, 33 | "snap_scale": false, 34 | "snap_scale_step": 0.1, 35 | "zoom": 1.41421 36 | } 37 | 3D={ 38 | "fov": 70.01, 39 | "gizmos_status": { 40 | "AudioListener3D": 0, 41 | "AudioStreamPlayer3D": 0, 42 | "CPUParticles3D": 0, 43 | "CSGShape3D": 0, 44 | "Camera3D": 0, 45 | "CollisionObject3D": 0, 46 | "CollisionPolygon3D": 0, 47 | "CollisionShape3D": 0, 48 | "Decal": 0, 49 | "FogVolume": 0, 50 | "GPUParticles3D": 0, 51 | "GPUParticlesCollision3D": 0, 52 | "Joint3D": 0, 53 | "Light3D": 0, 54 | "LightmapGI": 0, 55 | "LightmapProbe": 0, 56 | "Marker3D": 0, 57 | "NavigationLink3D": 0, 58 | "NavigationRegion3D": 0, 59 | "OccluderInstance3D": 0, 60 | "Path3D": 0, 61 | "PhysicalBone3D": 0, 62 | "RayCast3D": 0, 63 | "ReflectionProbe": 0, 64 | "ShapeCast3D": 0, 65 | "Skeleton3D": 0, 66 | "SoftBody3D": 0, 67 | "SpringArm3D": 0, 68 | "VehicleWheel3D": 0, 69 | "VisibleOnScreenNotifier3D": 0, 70 | "VoxelGI": 0 71 | }, 72 | "local_coords": false, 73 | "preview_sun_env": { 74 | "environ_ao_enabled": false, 75 | "environ_enabled": true, 76 | "environ_energy": 1.0, 77 | "environ_gi_enabled": false, 78 | "environ_glow_enabled": true, 79 | "environ_ground_color": Color(0.2, 0.169, 0.133, 1), 80 | "environ_sky_color": Color(0.385, 0.454, 0.55, 1), 81 | "environ_tonemap_enabled": true, 82 | "sun_color": Color(1, 1, 1, 1), 83 | "sun_enabled": true, 84 | "sun_energy": 1.0, 85 | "sun_max_distance": 100.0, 86 | "sun_rotation": Vector2(-1.0472, 2.61799) 87 | }, 88 | "rotate_snap": 15.0, 89 | "scale_snap": 10.0, 90 | "show_grid": true, 91 | "show_origin": true, 92 | "snap_enabled": false, 93 | "translate_snap": 1.0, 94 | "viewport_mode": 1, 95 | "viewports": [{ 96 | "auto_orthogonal": false, 97 | "auto_orthogonal_enabled": true, 98 | "cinematic_preview": false, 99 | "display_mode": 20, 100 | "distance": 4.0, 101 | "doppler": false, 102 | "frame_time": false, 103 | "gizmos": true, 104 | "half_res": false, 105 | "information": false, 106 | "listener": true, 107 | "position": Vector3(0, 0, 0), 108 | "use_environment": false, 109 | "use_orthogonal": false, 110 | "view_type": 0, 111 | "x_rotation": 0.5, 112 | "y_rotation": -0.5 113 | }, { 114 | "auto_orthogonal": false, 115 | "auto_orthogonal_enabled": true, 116 | "cinematic_preview": false, 117 | "display_mode": 20, 118 | "distance": 4.0, 119 | "doppler": false, 120 | "frame_time": false, 121 | "gizmos": true, 122 | "half_res": false, 123 | "information": false, 124 | "listener": false, 125 | "position": Vector3(0, 0, 0), 126 | "use_environment": false, 127 | "use_orthogonal": false, 128 | "view_type": 0, 129 | "x_rotation": 0.5, 130 | "y_rotation": -0.5 131 | }, { 132 | "auto_orthogonal": false, 133 | "auto_orthogonal_enabled": true, 134 | "cinematic_preview": false, 135 | "display_mode": 20, 136 | "distance": 4.0, 137 | "doppler": false, 138 | "frame_time": false, 139 | "gizmos": true, 140 | "half_res": false, 141 | "information": false, 142 | "listener": false, 143 | "position": Vector3(0, 0, 0), 144 | "use_environment": false, 145 | "use_orthogonal": false, 146 | "view_type": 0, 147 | "x_rotation": 0.5, 148 | "y_rotation": -0.5 149 | }, { 150 | "auto_orthogonal": false, 151 | "auto_orthogonal_enabled": true, 152 | "cinematic_preview": false, 153 | "display_mode": 20, 154 | "distance": 4.0, 155 | "doppler": false, 156 | "frame_time": false, 157 | "gizmos": true, 158 | "half_res": false, 159 | "information": false, 160 | "listener": false, 161 | "position": Vector3(0, 0, 0), 162 | "use_environment": false, 163 | "use_orthogonal": false, 164 | "view_type": 0, 165 | "x_rotation": 0.5, 166 | "y_rotation": -0.5 167 | }], 168 | "zfar": 4000.01, 169 | "znear": 0.05 170 | } 171 | --------------------------------------------------------------------------------