├── .gitignore ├── godot-multi ├── icon.png ├── bin │ ├── x11 │ │ └── release │ │ │ └── libgitapi.so │ └── win64 │ │ └── release │ │ └── libgitapi.dll ├── default_env.tres ├── addons │ └── mqtt │ │ ├── plugin.cfg │ │ └── mqtt.gd ├── git_api.gdns ├── git_api.gdnlib ├── player.tscn ├── gamestate.gd ├── icon.png.import ├── player.gd ├── main_menu.gd ├── project.godot ├── game_world.tscn ├── game_world.gd ├── network.gd ├── main_menu.tscn └── export_presets.cfg ├── README.md ├── .gitattributes └── .github └── workflows └── godot-ci.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Import cache 2 | .import/ 3 | 4 | # Binaries 5 | bin/ 6 | build/ 7 | lib/ 8 | -------------------------------------------------------------------------------- /godot-multi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DynamicDevices/godot-multi/HEAD/godot-multi/icon.png -------------------------------------------------------------------------------- /godot-multi/bin/x11/release/libgitapi.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DynamicDevices/godot-multi/HEAD/godot-multi/bin/x11/release/libgitapi.so -------------------------------------------------------------------------------- /godot-multi/bin/win64/release/libgitapi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DynamicDevices/godot-multi/HEAD/godot-multi/bin/win64/release/libgitapi.dll -------------------------------------------------------------------------------- /godot-multi/default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /godot-multi/addons/mqtt/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="mqtt" 4 | description="MQTT implementation for Godot based on Pycom implementation (see source for details)" 5 | author="Alex J Lennon" 6 | version="1.0" 7 | script="mqtt.gd" 8 | -------------------------------------------------------------------------------- /godot-multi/git_api.gdns: -------------------------------------------------------------------------------- 1 | [gd_resource type="NativeScript" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://git_api.gdnlib" type="GDNativeLibrary" id=1] 4 | 5 | [resource] 6 | resource_name = "GitAPI" 7 | class_name = "GitAPI" 8 | library = ExtResource( 1 ) 9 | script_class_name = "GitAPI" 10 | -------------------------------------------------------------------------------- /godot-multi/git_api.gdnlib: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | singleton=true 4 | load_once=true 5 | symbol_prefix="godot_" 6 | reloadable=false 7 | 8 | [entry] 9 | 10 | Windows.64="res://bin/win64/release/libgitapi.dll" 11 | X11.64="res://bin/x11/release/libgitapi.so" 12 | 13 | [dependencies] 14 | 15 | Windows.64=[ ] 16 | X11.64=[ ] 17 | -------------------------------------------------------------------------------- /godot-multi/player.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://player.gd" type="Script" id=1] 4 | [ext_resource path="res://icon.png" type="Texture" id=2] 5 | 6 | [node name="PlayerRoot" type="Node2D"] 7 | script = ExtResource( 1 ) 8 | 9 | [node name="icon" type="Sprite" parent="."] 10 | scale = Vector2( 0.6, 0.6 ) 11 | texture = ExtResource( 2 ) 12 | -------------------------------------------------------------------------------- /godot-multi/gamestate.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var player_info = { 4 | name = "Player", # How this player will be shown within the GUI 5 | net_id = 1, # By default everyone receives "server ID" (AJL - ???) 6 | actor_path = "res://player.tscn", # The class used to represent the player in the game world 7 | char_color = Color(1, 1, 1) # By default don't modulate the icon color 8 | } 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Multiplayer tutorial 3 | 4 | Working through the excellent tutorial [here](http://kehomsforge.com/tutorials/multi/gdMultiplayerSetup) 5 | 6 | - Parts I/II are completed and tagged part-02 7 | - Part III is completed and tagged part-03 8 | - There is a Linux debug export of the resulting game to try in `exports/` 9 | 10 | # Auto Build 11 | 12 | Commits now build exports including to web build. To play current web build go to 13 | 14 | https://dynamicdevices.github.io/godot-multi 15 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set the default behavior, in case people don't have core.autocrlf set. 2 | * text=auto 3 | 4 | # Explicitly declare text files you want to always be normalized and converted 5 | # to native line endings on checkout. 6 | *.cpp text 7 | *.c text 8 | *.h text 9 | *.gd text 10 | *.cs text 11 | 12 | # Declare files that will always have CRLF line endings on checkout. 13 | *.sln text eol=crlf 14 | 15 | # Denote all files that are truly binary and should not be modified. 16 | *.png binary 17 | *.jpg binary 18 | -------------------------------------------------------------------------------- /godot-multi/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /godot-multi/player.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var move_speed = 300 4 | 5 | slave var repl_position = Vector2() 6 | 7 | func set_dominant_color(color): 8 | $icon.modulate = color 9 | 10 | # Called when the node enters the scene tree for the first time. 11 | func _ready(): 12 | pass # Replace with function body. 13 | 14 | 15 | # Called every frame. 'delta' is the elapsed time since the previous frame. 16 | func _process(delta): 17 | if (is_network_master()): 18 | # Initialize the movement vector 19 | var move_dir = Vector2(0, 0) 20 | 21 | # Poll the actions keys 22 | if (Input.is_action_pressed("move_up")): 23 | # Negative Y will move the actor UP on the screen 24 | move_dir.y -= 1 25 | if (Input.is_action_pressed("move_down")): 26 | # Positive Y will move the actor DOWN on the screen 27 | move_dir.y += 1 28 | if (Input.is_action_pressed("move_left")): 29 | # Negative X will move the actor LEFT on the screen 30 | move_dir.x -= 1 31 | if (Input.is_action_pressed("move_right")): 32 | # Positive X will move the actor RIGHT on the screen 33 | move_dir.x += 1 34 | 35 | # Apply the movement formula to obtain the new actor position 36 | position += move_dir.normalized() * move_speed * delta 37 | 38 | mqtt.publish("VR/position", String(position)) 39 | 40 | # Replicate the position 41 | rset("repl_position", position) 42 | else: 43 | # Take replicated variables to set current actor state 44 | position = repl_position 45 | -------------------------------------------------------------------------------- /godot-multi/main_menu.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | func set_player_info(): 4 | if (!$PanelPlayer/txtPlayerName.text.empty()): 5 | gamestate.player_info.name = $PanelPlayer/txtPlayerName.text 6 | gamestate.player_info.char_color = $PanelPlayer/btColor.color 7 | 8 | func _on_btCreate_pressed(): 9 | # Properly set the local player information 10 | set_player_info() 11 | 12 | # Gather values from the GUI and fill the network.server_info dictionary 13 | if (!$PanelHost/txtServerName.text.empty()): 14 | network.server_info.name = $PanelHost/txtServerName.text 15 | network.server_info.max_players = int($PanelHost/txtMaxPlayers.value) 16 | network.server_info.used_port = int($PanelHost/txtServerPort.text) 17 | 18 | # And create the server, using the function previously added into the code 19 | print("Creating Server") 20 | network.create_server() 21 | 22 | func _on_btJoin_pressed(): 23 | 24 | # Properly set the local player information 25 | set_player_info() 26 | 27 | var port = int($PanelJoin/txtJoinPort.text) 28 | var ip = $PanelJoin/txtJoinIP.text 29 | print("Joining Server") 30 | network.join_server(ip, port) 31 | 32 | func _on_ready_to_play(): 33 | print("Moving to game world scene") 34 | get_tree().change_scene("res://game_world.tscn") 35 | 36 | func _on_join_fail(): 37 | print("Failed to join server") 38 | 39 | func _ready(): 40 | network.connect("server_created", self, "_on_ready_to_play") 41 | network.connect("join_success", self, "_on_ready_to_play") 42 | network.connect("join_fail", self, "_on_join_fail") 43 | 44 | # Note: Feature tags are case-sensitive! It's "Server", not "server". 45 | if OS.has_feature("Server"): 46 | # Run your server startup code here... 47 | # Note that using this check may break unit testing scripts when 48 | # running them with headless or server binaries. 49 | print("Running in server mode") 50 | network.server_info.name = "Lobby Server" 51 | network.server_info.max_players = 6 52 | network.server_info.used_port = 4546 53 | print("Creating server") 54 | network.create_server() 55 | -------------------------------------------------------------------------------- /godot-multi/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=4 10 | 11 | _global_script_classes=[ { 12 | "base": "", 13 | "class": "GitAPI", 14 | "language": "NativeScript", 15 | "path": "res://git_api.gdns" 16 | } ] 17 | _global_script_class_icons={ 18 | "GitAPI": "" 19 | } 20 | 21 | [application] 22 | 23 | config/name="Lobby Server" 24 | run/main_scene="res://main_menu.tscn" 25 | config/icon="res://icon.png" 26 | 27 | [autoload] 28 | 29 | gamestate="*res://gamestate.gd" 30 | network="*res://network.gd" 31 | mqtt="*res://addons/mqtt/mqtt.gd" 32 | 33 | [editor_plugins] 34 | 35 | enabled=PoolStringArray( "mqtt" ) 36 | 37 | [gdnative] 38 | 39 | singletons=[ "res://git_api.gdnlib" ] 40 | 41 | [input] 42 | 43 | move_up={ 44 | "deadzone": 0.5, 45 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"unicode":0,"echo":false,"script":null) 46 | ] 47 | } 48 | move_down={ 49 | "deadzone": 0.5, 50 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"unicode":0,"echo":false,"script":null) 51 | ] 52 | } 53 | move_left={ 54 | "deadzone": 0.5, 55 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"unicode":0,"echo":false,"script":null) 56 | ] 57 | } 58 | move_right={ 59 | "deadzone": 0.5, 60 | "events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"unicode":0,"echo":false,"script":null) 61 | ] 62 | } 63 | 64 | [rendering] 65 | 66 | environment/default_environment="res://default_env.tres" 67 | -------------------------------------------------------------------------------- /godot-multi/game_world.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://game_world.gd" type="Script" id=1] 4 | 5 | [node name="CanvasLayer" type="CanvasLayer"] 6 | 7 | [node name="GameWorld" type="Node2D" parent="."] 8 | position = Vector2( 72, 64 ) 9 | script = ExtResource( 1 ) 10 | 11 | [node name="HUD" type="CanvasLayer" parent="GameWorld"] 12 | 13 | [node name="PanelPlayerList" type="Panel" parent="GameWorld/HUD"] 14 | self_modulate = Color( 1, 1, 1, 0 ) 15 | margin_left = 31.0 16 | margin_top = 29.0 17 | margin_right = 171.0 18 | margin_bottom = 316.0 19 | __meta__ = { 20 | "_edit_use_anchors_": false 21 | } 22 | 23 | [node name="lblLocalPlayer" type="Label" parent="GameWorld/HUD/PanelPlayerList"] 24 | anchor_left = 0.514286 25 | anchor_top = 0.222997 26 | anchor_right = 0.514286 27 | anchor_bottom = 0.222997 28 | margin_left = 10.0 29 | margin_top = 11.0 30 | margin_right = 120.0 31 | margin_bottom = 25.0 32 | text = "LocalPlayerName" 33 | __meta__ = { 34 | "_edit_use_anchors_": false 35 | } 36 | 37 | [node name="boxList" type="VBoxContainer" parent="GameWorld/HUD/PanelPlayerList"] 38 | margin_left = 11.0 39 | margin_top = 34.0 40 | margin_right = 141.0 41 | margin_bottom = 212.0 42 | 43 | [node name="SpawnPoints" type="Node2D" parent="GameWorld"] 44 | 45 | [node name="1" type="Position2D" parent="GameWorld/SpawnPoints"] 46 | 47 | [node name="2" type="Position2D" parent="GameWorld/SpawnPoints"] 48 | position = Vector2( 64, 0 ) 49 | 50 | [node name="3" type="Position2D" parent="GameWorld/SpawnPoints"] 51 | position = Vector2( 128, 0 ) 52 | 53 | [node name="4" type="Position2D" parent="GameWorld/SpawnPoints"] 54 | position = Vector2( 192, 0 ) 55 | 56 | [node name="5" type="Position2D" parent="GameWorld/SpawnPoints"] 57 | position = Vector2( 0, 64 ) 58 | 59 | [node name="6" type="Position2D" parent="GameWorld/SpawnPoints"] 60 | position = Vector2( 64, 64 ) 61 | 62 | [node name="7" type="Position2D" parent="GameWorld/SpawnPoints"] 63 | position = Vector2( 128, 64 ) 64 | 65 | [node name="8" type="Position2D" parent="GameWorld/SpawnPoints"] 66 | position = Vector2( 192, 64 ) 67 | 68 | [node name="9" type="Position2D" parent="GameWorld/SpawnPoints"] 69 | position = Vector2( 0, 128 ) 70 | 71 | [node name="10" type="Position2D" parent="GameWorld/SpawnPoints"] 72 | position = Vector2( 64, 128 ) 73 | 74 | [node name="11" type="Position2D" parent="GameWorld/SpawnPoints"] 75 | position = Vector2( 128, 128 ) 76 | 77 | [node name="12" type="Position2D" parent="GameWorld/SpawnPoints"] 78 | position = Vector2( 192, 128 ) 79 | 80 | [node name="13" type="Position2D" parent="GameWorld/SpawnPoints"] 81 | position = Vector2( 0, 192 ) 82 | 83 | [node name="14" type="Position2D" parent="GameWorld/SpawnPoints"] 84 | position = Vector2( 64, 192 ) 85 | 86 | [node name="15" type="Position2D" parent="GameWorld/SpawnPoints"] 87 | position = Vector2( 128, 192 ) 88 | 89 | [node name="16" type="Position2D" parent="GameWorld/SpawnPoints"] 90 | position = Vector2( 192, 192 ) 91 | -------------------------------------------------------------------------------- /.github/workflows/godot-ci.yml: -------------------------------------------------------------------------------- 1 | name: "godot-ci export" 2 | on: push 3 | 4 | env: 5 | GODOT_VERSION: 3.2.3 6 | EXPORT_NAME: godot-multi 7 | 8 | jobs: 9 | export-windows: 10 | name: Windows Export 11 | runs-on: ubuntu-latest 12 | container: 13 | image: barichello/godot-ci:3.2.3 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v1 17 | - name: Setup 18 | run: | 19 | mkdir -v -p ~/.local/share/godot/templates 20 | mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable 21 | - name: Windows Build 22 | run: | 23 | mkdir -v -p build/windows 24 | cd $EXPORT_NAME 25 | godot -v --export "Windows Desktop" ../build/windows/$EXPORT_NAME.exe 26 | rm ../build/windows/libgitapi.dll 27 | - name: Upload Artifact 28 | uses: actions/upload-artifact@v1 29 | with: 30 | name: windows 31 | path: build/windows 32 | 33 | export-linux: 34 | name: Linux Export 35 | runs-on: ubuntu-latest 36 | container: 37 | image: barichello/godot-ci:3.2.3 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v1 41 | - name: Setup 42 | run: | 43 | mkdir -v -p ~/.local/share/godot/templates 44 | mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable 45 | - name: Linux Build 46 | run: | 47 | mkdir -v -p build/linux 48 | cd $EXPORT_NAME 49 | godot -v --export "Linux/X11" ../build/linux/$EXPORT_NAME.x86_64 50 | rm ../build/linux/libgitapi.so 51 | - name: Upload Artifact 52 | uses: actions/upload-artifact@v1 53 | with: 54 | name: linux 55 | path: build/linux 56 | 57 | export-web: 58 | name: Web Export 59 | runs-on: ubuntu-latest 60 | container: 61 | image: barichello/godot-ci:3.2.3 62 | steps: 63 | - name: Checkout 64 | uses: actions/checkout@v1 65 | - name: Setup 66 | run: | 67 | mkdir -v -p ~/.local/share/godot/templates 68 | mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable 69 | - name: Web Build 70 | run: | 71 | mkdir -v -p build/web 72 | cd $EXPORT_NAME 73 | godot -v --export "HTML5" ../build/web/index.html 74 | - name: Upload Artifact 75 | uses: actions/upload-artifact@v1 76 | with: 77 | name: web 78 | path: build/web 79 | # Installing rsync is needed in order to deploy to GitHub Pages. Without it, the build will fail. 80 | - name: Install rsync 📚 81 | run: | 82 | apt-get update && apt-get install -y rsync 83 | - name: Deploy to GitHub Pages 🚀 84 | uses: JamesIves/github-pages-deploy-action@releases/v3 85 | with: 86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 87 | BRANCH: gh-pages # The branch the action should deploy to. 88 | FOLDER: build/web # The folder the action should deploy. 89 | 90 | export-mac: 91 | name: Mac Export 92 | runs-on: ubuntu-latest 93 | container: 94 | image: barichello/godot-ci:3.2.3 95 | steps: 96 | - name: Checkout 97 | uses: actions/checkout@v1 98 | - name: Setup 99 | run: | 100 | mkdir -v -p ~/.local/share/godot/templates 101 | mv /root/.local/share/godot/templates/${GODOT_VERSION}.stable ~/.local/share/godot/templates/${GODOT_VERSION}.stable 102 | - name: Mac Build 103 | run: | 104 | mkdir -v -p build/mac 105 | cd $EXPORT_NAME 106 | godot -v --export "Mac OSX" ../build/mac/$EXPORT_NAME.zip 107 | - name: Upload Artifact 108 | uses: actions/upload-artifact@v1 109 | with: 110 | name: mac 111 | path: build/mac 112 | -------------------------------------------------------------------------------- /godot-multi/game_world.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | # Spawns a new player actor, using the provided player_info structure and the given spawn index 4 | remote func spawn_players(pinfo, spawn_index): 5 | # If the spawn_index is -1 then we define it based on the size of the player list 6 | if (spawn_index == -1): 7 | spawn_index = network.players.size() 8 | 9 | if (get_tree().is_network_server() && pinfo.net_id != 1): 10 | # We are on the server and the requested spawn does not belong to the server 11 | # Iterate through the connected players 12 | var s_index = 1 # Will be used as spawn index 13 | for id in network.players: 14 | # Spawn currently iterated player within the new player's scene, skipping the new player for now 15 | if (id != pinfo.net_id): 16 | rpc_id(pinfo.net_id, "spawn_players", network.players[id], s_index) 17 | 18 | # Spawn the new player within the currently iterated player as long it's not the server 19 | # Because the server's list already contains the new player, that one will also get itself! 20 | if (id != 1): 21 | rpc_id(id, "spawn_players", pinfo, spawn_index) 22 | 23 | s_index += 1 24 | 25 | # Load the scene and create an instance 26 | var pclass = load(pinfo.actor_path) 27 | var nactor = pclass.instance() 28 | # Setup player customization (well, the color) 29 | nactor.set_dominant_color(pinfo.char_color) 30 | # And the actor position 31 | nactor.position = $SpawnPoints.get_node(str(spawn_index)).position 32 | # If this actor does not belong to the server, change the node name and network master accordingly 33 | if (pinfo.net_id != 1): 34 | nactor.set_network_master(pinfo.net_id) 35 | nactor.set_name(str(pinfo.net_id)) 36 | # Finally add the actor into the world 37 | add_child(nactor) 38 | 39 | remote func despawn_player(pinfo): 40 | if (get_tree().is_network_server()): 41 | for id in network.players: 42 | # Skip disconnecte player and server from replication code 43 | if (id == pinfo.net_id || id == 1): 44 | continue 45 | 46 | # Replicate despawn into currently iterated player 47 | rpc_id(id, "despawn_player", pinfo) 48 | 49 | # Try to locate the player actor 50 | var player_node = get_node(str(pinfo.net_id)) 51 | if (!player_node): 52 | print("Cannoot remove invalid node from tree") 53 | return 54 | 55 | # Mark the node for deletion 56 | player_node.queue_free() 57 | 58 | func _on_player_list_changed(): 59 | # First remove all children from the boxList widget 60 | for c in $HUD/PanelPlayerList/boxList.get_children(): 61 | c.queue_free() 62 | 63 | # Now iterate through the player list creating a new entry into the boxList 64 | for p in network.players: 65 | if (p != gamestate.player_info.net_id): 66 | var nlabel = Label.new() 67 | nlabel.text = network.players[p].name 68 | $HUD/PanelPlayerList/boxList.add_child(nlabel) 69 | 70 | func _on_player_removed(pinfo): 71 | despawn_player(pinfo) 72 | 73 | # Called when the node enters the scene tree for the first time. 74 | func _ready(): 75 | # Connect event handler to the player_list_changed signal 76 | network.connect("player_list_changed", self, "_on_player_list_changed") 77 | # If we are in the server, connect to the event that will deal with player despawning 78 | if (get_tree().is_network_server()): 79 | network.connect("player_removed", self, "_on_player_removed") 80 | 81 | # Update the lblLocalPlayer label widget to display the local player name 82 | $HUD/PanelPlayerList/lblLocalPlayer.text = gamestate.player_info.name 83 | 84 | # Spawn the players 85 | if (get_tree().is_network_server()): 86 | spawn_players(gamestate.player_info, 1) 87 | else: 88 | rpc_id(1, "spawn_players", gamestate.player_info, -1) 89 | 90 | print("MQtt testing") 91 | mqtt.server = "127.0.0.1" 92 | mqtt.port = 1883 93 | mqtt.user = "mqtt" 94 | mqtt.pswd = "decafbad00" 95 | 96 | mqtt.connect("received_message", self, "_on_received_message") 97 | 98 | mqtt.connect_to_server() 99 | mqtt.ping() 100 | mqtt.subscribe("topic1") 101 | mqtt.publish("topic1", "Hello World", false, 1) 102 | # mqtt.disconnect_from_server() 103 | 104 | func _on_received_message(topic, message): 105 | print("Got message {topic} = {msg}".format( { "topic" : topic , "msg" : message } )) 106 | -------------------------------------------------------------------------------- /godot-multi/network.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var server_info = { 4 | name = "Server", # Holds the name of the server 5 | max_players = 0, # Maximum allowed connections 6 | used_port = 0 # Listening port 7 | } 8 | 9 | var players = {} # List of all players 10 | 11 | signal server_created # when server is successfully created 12 | signal join_success # When the peer successfully joins a server 13 | signal join_fail # Failed to join a server 14 | signal player_list_changed # List of players has been changed 15 | signal player_removed(pinfo) # A player has been removed from the list 16 | 17 | func create_server(): 18 | # Initialize the networking system 19 | var net = NetworkedMultiplayerENet.new() 20 | 21 | # Try to create the server 22 | if (net.create_server(server_info.used_port, server_info.max_players) != OK): 23 | print("Failed to create server") 24 | return 25 | 26 | # Assign it into the tree 27 | get_tree().set_network_peer(net) 28 | 29 | # Tell the server has been created successfully 30 | emit_signal("server_created") 31 | 32 | # Register the server's player in the local player list 33 | register_player(gamestate.player_info) 34 | 35 | func join_server(ip, port): 36 | var net = NetworkedMultiplayerENet.new() 37 | 38 | if (net.create_client(ip, port) != OK): 39 | print("Failed to create client") 40 | emit_signal("join_fail") 41 | return 42 | 43 | get_tree().set_network_peer(net) 44 | 45 | # Everyone gets notified whenever a new client joins the server 46 | func _on_player_connected(id): 47 | pass 48 | 49 | 50 | # Everyone gets notified whenever someone disconnects from the server 51 | func _on_player_disconnected(id): 52 | print("Player ", players[id].name, " disconnected from server") 53 | # Update the player tables 54 | if (get_tree().is_network_server()): 55 | # Unregister the player from the server's list 56 | unregister_player(id) 57 | # Then on all remaining peers 58 | rpc("unregister_player", id) 59 | 60 | # Peer trying to connect to server is notified on success 61 | func _on_connected_to_server(): 62 | emit_signal("join_success") 63 | # Update the player_info dictionary with the obtained unique network ID 64 | gamestate.player_info.net_id = get_tree().get_network_unique_id() 65 | # Request the server to register this new player across all connected players 66 | rpc_id(1, "register_player", gamestate.player_info) 67 | # And register itself on the local list 68 | register_player(gamestate.player_info) 69 | 70 | # Peer trying to connect to server is notified on failure 71 | func _on_connection_failed(): 72 | emit_signal("join_fail") 73 | get_tree().set_network_peer(null) 74 | 75 | # Peer is notified when disconnected from server 76 | func _on_disconnected_from_server(): 77 | print("Disconnected from server") 78 | # Clear the internal player list 79 | players.clear() 80 | # Reset the player info network ID 81 | gamestate.player_info.net_id = 1 82 | 83 | remote func register_player(pinfo): 84 | if (get_tree().is_network_server()): 85 | # We are on the server, so distribute the player list information throughout the connected players 86 | for id in players: 87 | # Send currently iterated player info to the new player 88 | rpc_id(pinfo.net_id, "register_player", players[id]) 89 | # Send new player info to currently iterated player, skipping the server (which will get the info shortly) 90 | if (id != 1): 91 | rpc_id(id, "register_player", pinfo) 92 | 93 | # Now to code that will be executed regardless of being on client or server 94 | print("Registering player ", pinfo.name, " (", pinfo.net_id, ") to internal player table") 95 | players[pinfo.net_id] = pinfo # Create the player entry in the dictionary 96 | emit_signal("player_list_changed") # And notify that the player list has been changed 97 | 98 | remote func unregister_player(id): 99 | print("Removing player ", players[id].name, " from internal table") 100 | # Cache the player info because it's still necessary for some upkeeping 101 | var pinfo = players[id] 102 | # Remove the player from the list 103 | players.erase(id) 104 | # And notify the list has been changed 105 | emit_signal("player_list_changed") 106 | # Emit the signal that is meant to be intercepted only by the server 107 | emit_signal("player_removed", pinfo) 108 | 109 | func _ready(): 110 | get_tree().connect("network_peer_connected", self, "_on_player_connected") 111 | get_tree().connect("network_peer_disconnected", self, "_on_player_disconnected") 112 | get_tree().connect("connected_to_server", self, "_on_connected_to_server") 113 | get_tree().connect("connection_failed", self, "_on_connection_failed") 114 | get_tree().connect("server_disconnected", self, "_on_disconnected_from_server") 115 | -------------------------------------------------------------------------------- /godot-multi/main_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://main_menu.gd" type="Script" id=1] 4 | 5 | [sub_resource type="ImageTexture" id=1] 6 | 7 | [node name="CanvasLayer" type="CanvasLayer"] 8 | script = ExtResource( 1 ) 9 | 10 | [node name="PanelHost" type="Panel" parent="."] 11 | margin_left = 15.0 12 | margin_top = 217.0 13 | margin_right = 393.0 14 | margin_bottom = 379.0 15 | __meta__ = { 16 | "_edit_use_anchors_": false 17 | } 18 | 19 | [node name="Label" type="Label" parent="PanelHost"] 20 | margin_left = 99.0 21 | margin_top = 8.0 22 | margin_right = 184.0 23 | margin_bottom = 22.0 24 | text = "Create Server" 25 | __meta__ = { 26 | "_edit_use_anchors_": false 27 | } 28 | 29 | [node name="Label2" type="Label" parent="PanelHost"] 30 | margin_left = 24.0 31 | margin_top = 32.0 32 | margin_right = 110.0 33 | margin_bottom = 46.0 34 | text = "Server Name" 35 | __meta__ = { 36 | "_edit_use_anchors_": false 37 | } 38 | 39 | [node name="txtServerName" type="LineEdit" parent="PanelHost"] 40 | margin_left = 23.0 41 | margin_top = 49.0 42 | margin_right = 268.0 43 | margin_bottom = 73.0 44 | text = "My Server" 45 | __meta__ = { 46 | "_edit_use_anchors_": false 47 | } 48 | 49 | [node name="Label3" type="Label" parent="PanelHost"] 50 | margin_left = 27.0 51 | margin_top = 78.0 52 | margin_right = 67.0 53 | margin_bottom = 92.0 54 | text = "Port" 55 | __meta__ = { 56 | "_edit_use_anchors_": false 57 | } 58 | 59 | [node name="txtServerPort" type="LineEdit" parent="PanelHost"] 60 | margin_left = 24.0 61 | margin_top = 96.0 62 | margin_right = 82.0 63 | margin_bottom = 120.0 64 | text = "4546" 65 | __meta__ = { 66 | "_edit_use_anchors_": false 67 | } 68 | 69 | [node name="Label4" type="Label" parent="PanelHost"] 70 | margin_left = 107.0 71 | margin_top = 79.0 72 | margin_right = 216.0 73 | margin_bottom = 93.0 74 | text = "Max Connections" 75 | __meta__ = { 76 | "_edit_use_anchors_": false 77 | } 78 | 79 | [node name="txtMaxPlayers" type="SpinBox" parent="PanelHost"] 80 | margin_left = 104.0 81 | margin_top = 96.0 82 | margin_right = 162.0 83 | margin_bottom = 120.0 84 | min_value = 2.0 85 | max_value = 16.0 86 | value = 6.0 87 | __meta__ = { 88 | "_edit_use_anchors_": false 89 | } 90 | 91 | [node name="btCreate" type="Button" parent="PanelHost"] 92 | margin_left = 311.0 93 | margin_top = 129.0 94 | margin_right = 364.0 95 | margin_bottom = 149.0 96 | text = "Create" 97 | __meta__ = { 98 | "_edit_use_anchors_": false 99 | } 100 | 101 | [node name="PanelJoin" type="Panel" parent="."] 102 | margin_left = 14.0 103 | margin_top = 384.0 104 | margin_right = 391.0 105 | margin_bottom = 507.0 106 | __meta__ = { 107 | "_edit_use_anchors_": false 108 | } 109 | 110 | [node name="Label" type="Label" parent="PanelJoin"] 111 | margin_left = 155.0 112 | margin_top = 6.0 113 | margin_right = 222.0 114 | margin_bottom = 20.0 115 | text = "Join Server" 116 | __meta__ = { 117 | "_edit_use_anchors_": false 118 | } 119 | 120 | [node name="Label2" type="Label" parent="PanelJoin"] 121 | margin_left = 25.0 122 | margin_top = 31.0 123 | margin_right = 65.0 124 | margin_bottom = 45.0 125 | text = "IP" 126 | __meta__ = { 127 | "_edit_use_anchors_": false 128 | } 129 | 130 | [node name="txtJoinIP" type="LineEdit" parent="PanelJoin"] 131 | margin_left = 21.0 132 | margin_top = 47.0 133 | margin_right = 183.0 134 | margin_bottom = 71.0 135 | text = "127.0.0.1" 136 | __meta__ = { 137 | "_edit_use_anchors_": false 138 | } 139 | 140 | [node name="Label3" type="Label" parent="PanelJoin"] 141 | margin_left = 205.0 142 | margin_top = 30.0 143 | margin_right = 245.0 144 | margin_bottom = 44.0 145 | text = "Port" 146 | __meta__ = { 147 | "_edit_use_anchors_": false 148 | } 149 | 150 | [node name="txtJoinPort" type="LineEdit" parent="PanelJoin"] 151 | margin_left = 202.0 152 | margin_top = 47.0 153 | margin_right = 260.0 154 | margin_bottom = 71.0 155 | text = "4546" 156 | selecting_enabled = false 157 | __meta__ = { 158 | "_edit_use_anchors_": false 159 | } 160 | 161 | [node name="btJoin" type="Button" parent="PanelJoin"] 162 | margin_left = 310.0 163 | margin_top = 89.0 164 | margin_right = 359.0 165 | margin_bottom = 109.0 166 | text = "Join" 167 | __meta__ = { 168 | "_edit_use_anchors_": false 169 | } 170 | 171 | [node name="PanelPlayer" type="Panel" parent="."] 172 | margin_left = 14.0 173 | margin_top = 8.0 174 | margin_right = 394.0 175 | margin_bottom = 211.0 176 | __meta__ = { 177 | "_edit_use_anchors_": false 178 | } 179 | 180 | [node name="Label" type="Label" parent="PanelPlayer"] 181 | margin_left = 23.0 182 | margin_top = 64.0 183 | margin_right = 138.0 184 | margin_bottom = 83.0 185 | text = "Player Name" 186 | __meta__ = { 187 | "_edit_use_anchors_": false 188 | } 189 | 190 | [node name="Label2" type="Label" parent="PanelPlayer"] 191 | margin_left = 173.0 192 | margin_top = 126.0 193 | margin_right = 213.0 194 | margin_bottom = 140.0 195 | text = "Color" 196 | __meta__ = { 197 | "_edit_use_anchors_": false 198 | } 199 | 200 | [node name="txtPlayerName" type="LineEdit" parent="PanelPlayer"] 201 | margin_left = 23.0 202 | margin_top = 91.0 203 | margin_right = 258.0 204 | margin_bottom = 115.0 205 | text = "Player" 206 | __meta__ = { 207 | "_edit_use_anchors_": false 208 | } 209 | 210 | [node name="PlayerIcon" type="Sprite" parent="PanelPlayer"] 211 | position = Vector2( 23, 94 ) 212 | scale = Vector2( 70, 61 ) 213 | texture = SubResource( 1 ) 214 | 215 | [node name="btColor" type="ColorPickerButton" parent="PanelPlayer"] 216 | margin_left = 153.0 217 | margin_top = 150.0 218 | margin_right = 228.0 219 | margin_bottom = 170.0 220 | color = Color( 1, 1, 1, 1 ) 221 | __meta__ = { 222 | "_edit_use_anchors_": false 223 | } 224 | 225 | [node name="btDefaultColor" type="Button" parent="PanelPlayer"] 226 | margin_left = 153.0 227 | margin_top = 172.0 228 | margin_right = 228.0 229 | margin_bottom = 192.0 230 | text = "Default" 231 | __meta__ = { 232 | "_edit_use_anchors_": false 233 | } 234 | 235 | [node name="Label3" type="Label" parent="PanelPlayer"] 236 | margin_left = 106.0 237 | margin_top = 15.0 238 | margin_right = 279.0 239 | margin_bottom = 40.0 240 | text = "Multiplayer Game Testing" 241 | __meta__ = { 242 | "_edit_use_anchors_": false 243 | } 244 | [connection signal="pressed" from="PanelHost/btCreate" to="." method="_on_btCreate_pressed"] 245 | [connection signal="pressed" from="PanelJoin/btJoin" to="." method="_on_btJoin_pressed"] 246 | -------------------------------------------------------------------------------- /godot-multi/addons/mqtt/mqtt.gd: -------------------------------------------------------------------------------- 1 | tool 2 | #extends EditorPlugin 3 | 4 | # Copyright (c) 2019, Pycom Limited. 5 | # Some parts copyright (c) 2020, Dynamic Devices Ltd 6 | 7 | # This software is licensed under the GNU GPL version 3 or any 8 | # later version, with permitted additional terms. For more information 9 | # see the Pycom Licence v1.0 document supplied with this file, or 10 | # available at https://www.pycom.io/opensource/licensing 11 | # 12 | # 13 | # Ported to gdscript by Alex J Lennon 14 | # 15 | # - ssl not implemented yet 16 | # - lwt not tested 17 | # - qos 1,2 may not work 18 | # 19 | # Code should be considered ALPHA 20 | 21 | extends Node 22 | 23 | var server = "127.0.0.1" 24 | var port = 1883 25 | var client_id = "client_id" 26 | var client = null 27 | var ssl = false 28 | var ssl_params = null 29 | var pid = 0 30 | var user = null 31 | var pswd = null 32 | var keepalive = 0 33 | var lw_topic = null 34 | var lw_msg = null 35 | var lw_qos = 0 36 | var lw_retain = false 37 | 38 | var _timer = null 39 | 40 | signal received_message(topic, message) 41 | 42 | #func _init(client_id, server, port=0, user=null, password=null, keepalive=0,ssl=false, ssl_params={}): 43 | # self.server = server 44 | # if port == 0: 45 | # port = 8883 if ssl else 1883 46 | # self.port = port 47 | # self.client_id = client_id 48 | # self.client = null 49 | # self.ssl = ssl 50 | # self.ssl_params = ssl_params 51 | # self.pid = 0 52 | # self.user = user 53 | # self.pswd = password 54 | # self.keepalive = keepalive 55 | # self.lw_topic = null 56 | # self.lw_msg = null 57 | # self.lw_qos = 0 58 | # self.lw_retain = false 59 | 60 | func _recv_len(): 61 | var n = 0 62 | var sh = 0 63 | var b 64 | while 1: 65 | b = self.client.get_u8() # Is this right ? 66 | n |= (b & 0x7f) << sh 67 | if not b & 0x80: 68 | return n 69 | sh += 7 70 | 71 | func set_last_will(topic, msg, retain=false, qos=0): 72 | assert(0 <= qos <= 2) 73 | assert(topic) 74 | self.lw_topic = topic 75 | self.lw_msg = msg 76 | self.lw_qos = qos 77 | self.lw_retain = retain 78 | 79 | func connect_to_server(clean_session=true): 80 | 81 | self.client = StreamPeerTCP.new() 82 | self.client.set_no_delay(true) 83 | self.client.set_big_endian(true) 84 | self.client.connect_to_host(self.server, self.port) 85 | # if self.ssl: 86 | # import ussl 87 | # self.sock = ussl.wrap_socket(self.sock, **self.ssl_params) 88 | 89 | # I don't think this test works... 90 | while not self.client.is_connected_to_host(): 91 | pass 92 | # todo: Add in a timeout 93 | while self.client.get_status() != StreamPeerTCP.STATUS_CONNECTED: 94 | pass 95 | print("Connected to server") 96 | 97 | # May need a little delay after connecting to the server ? 98 | 99 | var msg = PoolByteArray() 100 | # Must be an easier way of doing this... 101 | msg.append(0x10); 102 | msg.append(0x00); 103 | msg.append(0x00); 104 | msg.append(0x04); 105 | msg.append_array("MQTT".to_ascii()); 106 | msg.append(0x04); 107 | msg.append(0x02); 108 | msg.append(0x00); 109 | msg.append(0x00); 110 | 111 | msg[1] = 10 + 2 + len(self.client_id) 112 | msg[9] = (1<<1) if clean_session else 0 113 | if self.user != null: 114 | msg[1] += 2 + len(self.user) + 2 + len(self.pswd) 115 | msg[9] |= 0xC0 116 | if self.keepalive: 117 | assert(self.keepalive < 65536) 118 | msg[10] |= self.keepalive >> 8 119 | msg[11] |= self.keepalive & 0x00FF 120 | if self.lw_topic: 121 | msg[1] += 2 + len(self.lw_topic) + 2 + len(self.lw_msg) 122 | msg[9] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3 123 | msg[9] |= 1<<5 if self.lw_retain else 0 124 | 125 | msg.append(self.client_id.length() >> 8) 126 | msg.append(self.client_id.length() & 0xFF) 127 | msg.append_array(self.client_id.to_ascii()) 128 | if self.lw_topic: 129 | msg.append_array(self.lw_topic.to_ascii()) 130 | msg.append_array(self.lw_msg.to_ascii()) 131 | if self.user != null: 132 | msg.append(self.user.length() >> 8) 133 | msg.append(self.user.length() & 0xFF) 134 | msg.append_array(self.user.to_ascii()) 135 | msg.append(self.pswd.length() >> 8) 136 | msg.append(self.pswd.length() & 0xFF) 137 | msg.append_array(self.pswd.to_ascii()) 138 | self.client.put_data(msg) 139 | 140 | var ret = self.client.get_data(4) 141 | var error = ret[0] 142 | assert(error == 0) 143 | var data = ret[1] 144 | assert(data[0] == 0x20 and data[1] == 0x02) 145 | if data[3] != 0: 146 | push_error(data[3]) 147 | 148 | # Setup timer to check for incoming messages 149 | _timer = Timer.new() 150 | add_child(_timer) 151 | 152 | _timer.connect("timeout", self, "_on_Timer_timeout") 153 | _timer.set_wait_time(1.0) 154 | _timer.set_one_shot(false) # Make sure it loops 155 | _timer.start() 156 | 157 | return data[2] & 1 158 | 159 | func disconnect_from_server(): 160 | self.client.put_u16(0xE000) 161 | self.client.disconnect_from_host() 162 | _timer.stop() 163 | 164 | func ping(): 165 | self.client.put_u16(0xC000) 166 | 167 | func publish(topic, msg, retain=false, qos=0): 168 | 169 | var pkt = PoolByteArray() 170 | # Must be an easier way of doing this... 171 | pkt.append(0x30); 172 | pkt.append(0x00); 173 | 174 | pkt[0] |= ((1<<1) if qos else 0) | (1 if retain else 0) 175 | var sz = 2 + len(topic) + len(msg) 176 | if qos > 0: 177 | sz += 2 178 | assert(sz < 2097152) 179 | var i = 1 180 | while sz > 0x7f: 181 | pkt[i] = (sz & 0x7f) | 0x80 182 | sz >>= 7 183 | i += 1 184 | pkt[i] = sz 185 | 186 | pkt.append(topic.length() >> 8) 187 | pkt.append(topic.length() & 0xFF) 188 | pkt.append_array(topic.to_ascii()) 189 | 190 | if qos > 0: 191 | self.pid += 1 192 | pkt.append(self.pid >> 8) 193 | pkt.append(self.pid & 0xFF) 194 | 195 | pkt.append_array(msg.to_ascii()) 196 | 197 | self.client.put_data(pkt) 198 | 199 | if qos == 1: 200 | while 1: 201 | var op = self.wait_msg() 202 | if op == 0x40: 203 | sz = self.client.get_u8() 204 | assert(sz == 0x02) 205 | var rcv_pid = self.client.get_u16() 206 | if self.pid == rcv_pid: 207 | return 208 | elif qos == 2: 209 | assert(0) 210 | 211 | func subscribe(topic, qos=0): 212 | self.pid += 1 213 | 214 | var msg = PoolByteArray() 215 | # Must be an easier way of doing this... 216 | msg.append(0x82); 217 | var length = 2 + 2 + topic.length() + 1 218 | msg.append(length) 219 | msg.append(self.pid >> 8) 220 | msg.append(self.pid & 0xFF) 221 | msg.append(topic.length() >> 8) 222 | msg.append(topic.length() & 0xFF) 223 | msg.append_array(topic.to_ascii()) 224 | msg.append(qos); 225 | 226 | self.client.put_data(msg) 227 | 228 | while 1: 229 | var op = self.wait_msg() 230 | if op == 0x90: 231 | var ret = self.client.get_data(4) 232 | var error = ret[0] 233 | assert(error == 0) 234 | var data = ret[1] 235 | assert(data[1] == (self.pid >> 8) and data[2] == (self.pid & 0x0F)) 236 | if data[3] == 0x80: 237 | push_error(data[3]) 238 | return 239 | 240 | # Wait for a single incoming MQTT message and process it. 241 | # Subscribed messages are delivered to a callback previously 242 | # set by .set_callback() method. Other (internal) MQTT 243 | # messages processed internally. 244 | func wait_msg(): 245 | 246 | if(self.client == null): 247 | return 248 | 249 | if(!self.client.is_connected_to_host()): 250 | return 251 | 252 | if(self.client.get_available_bytes() <= 0): 253 | return 254 | 255 | var res = self.client.get_u8() 256 | # self.sock.setblocking(True) 257 | if res == null: 258 | return null 259 | if res == 0: 260 | push_error("-1") 261 | if res == 0xD0: # PINGRESP 262 | var sz = self.client.get_u8() 263 | assert(sz == 0) 264 | return null 265 | var op = res 266 | if op & 0xf0 != 0x30: 267 | return op 268 | var sz = _recv_len() 269 | var topic_len = self.client.get_u16() 270 | var ret = self.client.get_data(topic_len) 271 | var error = ret[0] 272 | assert(error == 0) 273 | var topic = ret[1].get_string_from_ascii() 274 | sz -= topic_len + 2 275 | if op & 6: 276 | var pid = self.sock.get_u16() 277 | sz -= 2 278 | ret = self.client.get_data(sz) 279 | error = ret[0] 280 | assert(error == 0) 281 | # warn: May not want to convert payload as ascii 282 | var msg = ret[1].get_string_from_ascii() 283 | 284 | emit_signal("received_message", topic, msg) 285 | 286 | # self.cb(topic, msg) 287 | if op & 6 == 2: 288 | var pkt = PoolByteArray() 289 | # Must be an easier way of doing this... 290 | pkt.append(0x40); 291 | pkt.append(0x02); 292 | pkt.append(0x00); 293 | pkt.append(0x00); 294 | # struct.pack_into("!H", pkt, 2, pid) 295 | # self.sock.write(pkt) 296 | elif op & 6 == 4: 297 | assert(0) 298 | 299 | # Checks whether a pending message from server is available. 300 | # If not, returns immediately with None. Otherwise, does 301 | # the same processing as wait_msg. 302 | func check_msg(): 303 | # self.sock.setblocking(false) 304 | return self.wait_msg() 305 | 306 | func _on_Timer_timeout(): 307 | check_msg() 308 | -------------------------------------------------------------------------------- /godot-multi/export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="Windows Desktop" 4 | platform="Windows Desktop" 5 | runnable=true 6 | custom_features="" 7 | export_filter="all_resources" 8 | include_filter="" 9 | exclude_filter="" 10 | export_path="" 11 | patch_list=PoolStringArray( ) 12 | 13 | [preset.0.options] 14 | 15 | texture_format/bptc=true 16 | texture_format/s3tc=true 17 | texture_format/etc=true 18 | texture_format/etc2=true 19 | texture_format/no_bptc_fallbacks=true 20 | binary_format/64_bits=true 21 | custom_template/release="" 22 | custom_template/debug="" 23 | application/icon="" 24 | application/file_version="" 25 | application/product_version="" 26 | application/company_name="" 27 | application/product_name="" 28 | application/file_description="" 29 | application/copyright="" 30 | application/trademarks="" 31 | 32 | [preset.1] 33 | 34 | name="Mac OSX" 35 | platform="Mac OSX" 36 | runnable=true 37 | custom_features="" 38 | export_filter="all_resources" 39 | include_filter="" 40 | exclude_filter="" 41 | export_path="" 42 | patch_list=PoolStringArray( ) 43 | 44 | [preset.1.options] 45 | 46 | custom_package/debug="" 47 | custom_package/release="" 48 | application/name="" 49 | application/info="Made with Godot Engine" 50 | application/icon="" 51 | application/identifier="" 52 | application/signature="" 53 | application/short_version="1.0" 54 | application/version="1.0" 55 | application/copyright="" 56 | display/high_res=true 57 | texture_format/s3tc=true 58 | texture_format/etc=true 59 | texture_format/etc2=true 60 | 61 | [preset.2] 62 | 63 | name="Linux/X11" 64 | platform="Linux/X11" 65 | runnable=true 66 | custom_features="" 67 | export_filter="all_resources" 68 | include_filter="" 69 | exclude_filter="" 70 | export_path="" 71 | patch_list=PoolStringArray( ) 72 | 73 | [preset.2.options] 74 | 75 | texture_format/bptc=true 76 | texture_format/s3tc=true 77 | texture_format/etc=true 78 | texture_format/etc2=true 79 | texture_format/no_bptc_fallbacks=true 80 | binary_format/64_bits=true 81 | custom_template/release="" 82 | custom_template/debug="" 83 | 84 | [preset.3] 85 | 86 | name="HTML5" 87 | platform="HTML5" 88 | runnable=true 89 | custom_features="" 90 | export_filter="all_resources" 91 | include_filter="" 92 | exclude_filter="" 93 | export_path="" 94 | patch_list=PoolStringArray( ) 95 | 96 | [preset.3.options] 97 | 98 | texture_format/s3tc=true 99 | texture_format/etc=true 100 | texture_format/etc2=true 101 | html/custom_html_shell="" 102 | html/head_include="" 103 | custom_template/release="" 104 | custom_template/debug="" 105 | 106 | [preset.4] 107 | 108 | name="Android Debug" 109 | platform="Android" 110 | runnable=true 111 | custom_features="" 112 | export_filter="all_resources" 113 | include_filter="" 114 | exclude_filter="" 115 | patch_list=PoolStringArray( ) 116 | script_export_mode=1 117 | script_encryption_key="" 118 | 119 | [preset.4.options] 120 | 121 | graphics/32_bits_framebuffer=true 122 | xr_features/xr_mode=0 123 | xr_features/degrees_of_freedom=0 124 | xr_features/hand_tracking=0 125 | xr_features/focus_awareness=false 126 | one_click_deploy/clear_previous_install=false 127 | custom_template/debug="" 128 | custom_template/release="" 129 | custom_template/use_custom_build=false 130 | command_line/extra_args="" 131 | version/code=1 132 | version/name="1.0" 133 | package/unique_name="org.godotengine.$genname" 134 | package/name="" 135 | package/signed=true 136 | screen/immersive_mode=true 137 | screen/orientation=0 138 | screen/support_small=true 139 | screen/support_normal=true 140 | screen/support_large=true 141 | screen/support_xlarge=true 142 | screen/opengl_debug=false 143 | launcher_icons/main_192x192="" 144 | launcher_icons/adaptive_foreground_432x432="" 145 | launcher_icons/adaptive_background_432x432="" 146 | keystore/debug="" 147 | keystore/debug_user="" 148 | keystore/debug_password="" 149 | keystore/release="" 150 | keystore/release_user="" 151 | keystore/release_password="" 152 | apk_expansion/enable=false 153 | apk_expansion/SALT="" 154 | apk_expansion/public_key="" 155 | architectures/armeabi-v7a=true 156 | architectures/arm64-v8a=true 157 | architectures/x86=false 158 | architectures/x86_64=false 159 | permissions/custom_permissions=PoolStringArray( ) 160 | permissions/access_checkin_properties=false 161 | permissions/access_coarse_location=false 162 | permissions/access_fine_location=false 163 | permissions/access_location_extra_commands=false 164 | permissions/access_mock_location=false 165 | permissions/access_network_state=false 166 | permissions/access_surface_flinger=false 167 | permissions/access_wifi_state=false 168 | permissions/account_manager=false 169 | permissions/add_voicemail=false 170 | permissions/authenticate_accounts=false 171 | permissions/battery_stats=false 172 | permissions/bind_accessibility_service=false 173 | permissions/bind_appwidget=false 174 | permissions/bind_device_admin=false 175 | permissions/bind_input_method=false 176 | permissions/bind_nfc_service=false 177 | permissions/bind_notification_listener_service=false 178 | permissions/bind_print_service=false 179 | permissions/bind_remoteviews=false 180 | permissions/bind_text_service=false 181 | permissions/bind_vpn_service=false 182 | permissions/bind_wallpaper=false 183 | permissions/bluetooth=false 184 | permissions/bluetooth_admin=false 185 | permissions/bluetooth_privileged=false 186 | permissions/brick=false 187 | permissions/broadcast_package_removed=false 188 | permissions/broadcast_sms=false 189 | permissions/broadcast_sticky=false 190 | permissions/broadcast_wap_push=false 191 | permissions/call_phone=false 192 | permissions/call_privileged=false 193 | permissions/camera=false 194 | permissions/capture_audio_output=false 195 | permissions/capture_secure_video_output=false 196 | permissions/capture_video_output=false 197 | permissions/change_component_enabled_state=false 198 | permissions/change_configuration=false 199 | permissions/change_network_state=false 200 | permissions/change_wifi_multicast_state=false 201 | permissions/change_wifi_state=false 202 | permissions/clear_app_cache=false 203 | permissions/clear_app_user_data=false 204 | permissions/control_location_updates=false 205 | permissions/delete_cache_files=false 206 | permissions/delete_packages=false 207 | permissions/device_power=false 208 | permissions/diagnostic=false 209 | permissions/disable_keyguard=false 210 | permissions/dump=false 211 | permissions/expand_status_bar=false 212 | permissions/factory_test=false 213 | permissions/flashlight=false 214 | permissions/force_back=false 215 | permissions/get_accounts=false 216 | permissions/get_package_size=false 217 | permissions/get_tasks=false 218 | permissions/get_top_activity_info=false 219 | permissions/global_search=false 220 | permissions/hardware_test=false 221 | permissions/inject_events=false 222 | permissions/install_location_provider=false 223 | permissions/install_packages=false 224 | permissions/install_shortcut=false 225 | permissions/internal_system_window=false 226 | permissions/internet=false 227 | permissions/kill_background_processes=false 228 | permissions/location_hardware=false 229 | permissions/manage_accounts=false 230 | permissions/manage_app_tokens=false 231 | permissions/manage_documents=false 232 | permissions/master_clear=false 233 | permissions/media_content_control=false 234 | permissions/modify_audio_settings=false 235 | permissions/modify_phone_state=false 236 | permissions/mount_format_filesystems=false 237 | permissions/mount_unmount_filesystems=false 238 | permissions/nfc=false 239 | permissions/persistent_activity=false 240 | permissions/process_outgoing_calls=false 241 | permissions/read_calendar=false 242 | permissions/read_call_log=false 243 | permissions/read_contacts=false 244 | permissions/read_external_storage=false 245 | permissions/read_frame_buffer=false 246 | permissions/read_history_bookmarks=false 247 | permissions/read_input_state=false 248 | permissions/read_logs=false 249 | permissions/read_phone_state=false 250 | permissions/read_profile=false 251 | permissions/read_sms=false 252 | permissions/read_social_stream=false 253 | permissions/read_sync_settings=false 254 | permissions/read_sync_stats=false 255 | permissions/read_user_dictionary=false 256 | permissions/reboot=false 257 | permissions/receive_boot_completed=false 258 | permissions/receive_mms=false 259 | permissions/receive_sms=false 260 | permissions/receive_wap_push=false 261 | permissions/record_audio=false 262 | permissions/reorder_tasks=false 263 | permissions/restart_packages=false 264 | permissions/send_respond_via_message=false 265 | permissions/send_sms=false 266 | permissions/set_activity_watcher=false 267 | permissions/set_alarm=false 268 | permissions/set_always_finish=false 269 | permissions/set_animation_scale=false 270 | permissions/set_debug_app=false 271 | permissions/set_orientation=false 272 | permissions/set_pointer_speed=false 273 | permissions/set_preferred_applications=false 274 | permissions/set_process_limit=false 275 | permissions/set_time=false 276 | permissions/set_time_zone=false 277 | permissions/set_wallpaper=false 278 | permissions/set_wallpaper_hints=false 279 | permissions/signal_persistent_processes=false 280 | permissions/status_bar=false 281 | permissions/subscribed_feeds_read=false 282 | permissions/subscribed_feeds_write=false 283 | permissions/system_alert_window=false 284 | permissions/transmit_ir=false 285 | permissions/uninstall_shortcut=false 286 | permissions/update_device_stats=false 287 | permissions/use_credentials=false 288 | permissions/use_sip=false 289 | permissions/vibrate=false 290 | permissions/wake_lock=false 291 | permissions/write_apn_settings=false 292 | permissions/write_calendar=false 293 | permissions/write_call_log=false 294 | permissions/write_contacts=false 295 | permissions/write_external_storage=false 296 | permissions/write_gservices=false 297 | permissions/write_history_bookmarks=false 298 | permissions/write_profile=false 299 | permissions/write_secure_settings=false 300 | permissions/write_settings=false 301 | permissions/write_sms=false 302 | permissions/write_social_stream=false 303 | permissions/write_sync_settings=false 304 | permissions/write_user_dictionary=false 305 | 306 | [preset.5] 307 | 308 | name="Android" 309 | platform="Android" 310 | runnable=true 311 | custom_features="" 312 | export_filter="all_resources" 313 | include_filter="" 314 | exclude_filter="" 315 | patch_list=PoolStringArray( ) 316 | script_export_mode=1 317 | script_encryption_key="" 318 | 319 | [preset.5.options] 320 | 321 | graphics/32_bits_framebuffer=true 322 | xr_features/xr_mode=0 323 | xr_features/degrees_of_freedom=0 324 | xr_features/hand_tracking=0 325 | xr_features/focus_awareness=false 326 | one_click_deploy/clear_previous_install=false 327 | custom_template/debug="" 328 | custom_template/release="" 329 | custom_template/use_custom_build=false 330 | command_line/extra_args="" 331 | version/code=1 332 | version/name="1.0" 333 | package/unique_name="org.godotengine.$genname" 334 | package/name="" 335 | package/signed=true 336 | screen/immersive_mode=true 337 | screen/orientation=0 338 | screen/support_small=true 339 | screen/support_normal=true 340 | screen/support_large=true 341 | screen/support_xlarge=true 342 | screen/opengl_debug=false 343 | launcher_icons/main_192x192="" 344 | launcher_icons/adaptive_foreground_432x432="" 345 | launcher_icons/adaptive_background_432x432="" 346 | keystore/debug="" 347 | keystore/debug_user="" 348 | keystore/debug_password="" 349 | keystore/release="" 350 | keystore/release_user="" 351 | keystore/release_password="" 352 | apk_expansion/enable=false 353 | apk_expansion/SALT="" 354 | apk_expansion/public_key="" 355 | architectures/armeabi-v7a=true 356 | architectures/arm64-v8a=true 357 | architectures/x86=false 358 | architectures/x86_64=false 359 | permissions/custom_permissions=PoolStringArray( ) 360 | permissions/access_checkin_properties=false 361 | permissions/access_coarse_location=false 362 | permissions/access_fine_location=false 363 | permissions/access_location_extra_commands=false 364 | permissions/access_mock_location=false 365 | permissions/access_network_state=false 366 | permissions/access_surface_flinger=false 367 | permissions/access_wifi_state=false 368 | permissions/account_manager=false 369 | permissions/add_voicemail=false 370 | permissions/authenticate_accounts=false 371 | permissions/battery_stats=false 372 | permissions/bind_accessibility_service=false 373 | permissions/bind_appwidget=false 374 | permissions/bind_device_admin=false 375 | permissions/bind_input_method=false 376 | permissions/bind_nfc_service=false 377 | permissions/bind_notification_listener_service=false 378 | permissions/bind_print_service=false 379 | permissions/bind_remoteviews=false 380 | permissions/bind_text_service=false 381 | permissions/bind_vpn_service=false 382 | permissions/bind_wallpaper=false 383 | permissions/bluetooth=false 384 | permissions/bluetooth_admin=false 385 | permissions/bluetooth_privileged=false 386 | permissions/brick=false 387 | permissions/broadcast_package_removed=false 388 | permissions/broadcast_sms=false 389 | permissions/broadcast_sticky=false 390 | permissions/broadcast_wap_push=false 391 | permissions/call_phone=false 392 | permissions/call_privileged=false 393 | permissions/camera=false 394 | permissions/capture_audio_output=false 395 | permissions/capture_secure_video_output=false 396 | permissions/capture_video_output=false 397 | permissions/change_component_enabled_state=false 398 | permissions/change_configuration=false 399 | permissions/change_network_state=false 400 | permissions/change_wifi_multicast_state=false 401 | permissions/change_wifi_state=false 402 | permissions/clear_app_cache=false 403 | permissions/clear_app_user_data=false 404 | permissions/control_location_updates=false 405 | permissions/delete_cache_files=false 406 | permissions/delete_packages=false 407 | permissions/device_power=false 408 | permissions/diagnostic=false 409 | permissions/disable_keyguard=false 410 | permissions/dump=false 411 | permissions/expand_status_bar=false 412 | permissions/factory_test=false 413 | permissions/flashlight=false 414 | permissions/force_back=false 415 | permissions/get_accounts=false 416 | permissions/get_package_size=false 417 | permissions/get_tasks=false 418 | permissions/get_top_activity_info=false 419 | permissions/global_search=false 420 | permissions/hardware_test=false 421 | permissions/inject_events=false 422 | permissions/install_location_provider=false 423 | permissions/install_packages=false 424 | permissions/install_shortcut=false 425 | permissions/internal_system_window=false 426 | permissions/internet=false 427 | permissions/kill_background_processes=false 428 | permissions/location_hardware=false 429 | permissions/manage_accounts=false 430 | permissions/manage_app_tokens=false 431 | permissions/manage_documents=false 432 | permissions/master_clear=false 433 | permissions/media_content_control=false 434 | permissions/modify_audio_settings=false 435 | permissions/modify_phone_state=false 436 | permissions/mount_format_filesystems=false 437 | permissions/mount_unmount_filesystems=false 438 | permissions/nfc=false 439 | permissions/persistent_activity=false 440 | permissions/process_outgoing_calls=false 441 | permissions/read_calendar=false 442 | permissions/read_call_log=false 443 | permissions/read_contacts=false 444 | permissions/read_external_storage=false 445 | permissions/read_frame_buffer=false 446 | permissions/read_history_bookmarks=false 447 | permissions/read_input_state=false 448 | permissions/read_logs=false 449 | permissions/read_phone_state=false 450 | permissions/read_profile=false 451 | permissions/read_sms=false 452 | permissions/read_social_stream=false 453 | permissions/read_sync_settings=false 454 | permissions/read_sync_stats=false 455 | permissions/read_user_dictionary=false 456 | permissions/reboot=false 457 | permissions/receive_boot_completed=false 458 | permissions/receive_mms=false 459 | permissions/receive_sms=false 460 | permissions/receive_wap_push=false 461 | permissions/record_audio=false 462 | permissions/reorder_tasks=false 463 | permissions/restart_packages=false 464 | permissions/send_respond_via_message=false 465 | permissions/send_sms=false 466 | permissions/set_activity_watcher=false 467 | permissions/set_alarm=false 468 | permissions/set_always_finish=false 469 | permissions/set_animation_scale=false 470 | permissions/set_debug_app=false 471 | permissions/set_orientation=false 472 | permissions/set_pointer_speed=false 473 | permissions/set_preferred_applications=false 474 | permissions/set_process_limit=false 475 | permissions/set_time=false 476 | permissions/set_time_zone=false 477 | permissions/set_wallpaper=false 478 | permissions/set_wallpaper_hints=false 479 | permissions/signal_persistent_processes=false 480 | permissions/status_bar=false 481 | permissions/subscribed_feeds_read=false 482 | permissions/subscribed_feeds_write=false 483 | permissions/system_alert_window=false 484 | permissions/transmit_ir=false 485 | permissions/uninstall_shortcut=false 486 | permissions/update_device_stats=false 487 | permissions/use_credentials=false 488 | permissions/use_sip=false 489 | permissions/vibrate=false 490 | permissions/wake_lock=false 491 | permissions/write_apn_settings=false 492 | permissions/write_calendar=false 493 | permissions/write_call_log=false 494 | permissions/write_contacts=false 495 | permissions/write_external_storage=false 496 | permissions/write_gservices=false 497 | permissions/write_history_bookmarks=false 498 | permissions/write_profile=false 499 | permissions/write_secure_settings=false 500 | permissions/write_settings=false 501 | permissions/write_sms=false 502 | permissions/write_social_stream=false 503 | permissions/write_sync_settings=false 504 | permissions/write_user_dictionary=false 505 | 506 | # [preset.6] 507 | # 508 | # name="Linux/X11" 509 | # platform="Linux/X11" 510 | # runnable=true 511 | # custom_features="" 512 | # export_filter="all_resources" 513 | # include_filter="" 514 | # exclude_filter="" 515 | # export_path="exports/Lobby Server.x86_64" 516 | # patch_list=PoolStringArray( ) 517 | # script_export_mode=1 518 | # script_encryption_key="" 519 | # 520 | # [preset.6.options] 521 | # 522 | # texture_format/bptc=false 523 | # texture_format/s3tc=true 524 | # texture_format/etc=false 525 | # texture_format/etc2=false 526 | # texture_format/no_bptc_fallbacks=true 527 | # binary_format/64_bits=true 528 | # binary_format/embed_pck=false 529 | # custom_template/release="" 530 | # custom_template/debug="" 531 | --------------------------------------------------------------------------------