From 3df8e2c3167849023c822004b4525bcae7bb3cd1 Mon Sep 17 00:00:00 2001 From: nokoe Date: Sat, 28 Sep 2024 23:28:03 +0200 Subject: start and use mdns discovery in client; fixes #177 --- client/menu/main.gd | 2 +- client/menu/play.gd | 28 ++++++++----- client/project.godot | 2 +- client/server_list.gd | 106 +++++++++++++++++++++++++++++++++++++++++------- client/server_list.tscn | 8 ---- client/settings.gd | 7 ++++ 6 files changed, 118 insertions(+), 35 deletions(-) delete mode 100644 client/server_list.tscn diff --git a/client/menu/main.gd b/client/menu/main.gd index 997653c9..5aa3fbaa 100644 --- a/client/menu/main.gd +++ b/client/menu/main.gd @@ -24,7 +24,7 @@ func _ready(): if OS.has_feature("web"): quit_button.hide() Sound.play_music("MainMenu") - ServerList.fetch_server_list() + ServerList.one_shot() func _menu_cover(state): $side.visible = not state diff --git a/client/menu/play.gd b/client/menu/play.gd index b117d6a4..44cd3eb0 100644 --- a/client/menu/play.gd +++ b/client/menu/play.gd @@ -37,21 +37,23 @@ func _ready(): ServerList.update_loading.connect(update_server_list_loading) update_server_list(ServerList.current_list) update_server_list_loading(ServerList.loading) - + + ServerList.start() super() -func update_server_list(json: Array): +func update_server_list(lists: Array[Array]): for c in server_list.get_children(): c.queue_free() - for i in json: - var b := Button.new() - b.text_overrun_behavior = TextServer.OVERRUN_TRIM_WORD_ELLIPSIS - b.text = "%s (%d players)" % [i.name, i.players_online] - # TODO: Implement fallback address correctly - if i.version[0] != Multiplayer.VERSION_MAJOR or i.version[1] > Multiplayer.VERSION_MINOR: - b.disabled = true - b.pressed.connect(connect_to.bind(i.address[0])) - server_list.add_child(b) + for l in lists: + for i in l: + var b := Button.new() + b.text_overrun_behavior = TextServer.OVERRUN_TRIM_WORD_ELLIPSIS + b.text = "%s (%d players)" % [i.name, i.players_online] + # TODO: Implement fallback address correctly + if i.version[0] != Multiplayer.VERSION_MAJOR or i.version[1] > Multiplayer.VERSION_MINOR: + b.disabled = true + b.pressed.connect(connect_to.bind(i.address[0])) + server_list.add_child(b) func update_server_list_loading(status: bool): server_list_loading.visible = status @@ -127,3 +129,7 @@ func _on_uri_text_changed(new_text): func _on_back_pressed(): exit() + +func _menu_exit(): + ServerList.stop() + super() diff --git a/client/project.godot b/client/project.godot index d1e85f05..d931a1b0 100644 --- a/client/project.godot +++ b/client/project.godot @@ -24,7 +24,7 @@ Server="*res://server.gd" Sound="*res://audio/sound.tscn" DisableWrongJoypads="*res://disable_wrong_joypads.gd" InputManager="*res://menu/settings/input/input_manager.gd" -ServerList="*res://server_list.tscn" +ServerList="*res://server_list.gd" [display] diff --git a/client/server_list.gd b/client/server_list.gd index f509a34e..43cfc15c 100644 --- a/client/server_list.gd +++ b/client/server_list.gd @@ -1,5 +1,6 @@ # Hurry Curry! - a game about cooking # Copyright 2024 tpart +# Copyright 2024 nokoe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by @@ -18,24 +19,78 @@ extends Node signal update_loading(status: bool) signal update_server_list(list: Array) -var current_list := [] +enum Registry { + MDNS = 0, + GLOBAL = 1, +} + +const MDNS_URL: String = "http://127.0.0.1:27033/v1/list" +const HEADERS: Array[String] = [ + "Accept: application/json", + "User-Agent: Hurry Curry! %s" % Global.VERSION + ] + +var current_list: Array[Array] = [[], []] var loading := false +var mdns := HTTPRequest.new() +var reg := HTTPRequest.new() -@onready var req: HTTPRequest = $HTTPRequest +var thread: Thread +var pid: int + +var mdns_timer := Timer.new() +var reg_timer := Timer.new() +# after 30 minutes we stop fetching results to reduce server load +var timeout := Timer.new() + +func _ready() -> void: + add_child(mdns) + add_child(reg) + mdns_timer.wait_time = 5. + mdns_timer.one_shot = false + mdns_timer.timeout.connect(fetch_server_list.bind(Registry.MDNS)) + add_child(mdns_timer) + reg_timer.wait_time = 60. + reg_timer.one_shot = false + reg_timer.timeout.connect(fetch_server_list.bind(Registry.GLOBAL)) + add_child(reg_timer) + timeout.wait_time = 60. * 30. + timeout.timeout.connect(func(): + stop() + ) + add_child(timeout) + mdns.request_completed.connect(_on_request_completed.bind(Registry.MDNS)) + reg.request_completed.connect(_on_request_completed.bind(Registry.GLOBAL)) + if Global.get_setting("online.use_discovery"): + thread = Thread.new() + thread.start(func(): + # SAFETY: reading/writing arrays/dictionaries is allowed as long as + # the size is not changed. + # cf.: https://docs.godotengine.org/en/4.3/tutorials/performance/thread_safe_apis.html#gdscript-arrays-dictionaries + var res = OS.create_process(get_discovery_path(), [], true) + # SAFETY: since this is not read until the thread stops, this should + # probably be safe without synchronisation :) + pid = res + ) + +static func get_discovery_path() -> String: + var path: String = Global.get_setting("online.discovery_binary") + if path != "": + return path + else: + return "hurrycurry-discovery" + +func fetch_server_list(registry: Registry) -> void: + match registry: + Registry.MDNS: + mdns.request(MDNS_URL, HEADERS) + Registry.GLOBAL: + reg.request(Global.get_setting("online.registry_url") + "/v1/list", HEADERS) -func fetch_server_list(): - if loading: - push_warning("Server list is already loading") - return loading = true update_loading.emit(true) - req.request_completed.connect(_on_request_completed) - req.request("https://hurrycurry-registry.metamuffin.org/v1/list", [ - "Accept: application/json", - "User-Agent: Hurry Curry! %s" % Global.VERSION - ]) -func _on_request_completed(result: int, _response_code: int, _headers: PackedStringArray, body: PackedByteArray): +func _on_request_completed(result: int, _response_code: int, _headers: PackedStringArray, body: PackedByteArray, registry: Registry): loading = false update_loading.emit(false) if result != 0: @@ -45,5 +100,28 @@ func _on_request_completed(result: int, _response_code: int, _headers: PackedStr if json == null: push_error("Server list response invalid") return - current_list = json - update_server_list.emit(json) + current_list[registry] = json + update_server_list.emit(current_list) + +func start() -> void: + timeout.stop() + timeout.start() + if Global.get_setting("online.use_discovery"): + mdns_timer.start() + fetch_server_list(Registry.MDNS) + if Global.get_setting("online.use_registry"): + reg_timer.start() + fetch_server_list(Registry.GLOBAL) + +func stop() -> void: + timeout.stop() + mdns_timer.stop() + reg_timer.stop() + +func one_shot() -> void: + start() + stop() + +func _exit_tree(): + if thread != null: thread.wait_to_finish() + if pid != null: OS.kill(pid) diff --git a/client/server_list.tscn b/client/server_list.tscn deleted file mode 100644 index f43da77f..00000000 --- a/client/server_list.tscn +++ /dev/null @@ -1,8 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://3p61jpqt7acp"] - -[ext_resource type="Script" path="res://server_list.gd" id="1_a8xqo"] - -[node name="ServerList" type="Node"] -script = ExtResource("1_a8xqo") - -[node name="HTTPRequest" type="HTTPRequest" parent="."] diff --git a/client/settings.gd b/client/settings.gd index 667ffa41..eddc5207 100644 --- a/client/settings.gd +++ b/client/settings.gd @@ -70,6 +70,13 @@ static func get_root(): ToggleSetting.new("upnp", false), ToggleSetting.new("mdns", false), ToggleSetting.new("register", false), + ]), + SettingsCategory.new("online", [ + # TODO: make this opt-in + ToggleSetting.new("use_registry", true), + TextSetting.new("registry_url", "https://hurrycurry-registry.metamuffin.org/"), + ToggleSetting.new("use_discovery", true), + TextSetting.new("discovery_binary", ""), ]) ]) -- cgit v1.2.3-70-g09d2