From aca5a97162b04e1e60b28372ddadefe8b0377b47 Mon Sep 17 00:00:00 2001 From: Sebastian Lohff Date: Mon, 14 Apr 2025 02:46:15 +0200 Subject: [PATCH] Autoconnect, reconnect, connection display We can now autoconnect via parameter on web, e.g. `freq=430.200`, which then autoconnects to that frequency, creating it when it is not already created. We also now reconnect on connection failure and display the connection state down below. --- autoloads/utils.gd | 4 +- scenes/MultiplayerConnect.tscn | 107 ++++++++++++++++++++++----------- scenes/main.gd | 1 - scenes/multiplayer_connect.gd | 80 +++++++++++++++++------- signalsrv/signalsrv.py | 5 +- 5 files changed, 136 insertions(+), 61 deletions(-) diff --git a/autoloads/utils.gd b/autoloads/utils.gd index d583f43..53184f7 100644 --- a/autoloads/utils.gd +++ b/autoloads/utils.gd @@ -1,7 +1,9 @@ extends Node +var first_connect_done := false + func get_external_freq_param(): match OS.get_name(): - "web": + "Web": return JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('freq')") return null diff --git a/scenes/MultiplayerConnect.tscn b/scenes/MultiplayerConnect.tscn index 6b8d360..f5d4758 100644 --- a/scenes/MultiplayerConnect.tscn +++ b/scenes/MultiplayerConnect.tscn @@ -13,8 +13,20 @@ grow_vertical = 2 theme = ExtResource("1_2wc0w") script = ExtResource("1_uyd8l") -[node name="ConnectView" type="Control" parent="."] +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ConnectView" type="Control" parent="VBoxContainer"] unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/ConnectView"] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -22,46 +34,42 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="VBoxContainer" type="VBoxContainer" parent="ConnectView"] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 - -[node name="HBoxContainer" type="HBoxContainer" parent="ConnectView/VBoxContainer"] +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/ConnectView/VBoxContainer"] layout_mode = 2 -[node name="FrequencyCreator" type="TextEdit" parent="ConnectView/VBoxContainer/HBoxContainer"] +[node name="FrequencyCreator" type="TextEdit" parent="VBoxContainer/ConnectView/VBoxContainer/HBoxContainer"] unique_name_in_owner = true custom_minimum_size = Vector2(100, 0) layout_mode = 2 theme_override_font_sizes/font_size = 20 text = "430.200" -[node name="Label" type="Label" parent="ConnectView/VBoxContainer/HBoxContainer"] +[node name="Label" type="Label" parent="VBoxContainer/ConnectView/VBoxContainer/HBoxContainer"] layout_mode = 2 text = "MHz" -[node name="CreateButton" type="Button" parent="ConnectView/VBoxContainer/HBoxContainer"] +[node name="CreateButton" type="Button" parent="VBoxContainer/ConnectView/VBoxContainer/HBoxContainer"] custom_minimum_size = Vector2(0, 100) layout_mode = 2 size_flags_stretch_ratio = 2.41 text = "Create Frequency" -[node name="RefreshButton" type="Button" parent="ConnectView/VBoxContainer"] +[node name="RefreshButton" type="Button" parent="VBoxContainer/ConnectView/VBoxContainer"] layout_mode = 2 text = "Refresh" -[node name="FreqList" type="ItemList" parent="ConnectView/VBoxContainer"] +[node name="FreqList" type="ItemList" parent="VBoxContainer/ConnectView/VBoxContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -[node name="MorseView" type="Control" parent="."] +[node name="MorseView" type="Control" parent="VBoxContainer"] unique_name_in_owner = true visible = false +layout_mode = 2 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MorseView"] layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -69,43 +77,70 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 -[node name="VBoxContainer" type="VBoxContainer" parent="MorseView"] -layout_mode = 1 -anchors_preset = 15 -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="VBoxContainer/MorseView/VBoxContainer"] layout_mode = 2 -[node name="Label" type="Label" parent="MorseView/VBoxContainer/HBoxContainer"] +[node name="Label" type="Label" parent="VBoxContainer/MorseView/VBoxContainer/HBoxContainer"] layout_mode = 2 text = "Freq:" -[node name="FreqLabel" type="Label" parent="MorseView/VBoxContainer/HBoxContainer"] +[node name="FreqLabel" type="Label" parent="VBoxContainer/MorseView/VBoxContainer/HBoxContainer"] unique_name_in_owner = true layout_mode = 2 -[node name="PlayerContainer" type="VBoxContainer" parent="MorseView/VBoxContainer"] +[node name="PlayerContainer" type="VBoxContainer" parent="VBoxContainer/MorseView/VBoxContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -[node name="MorseButton" type="Button" parent="MorseView/VBoxContainer"] +[node name="MorseButton" type="Button" parent="VBoxContainer/MorseView/VBoxContainer"] layout_mode = 2 size_flags_vertical = 3 size_flags_stretch_ratio = 2.0 text = "MORSE" -[node name="LeaveButton" type="Button" parent="MorseView/VBoxContainer"] +[node name="LeaveButton" type="Button" parent="VBoxContainer/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/RefreshButton" to="." method="_on_refresh_button_pressed"] -[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_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"] +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "Status:" + +[node name="StatusLabel" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Disconnected" + +[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer2"] +layout_mode = 2 +mouse_filter = 0 +text = "Last Error:" + +[node name="ErrorLabel" type="Label" parent="VBoxContainer/HBoxContainer/HBoxContainer2"] +unique_name_in_owner = true +custom_minimum_size = Vector2(100, 0) +layout_mode = 2 +mouse_filter = 0 +text = "" +autowrap_mode = 2 + +[connection signal="pressed" from="VBoxContainer/ConnectView/VBoxContainer/HBoxContainer/CreateButton" to="." method="_on_create_button_pressed"] +[connection signal="pressed" from="VBoxContainer/ConnectView/VBoxContainer/RefreshButton" to="." method="_on_refresh_button_pressed"] +[connection signal="item_clicked" from="VBoxContainer/ConnectView/VBoxContainer/FreqList" to="." method="_on_freq_list_join"] +[connection signal="button_down" from="VBoxContainer/MorseView/VBoxContainer/MorseButton" to="." method="_on_morse_button_button_down"] +[connection signal="button_up" from="VBoxContainer/MorseView/VBoxContainer/MorseButton" to="." method="_on_morse_button_button_up"] +[connection signal="pressed" from="VBoxContainer/MorseView/VBoxContainer/LeaveButton" to="." method="_on_leave_button_pressed"] +[connection signal="gui_input" from="VBoxContainer/HBoxContainer/HBoxContainer2/Label" to="." method="_on_error_label_gui_input"] +[connection signal="gui_input" from="VBoxContainer/HBoxContainer/HBoxContainer2/ErrorLabel" to="." method="_on_error_label_gui_input"] diff --git a/scenes/main.gd b/scenes/main.gd index 26190d9..41fad80 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -33,7 +33,6 @@ func _ready(): print("External freq: ", Utils.get_external_freq_param()) if Utils.get_external_freq_param(): - _on_multiplayer_button_pressed() # FIXME: make this dependant on button diff --git a/scenes/multiplayer_connect.gd b/scenes/multiplayer_connect.gd index 8e7a450..e1e5c00 100644 --- a/scenes/multiplayer_connect.gd +++ b/scenes/multiplayer_connect.gd @@ -5,6 +5,8 @@ var port := "3784" var ws_url := "wss://seba-geek.de/godot/cw-generator-ws/" var IS_DEBUG = false var ws := WebSocketPeer.new() +var ws_last_status = -1 +var autoconnect_to_freq: String var available_freqs = [] var mmb_self: MultiMorseBanner @@ -32,39 +34,61 @@ func _ready() -> void: # FIXME: status / error messages # FIXME: randomize default join frquency # FIXME: automatic refresh + _connect_ws() + + if not Utils.first_connect_done: + var cmdline_freq = Utils.get_external_freq_param() + if cmdline_freq: + autoconnect_to_freq = cmdline_freq + +func _connect_ws(): if not IS_DEBUG: - print(ws_url) + print("Connecting to ", 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: - pass - - func _process(_delta: float) -> void: ws.poll() var state := ws.get_ready_state() - # print("moin ", state) while state == WebSocketPeer.STATE_OPEN and ws.get_available_packet_count(): _handle_packet() - if state != WebSocketPeer.STATE_OPEN: - print("Error: websocket in state", state) - + if ws_last_status != state: + match state: + WebSocketPeer.STATE_CONNECTING: + %StatusLabel.text = "Connecting...!" + WebSocketPeer.STATE_OPEN: + %StatusLabel.text = "Connected!" + WebSocketPeer.STATE_CLOSING: + %StatusLabel.text = "Disconnecting..." + WebSocketPeer.STATE_CLOSED: + %StatusLabel.text = "Disconnected :(" + + # Trigger reconnect + _trigger_reconnect(1) + ws_last_status = state + +func _trigger_reconnect(delay: int): + await get_tree().create_timer(delay).timeout + _connect_ws() + func _handle_packet() -> void: var data := ws.get_packet().get_string_from_utf8() - print(data) var parsed: Dictionary = JSON.parse_string(data) if typeof(parsed) != TYPE_DICTIONARY or not parsed.has("type"): + print("Error: Could not parse data: ", data) return - print("parsed ", parsed) + + print("Recvd data: ", parsed) match parsed["type"]: "hello": # fetch frequency list on first join _on_refresh_button_pressed() + + if autoconnect_to_freq: + send_data({"cmd": "create", "freq": autoconnect_to_freq, "join-if-present": true}) "join": _join_freq(parsed["freq"], parsed["self_id"], parsed["other_players"]) @@ -111,15 +135,9 @@ func _handle_packet() -> void: return 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() + _leave_freq() + "error": + %ErrorLabel.text = parsed["message"] _: print("Unhandled message: ", parsed["type"]) @@ -136,6 +154,20 @@ func _join_freq(freq: String, player_id: String, other_players: Array): %ConnectView.hide() %MorseView.show() +func _leave_freq(): + %MorseView.hide() + if mmb_self: + mmb_self.set_morse_state(false) + mmb_self = null + + for mmb: MultiMorseBanner in mmb_others.values(): + mmb.set_morse_state(false) + + mmb_others.clear() + for child in %PlayerContainer.get_children(): + %PlayerContainer.remove_child(child) + %ConnectView.show() + func _on_refresh_button_pressed() -> void: var refresh_cmd := {"cmd": "list", "type": "cw-generator"} var data := JSON.stringify(refresh_cmd) + "\n" @@ -191,6 +223,10 @@ func _on_morse_button_button_up() -> void: return set_morse_state(false) - func _on_leave_button_pressed() -> void: send_data({"cmd": "leave"}) + + +func _on_error_label_gui_input(event: InputEvent) -> void: + if event is InputEventMouseButton and event.pressed and event.button_index == 1: + %ErrorLabel.text = "" diff --git a/signalsrv/signalsrv.py b/signalsrv/signalsrv.py index 3635a90..89fde8f 100644 --- a/signalsrv/signalsrv.py +++ b/signalsrv/signalsrv.py @@ -97,7 +97,10 @@ class Client: return if freq in self.freqs: - await self._send_error("Frequency already in use") + if data.get("join-if-present"): + await self._join_room({"freq": freq}) + else: + await self._send_error("Frequency already in use") return self.curr_freq = freq