diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..b370ceb --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ + diff --git a/icon.svg.import b/icon.svg.import new file mode 100644 index 0000000..4b0198f --- /dev/null +++ b/icon.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dlbbl7reybbvh" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/icons/mic_active.svg b/icons/mic_active.svg new file mode 100644 index 0000000..aeffaf3 --- /dev/null +++ b/icons/mic_active.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mic_active.svg.import b/icons/mic_active.svg.import new file mode 100644 index 0000000..727fb74 --- /dev/null +++ b/icons/mic_active.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c2slehdr0kxo4" +path="res://.godot/imported/mic_active.svg-18499b0b78a23d9ea53aa3a320fe1e2c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/mic_active.svg" +dest_files=["res://.godot/imported/mic_active.svg-18499b0b78a23d9ea53aa3a320fe1e2c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/icons/mic_inactive.svg b/icons/mic_inactive.svg new file mode 100644 index 0000000..43fe584 --- /dev/null +++ b/icons/mic_inactive.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mic_inactive.svg.import b/icons/mic_inactive.svg.import new file mode 100644 index 0000000..d26babe --- /dev/null +++ b/icons/mic_inactive.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d1pmeyg5jhptu" +path="res://.godot/imported/mic_inactive.svg-d6a42ee2c060cb2d9b17cec264926886.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icons/mic_inactive.svg" +dest_files=["res://.godot/imported/mic_inactive.svg-d6a42ee2c060cb2d9b17cec264926886.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..9490d2b --- /dev/null +++ b/project.godot @@ -0,0 +1,25 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="VoIP" +run/main_scene="res://ui/main_menu/main_menu.tscn" +config/features=PackedStringArray("4.2", "Forward Plus") +config/icon="res://icon.svg" + +[audio] + +buses/default_bus_layout="res://voip_setup.tres" +driver/enable_input=true + +[dotnet] + +project/assembly_name="VoIP" diff --git a/ui/main_menu/main_menu.tscn b/ui/main_menu/main_menu.tscn new file mode 100644 index 0000000..4e65949 --- /dev/null +++ b/ui/main_menu/main_menu.tscn @@ -0,0 +1,47 @@ +[gd_scene load_steps=2 format=3 uid="uid://nw148tplcq6w"] + +[ext_resource type="Script" path="res://ui/main_menu/scripts/main_menu.gd" id="1_bwmj7"] + +[node name="MainMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_bwmj7") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="HostIP" type="LineEdit" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +placeholder_text = "Host IP" + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="HostGame" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Host Game" + +[node name="JoinGame" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Join Game" + +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/HostGame" to="." method="_on_host_game_pressed"] +[connection signal="pressed" from="MarginContainer/VBoxContainer/HBoxContainer/JoinGame" to="." method="_on_join_game_pressed"] diff --git a/ui/main_menu/scripts/main_menu.gd b/ui/main_menu/scripts/main_menu.gd new file mode 100644 index 0000000..b0c61b2 --- /dev/null +++ b/ui/main_menu/scripts/main_menu.gd @@ -0,0 +1,98 @@ +extends Control + +#region Signals + +signal player_connected(peer_id, player_info) +signal player_disconnected(peer_id) +signal server_disconnected + +#endregion + +@onready var host_ip: LineEdit = $MarginContainer/VBoxContainer/HostIP +const PORT: int = 7000 +const DEFAULT_SERVER_IP: String = "127.0.0.1" +const MAX_CONNECTIONS: int = 20 + +var players = {} + +var player_info = {"name": "Name"} +var players_loaded: int = 0 + +func _ready(): + multiplayer.peer_connected.connect(_on_player_connected) + multiplayer.peer_disconnected.connect(_on_player_disconnected) + multiplayer.connected_to_server.connect(_on_connected_ok) + multiplayer.connection_failed.connect(_on_connected_fail) + multiplayer.server_disconnected.connect(_on_server_disconnected) + +func _on_host_game_pressed(): + var peer = ENetMultiplayerPeer.new() + var error = peer.create_server(PORT, MAX_CONNECTIONS) + if error: + return error + multiplayer.multiplayer_peer = peer + + players[1] = player_info + player_connected.emit(1, player_info) + load_game.rpc_id(1, "res://ui/main_scene/main_scene.tscn") + +func _on_join_game_pressed(): + var address: String = host_ip.text + if address.is_empty(): + address = DEFAULT_SERVER_IP + var peer = ENetMultiplayerPeer.new() + var error = peer.create_client(address, PORT) + if error: + return error + multiplayer.multiplayer_peer = peer + +func remove_multiplayer_peer(): + multiplayer.multiplayer_peer = null + +#region Events + +@rpc("call_local", "reliable") +func load_game(game_scene_path): + get_tree().change_scene_to_file(game_scene_path) + +@rpc("any_peer", "call_local", "reliable") +func player_loaded(): + if multiplayer.is_server(): + players_loaded += 1 + if players_loaded == players.size(): + players_loaded = 0 + +@rpc("any_peer", "reliable") +func _register_player(new_player_info): + var new_player_id = multiplayer.get_remote_sender_id() + players[new_player_id] = new_player_info + player_connected.emit(new_player_id, new_player_info) + +#region Multiplayer Signal Handlers + +func _on_player_connected(id): + _register_player.rpc_id(id, player_info) + +func _on_player_disconnected(id): + players.erase(id) + player_disconnected.emit(id) + +func _on_connected_ok(): + var peer_id = multiplayer.get_unique_id() + players[peer_id] = player_info + print("Connected: ", peer_id, " ", players[peer_id]) + player_connected.emit(peer_id, player_info) + + load_game.rpc_id(peer_id, "res://ui/main_scene/main_scene.tscn") + +func _on_connected_fail(): + multiplayer.multiplayer_peer = null + +func _on_server_disconnected(): + multiplayer.multiplayer_peer = null + players.clear() + server_disconnected.emit() + +#endregion + +#endregion diff --git a/ui/main_scene/Microphone.gd b/ui/main_scene/Microphone.gd new file mode 100644 index 0000000..56a06b0 --- /dev/null +++ b/ui/main_scene/Microphone.gd @@ -0,0 +1,25 @@ +extends AudioStreamPlayer + +var effect: AudioEffectCapture +var recording +var playback: AudioStreamGeneratorPlayback + +@onready var voip: AudioStreamPlayer = $"../VoIP" + +func _ready(): + var idx = AudioServer.get_bus_index("Mic") + effect = AudioServer.get_bus_effect(idx, 0) + + playback = voip.get_stream_playback() + +func _process(_delta): + var frames: PackedVector2Array + + if effect.can_get_buffer(1024): + frames = effect.get_buffer(1024) + playback_voip.rpc(frames) + + +@rpc("any_peer", "call_remote", "unreliable_ordered", 1) +func playback_voip(frames: PackedVector2Array): + playback.push_buffer(frames) diff --git a/ui/main_scene/main_scene.tscn b/ui/main_scene/main_scene.tscn new file mode 100644 index 0000000..3db55e8 --- /dev/null +++ b/ui/main_scene/main_scene.tscn @@ -0,0 +1,116 @@ +[gd_scene load_steps=7 format=3 uid="uid://dvy8ub5r3mnjc"] + +[ext_resource type="Script" path="res://ui/main_scene/Microphone.gd" id="2_h65el"] + +[sub_resource type="SystemFont" id="SystemFont_dukv4"] +font_names = PackedStringArray("JetBrains Mono NL") +font_weight = 425 + +[sub_resource type="LabelSettings" id="LabelSettings_jutwa"] +font = SubResource("SystemFont_dukv4") + +[sub_resource type="LabelSettings" id="LabelSettings_bt5o0"] +font_size = 11 + +[sub_resource type="AudioStreamMicrophone" id="AudioStreamMicrophone_1rv7u"] + +[sub_resource type="AudioStreamGenerator" id="AudioStreamGenerator_a1tib"] + +[node name="MainScene" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +text = "Your Client is Now sending your microphone input over the network + to all other clients connected to the server." +label_settings = SubResource("LabelSettings_jutwa") +horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label2" type="Label" parent="MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +text = "Note: I'm 99% sure this demo would get buggy with more than one client connected to the server since there is only one VoIP Playback. To solve this just spawn one per client." +label_settings = SubResource("LabelSettings_bt5o0") +horizontal_alignment = 1 +vertical_alignment = 1 +autowrap_mode = 2 + +[node name="MarginContainer2" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 0 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer2"] +custom_minimum_size = Vector2(0, 50) +layout_mode = 2 +alignment = 1 + +[node name="LinkButton" type="LinkButton" parent="MarginContainer2/HBoxContainer"] +layout_mode = 2 +text = "YouTube" +underline = 2 +uri = "https://youtube.com/@4musedtv" + +[node name="Control2" type="Control" parent="MarginContainer2/HBoxContainer"] +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 + +[node name="LinkButton2" type="LinkButton" parent="MarginContainer2/HBoxContainer"] +layout_mode = 2 +text = "Twitter" +underline = 2 +uri = "https://twitter.com/4musedWasTaken" + +[node name="Control" type="Control" parent="MarginContainer2/HBoxContainer"] +custom_minimum_size = Vector2(30, 0) +layout_mode = 2 + +[node name="LinkButton3" type="LinkButton" parent="MarginContainer2/HBoxContainer"] +layout_mode = 2 +text = "Twitch" +underline = 2 +uri = "https://twitch.tv/4musedtv" + +[node name="Microphone" type="AudioStreamPlayer" parent="."] +stream = SubResource("AudioStreamMicrophone_1rv7u") +autoplay = true +bus = &"Mic" +script = ExtResource("2_h65el") + +[node name="VoIP" type="AudioStreamPlayer" parent="."] +stream = SubResource("AudioStreamGenerator_a1tib") +autoplay = true + +[node name="AudioListener3D" type="AudioListener3D" parent="."] diff --git a/voip_setup.tres b/voip_setup.tres new file mode 100644 index 0000000..6a63486 --- /dev/null +++ b/voip_setup.tres @@ -0,0 +1,20 @@ +[gd_resource type="AudioBusLayout" load_steps=2 format=3 uid="uid://dr43jvdvgkbuo"] + +[sub_resource type="AudioEffectCapture" id="AudioEffectCapture_pihug"] +resource_name = "Capture" + +[resource] +bus/1/name = &"MicSink" +bus/1/solo = false +bus/1/mute = true +bus/1/bypass_fx = false +bus/1/volume_db = 0.0 +bus/1/send = &"Master" +bus/2/name = &"Mic" +bus/2/solo = false +bus/2/mute = false +bus/2/bypass_fx = false +bus/2/volume_db = 0.0 +bus/2/send = &"MicSink" +bus/2/effect/0/effect = SubResource("AudioEffectCapture_pihug") +bus/2/effect/0/enabled = true