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.
This commit is contained in:
Sebastian Lohff 2025-04-14 02:46:15 +02:00
parent 5dda7af184
commit aca5a97162
5 changed files with 136 additions and 61 deletions

View File

@ -1,7 +1,9 @@
extends Node extends Node
var first_connect_done := false
func get_external_freq_param(): func get_external_freq_param():
match OS.get_name(): match OS.get_name():
"web": "Web":
return JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('freq')") return JavaScriptBridge.eval("new URL(window.location.href).searchParams.get('freq')")
return null return null

View File

@ -13,8 +13,20 @@ grow_vertical = 2
theme = ExtResource("1_2wc0w") theme = ExtResource("1_2wc0w")
script = ExtResource("1_uyd8l") 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 unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/ConnectView"]
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@ -22,46 +34,42 @@ anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="ConnectView"] [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/ConnectView/VBoxContainer"]
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"]
layout_mode = 2 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 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 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="VBoxContainer/ConnectView/VBoxContainer/HBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "MHz" 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) 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"
[node name="RefreshButton" type="Button" parent="ConnectView/VBoxContainer"] [node name="RefreshButton" type="Button" parent="VBoxContainer/ConnectView/VBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "Refresh" text = "Refresh"
[node name="FreqList" type="ItemList" parent="ConnectView/VBoxContainer"] [node name="FreqList" type="ItemList" parent="VBoxContainer/ConnectView/VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="MorseView" type="Control" parent="."] [node name="MorseView" type="Control" parent="VBoxContainer"]
unique_name_in_owner = true unique_name_in_owner = true
visible = false visible = false
layout_mode = 2
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MorseView"]
layout_mode = 1 layout_mode = 1
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@ -69,43 +77,70 @@ anchor_bottom = 1.0
grow_horizontal = 2 grow_horizontal = 2
grow_vertical = 2 grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="MorseView"] [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/MorseView/VBoxContainer"]
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"]
layout_mode = 2 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 layout_mode = 2
text = "Freq:" 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 unique_name_in_owner = true
layout_mode = 2 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 unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 size_flags_vertical = 3
[node name="MorseButton" type="Button" parent="MorseView/VBoxContainer"] [node name="MorseButton" type="Button" parent="VBoxContainer/MorseView/VBoxContainer"]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 3 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"] [node name="LeaveButton" type="Button" parent="VBoxContainer/MorseView/VBoxContainer"]
layout_mode = 2 layout_mode = 2
text = "Leave Frequency" text = "Leave Frequency"
[connection signal="pressed" from="ConnectView/VBoxContainer/HBoxContainer/CreateButton" to="." method="_on_create_button_pressed"] [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
[connection signal="pressed" from="ConnectView/VBoxContainer/RefreshButton" to="." method="_on_refresh_button_pressed"] layout_mode = 2
[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"] [node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
[connection signal="button_up" from="MorseView/VBoxContainer/MorseButton" to="." method="_on_morse_button_button_up"] layout_mode = 2
[connection signal="pressed" from="MorseView/VBoxContainer/LeaveButton" to="." method="_on_leave_button_pressed"] 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 = "<None>"
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"]

View File

@ -33,7 +33,6 @@ func _ready():
print("External freq: ", Utils.get_external_freq_param()) print("External freq: ", Utils.get_external_freq_param())
if Utils.get_external_freq_param(): if Utils.get_external_freq_param():
_on_multiplayer_button_pressed() _on_multiplayer_button_pressed()
# FIXME: make this dependant on button # FIXME: make this dependant on button

View File

@ -5,6 +5,8 @@ var port := "3784"
var ws_url := "wss://seba-geek.de/godot/cw-generator-ws/" var ws_url := "wss://seba-geek.de/godot/cw-generator-ws/"
var IS_DEBUG = false var IS_DEBUG = false
var ws := WebSocketPeer.new() var ws := WebSocketPeer.new()
var ws_last_status = -1
var autoconnect_to_freq: String
var available_freqs = [] var available_freqs = []
var mmb_self: MultiMorseBanner var mmb_self: MultiMorseBanner
@ -32,39 +34,61 @@ 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
_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: if not IS_DEBUG:
print(ws_url) print("Connecting to ", ws_url)
ws.connect_to_url(ws_url) ws.connect_to_url(ws_url)
else: else:
print("ws://%s:%s" % [server, port]) print("ws://%s:%s" % [server, port])
ws.connect_to_url("ws://%s:%s" % [server, port]) ws.connect_to_url("ws://%s:%s" % [server, port])
func refresh_list() -> void:
pass
func _process(_delta: float) -> void: func _process(_delta: float) -> void:
ws.poll() ws.poll()
var state := ws.get_ready_state() var state := ws.get_ready_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: if ws_last_status != state:
print("Error: websocket in state", 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: func _handle_packet() -> void:
var data := ws.get_packet().get_string_from_utf8() var data := ws.get_packet().get_string_from_utf8()
print(data)
var parsed: Dictionary = JSON.parse_string(data) var parsed: Dictionary = JSON.parse_string(data)
if typeof(parsed) != TYPE_DICTIONARY or not parsed.has("type"): if typeof(parsed) != TYPE_DICTIONARY or not parsed.has("type"):
print("Error: Could not parse data: ", data)
return return
print("parsed ", parsed)
print("Recvd data: ", parsed)
match parsed["type"]: match parsed["type"]:
"hello": "hello":
# fetch frequency list on first join # fetch frequency list on first join
_on_refresh_button_pressed() _on_refresh_button_pressed()
if autoconnect_to_freq:
send_data({"cmd": "create", "freq": autoconnect_to_freq, "join-if-present": true})
"join": "join":
_join_freq(parsed["freq"], parsed["self_id"], parsed["other_players"]) _join_freq(parsed["freq"], parsed["self_id"], parsed["other_players"])
@ -111,15 +135,9 @@ func _handle_packet() -> void:
return return
remove_player(parsed["player"]) remove_player(parsed["player"])
"leave": "leave":
%MorseView.hide() _leave_freq()
mmb_self.set_morse_state(false) "error":
for mmb: MultiMorseBanner in mmb_others.values(): %ErrorLabel.text = parsed["message"]
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"])
@ -136,6 +154,20 @@ func _join_freq(freq: String, player_id: String, other_players: Array):
%ConnectView.hide() %ConnectView.hide()
%MorseView.show() %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: func _on_refresh_button_pressed() -> void:
var refresh_cmd := {"cmd": "list", "type": "cw-generator"} var refresh_cmd := {"cmd": "list", "type": "cw-generator"}
var data := JSON.stringify(refresh_cmd) + "\n" var data := JSON.stringify(refresh_cmd) + "\n"
@ -191,6 +223,10 @@ func _on_morse_button_button_up() -> void:
return return
set_morse_state(false) set_morse_state(false)
func _on_leave_button_pressed() -> void: func _on_leave_button_pressed() -> void:
send_data({"cmd": "leave"}) 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 = "<cleared>"

View File

@ -97,6 +97,9 @@ class Client:
return return
if freq in self.freqs: if freq in self.freqs:
if data.get("join-if-present"):
await self._join_room({"freq": freq})
else:
await self._send_error("Frequency already in use") await self._send_error("Frequency already in use")
return return