Working Multiplayer

...now with a GUI Theme! Players can leave a room as well. Actually
usable on mobile!
This commit is contained in:
Sebastian Lohff 2025-04-13 00:20:10 +02:00
parent 970f5d951b
commit fb500705ef
6 changed files with 80 additions and 17 deletions

4
scenes/GUITheme.tres Normal file
View File

@ -0,0 +1,4 @@
[gd_resource type="Theme" format=3 uid="uid://xxoc27tvaiut"]
[resource]
default_font_size = 30

View File

@ -1,5 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://dnxcrx04kl3xy"] [gd_scene load_steps=3 format=3 uid="uid://dnxcrx04kl3xy"]
[ext_resource type="Theme" uid="uid://xxoc27tvaiut" path="res://scenes/GUITheme.tres" id="1_2wc0w"]
[ext_resource type="Script" uid="uid://di8r70441xdms" path="res://scenes/multiplayer_connect.gd" id="1_uyd8l"] [ext_resource type="Script" uid="uid://di8r70441xdms" path="res://scenes/multiplayer_connect.gd" id="1_uyd8l"]
[node name="MultiplayerConnect" type="Control"] [node name="MultiplayerConnect" type="Control"]
@ -9,6 +10,7 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
theme = ExtResource("1_2wc0w")
script = ExtResource("1_uyd8l") script = ExtResource("1_uyd8l")
[node name="ConnectView" type="Control" parent="."] [node name="ConnectView" type="Control" parent="."]
@ -35,6 +37,7 @@ layout_mode = 2
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(100, 0) custom_minimum_size = Vector2(100, 0)
layout_mode = 2 layout_mode = 2
theme_override_font_sizes/font_size = 20
text = "430.200" text = "430.200"
[node name="Label" type="Label" parent="ConnectView/VBoxContainer/HBoxContainer"] [node name="Label" type="Label" parent="ConnectView/VBoxContainer/HBoxContainer"]
@ -42,6 +45,7 @@ layout_mode = 2
text = "MHz" text = "MHz"
[node name="CreateButton" type="Button" parent="ConnectView/VBoxContainer/HBoxContainer"] [node name="CreateButton" type="Button" parent="ConnectView/VBoxContainer/HBoxContainer"]
custom_minimum_size = Vector2(0, 100)
layout_mode = 2 layout_mode = 2
size_flags_stretch_ratio = 2.41 size_flags_stretch_ratio = 2.41
text = "Create Frequency" text = "Create Frequency"
@ -58,14 +62,20 @@ size_flags_vertical = 3
[node name="MorseView" type="Control" parent="."] [node name="MorseView" type="Control" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
visible = false visible = false
anchors_preset = 0 layout_mode = 1
offset_right = 40.0 anchors_preset = 15
offset_bottom = 40.0 anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="MorseView"] [node name="VBoxContainer" type="VBoxContainer" parent="MorseView"]
layout_mode = 0 layout_mode = 1
offset_right = 40.0 anchors_preset = 15
offset_bottom = 40.0 anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="HBoxContainer" type="HBoxContainer" parent="MorseView/VBoxContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="MorseView/VBoxContainer"]
layout_mode = 2 layout_mode = 2
@ -81,6 +91,7 @@ layout_mode = 2
[node name="PlayerContainer" type="VBoxContainer" parent="MorseView/VBoxContainer"] [node name="PlayerContainer" type="VBoxContainer" parent="MorseView/VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3
[node name="MorseButton" type="Button" parent="MorseView/VBoxContainer"] [node name="MorseButton" type="Button" parent="MorseView/VBoxContainer"]
layout_mode = 2 layout_mode = 2
@ -88,8 +99,13 @@ size_flags_vertical = 3
size_flags_stretch_ratio = 2.0 size_flags_stretch_ratio = 2.0
text = "MORSE" text = "MORSE"
[node name="LeaveButton" type="Button" parent="MorseView/VBoxContainer"]
layout_mode = 2
text = "Leave Frequency"
[connection signal="pressed" from="ConnectView/VBoxContainer/HBoxContainer/CreateButton" to="." method="_on_create_button_pressed"] [connection signal="pressed" from="ConnectView/VBoxContainer/HBoxContainer/CreateButton" to="." method="_on_create_button_pressed"]
[connection signal="pressed" from="ConnectView/VBoxContainer/RefreshButton" to="." method="_on_refresh_button_pressed"] [connection signal="pressed" from="ConnectView/VBoxContainer/RefreshButton" to="." method="_on_refresh_button_pressed"]
[connection signal="item_clicked" from="ConnectView/VBoxContainer/FreqList" to="." method="_on_freq_list_join"] [connection signal="item_clicked" from="ConnectView/VBoxContainer/FreqList" to="." method="_on_freq_list_join"]
[connection signal="button_down" from="MorseView/VBoxContainer/MorseButton" to="." method="_on_morse_button_button_down"] [connection signal="button_down" from="MorseView/VBoxContainer/MorseButton" to="." method="_on_morse_button_button_down"]
[connection signal="button_up" from="MorseView/VBoxContainer/MorseButton" to="." method="_on_morse_button_button_up"] [connection signal="button_up" from="MorseView/VBoxContainer/MorseButton" to="." method="_on_morse_button_button_up"]
[connection signal="pressed" from="MorseView/VBoxContainer/LeaveButton" to="." method="_on_leave_button_pressed"]

View File

@ -27,7 +27,7 @@ func _ready():
fill_buffer() fill_buffer()
if multiplayer_enabled: if multiplayer_enabled:
$VBoxContainer/MultiplayerButton.visible = true %MultiplayerButton.visible = true
OS.open_midi_inputs() OS.open_midi_inputs()
print(OS.get_connected_midi_inputs()) print(OS.get_connected_midi_inputs())

View File

@ -1,6 +1,7 @@
[gd_scene load_steps=5 format=3 uid="uid://ctak1goemnnc5"] [gd_scene load_steps=6 format=3 uid="uid://ctak1goemnnc5"]
[ext_resource type="Script" uid="uid://dmeokosn7gr27" path="res://scenes/main.gd" id="1_8bx00"] [ext_resource type="Script" uid="uid://dmeokosn7gr27" path="res://scenes/main.gd" id="1_8bx00"]
[ext_resource type="Theme" uid="uid://xxoc27tvaiut" path="res://scenes/GUITheme.tres" id="1_jyhfs"]
[ext_resource type="PackedScene" uid="uid://xqic6oa5d7oc" path="res://scenes/MorseBanner.tscn" id="2_v02md"] [ext_resource type="PackedScene" uid="uid://xqic6oa5d7oc" path="res://scenes/MorseBanner.tscn" id="2_v02md"]
[ext_resource type="Script" uid="uid://bjt60u6r1hqf7" path="res://addons/SharePlugin/Share.gd" id="3_sugp2"] [ext_resource type="Script" uid="uid://bjt60u6r1hqf7" path="res://addons/SharePlugin/Share.gd" id="3_sugp2"]
@ -15,6 +16,7 @@ anchor_right = 1.0
anchor_bottom = 1.0 anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
theme = ExtResource("1_jyhfs")
script = ExtResource("1_8bx00") script = ExtResource("1_8bx00")
[node name="VBoxContainer" type="VBoxContainer" parent="."] [node name="VBoxContainer" type="VBoxContainer" parent="."]
@ -57,15 +59,23 @@ layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
text = "Write Wav" text = "Write Wav"
[node name="ResetButton" type="Button" parent="VBoxContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="ResetButton" type="Button" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
size_flags_stretch_ratio = 0.5 size_flags_stretch_ratio = 0.5
text = "Reset" text = "Reset"
[node name="MultiplayerButton" type="Button" parent="VBoxContainer"] [node name="MultiplayerButton" type="Button" parent="VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
visible = false visible = false
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.5
text = "Connect to Frequency" text = "Connect to Frequency"
[node name="Player" type="AudioStreamPlayer" parent="."] [node name="Player" type="AudioStreamPlayer" parent="."]
@ -78,5 +88,5 @@ script = ExtResource("3_sugp2")
[connection signal="button_down" from="VBoxContainer/MorseButton" to="." method="_on_morse_button_down"] [connection signal="button_down" from="VBoxContainer/MorseButton" to="." method="_on_morse_button_down"]
[connection signal="button_up" from="VBoxContainer/MorseButton" to="." method="_on_morse_button_up"] [connection signal="button_up" from="VBoxContainer/MorseButton" to="." method="_on_morse_button_up"]
[connection signal="pressed" from="VBoxContainer/WavButton" to="." method="_on_wav_button_pressed"] [connection signal="pressed" from="VBoxContainer/WavButton" to="." method="_on_wav_button_pressed"]
[connection signal="pressed" from="VBoxContainer/ResetButton" to="." method="_on_reset_button_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/ResetButton" to="." method="_on_reset_button_pressed"]
[connection signal="pressed" from="VBoxContainer/MultiplayerButton" to="." method="_on_multiplayer_button_pressed"] [connection signal="pressed" from="VBoxContainer/HBoxContainer/MultiplayerButton" to="." method="_on_multiplayer_button_pressed"]

View File

@ -2,6 +2,8 @@ extends Control
var server := "seba-geek.de" var server := "seba-geek.de"
var port := "3784" var port := "3784"
var ws_url := "wss://seba-geek.de/godot/cw-generator-ws/"
var IS_DEBUG = false
var ws := WebSocketPeer.new() var ws := WebSocketPeer.new()
var available_freqs = [] var available_freqs = []
@ -30,9 +32,14 @@ func _ready() -> void:
# FIXME: status / error messages # FIXME: status / error messages
# FIXME: randomize default join frquency # FIXME: randomize default join frquency
# FIXME: automatic refresh # FIXME: automatic refresh
print("ws://%s:%s" % [server, port]) if not IS_DEBUG:
ws.connect_to_url("ws://%s:%s" % [server, port]) print(ws_url)
ws.connect_to_url(ws_url)
else:
print("ws://%s:%s" % [server, port])
ws.connect_to_url("ws://%s:%s" % [server, port])
func refresh_list() -> void: func refresh_list() -> void:
pass pass
@ -43,6 +50,8 @@ func _process(_delta: float) -> void:
# print("moin ", state) # print("moin ", state)
while state == WebSocketPeer.STATE_OPEN and ws.get_available_packet_count(): while state == WebSocketPeer.STATE_OPEN and ws.get_available_packet_count():
_handle_packet() _handle_packet()
if state != WebSocketPeer.STATE_OPEN:
print("Error: websocket in state", state)
func _handle_packet() -> void: func _handle_packet() -> void:
var data := ws.get_packet().get_string_from_utf8() var data := ws.get_packet().get_string_from_utf8()
@ -101,7 +110,16 @@ func _handle_packet() -> void:
print("Error: player not part of freq ", player) print("Error: player not part of freq ", player)
return return
remove_player(parsed["player"]) remove_player(parsed["player"])
"leave":
%MorseView.hide()
mmb_self.set_morse_state(false)
for mmb: MultiMorseBanner in mmb_others.values():
mmb.set_morse_state(false)
mmb_self = null
mmb_others.clear()
for child in %PlayerContainer.get_children():
%PlayerContainer.remove_child(child)
%ConnectView.show()
_: _:
print("Unhandled message: ", parsed["type"]) print("Unhandled message: ", parsed["type"])
@ -172,3 +190,7 @@ func _on_morse_button_button_up() -> void:
if not mmb_self: if not mmb_self:
return return
set_morse_state(false) set_morse_state(false)
func _on_leave_button_pressed() -> void:
send_data({"cmd": "leave"})

View File

@ -62,6 +62,8 @@ class Client:
await self._create_room(data) await self._create_room(data)
case "join": case "join":
await self._join_room(data) await self._join_room(data)
case "leave":
await self._leave_room()
case "list": case "list":
freqs = [{"freq": freq, "players": len(players)} freqs = [{"freq": freq, "players": len(players)}
for freq, players in self.freqs.items()] for freq, players in self.freqs.items()]
@ -131,6 +133,10 @@ class Client:
await other._send(type="morse-state", state=data["state"], from_player=self.id) await other._send(type="morse-state", state=data["state"], from_player=self.id)
async def _leave_room(self): async def _leave_room(self):
if not self.curr_freq:
self._send_error("You are not on a frequency")
return
for other in self._others(self.curr_freq): for other in self._others(self.curr_freq):
await other._send(type="player-left", player=self.id) await other._send(type="player-left", player=self.id)
try: try:
@ -141,6 +147,11 @@ class Client:
del self.freqs[self.curr_freq] del self.freqs[self.curr_freq]
self.curr_freq = None self.curr_freq = None
try:
await self._send(type="leave")
except Exception:
pass
def _others(self, freq): def _others(self, freq):
return [c for c in self.freqs[freq] if c.id != self.id] return [c for c in self.freqs[freq] if c.id != self.id]