diff --git a/MainWindow.gd b/MainWindow.gd index e5a53d0..4b539e5 100644 --- a/MainWindow.gd +++ b/MainWindow.gd @@ -257,7 +257,7 @@ func fetch_song_list_at_start() -> void: print("Unexpected data") Playlist.update_last_played_song() update_song_list() - Settings.make_request2("/music/list", fetch_song_list_at_start_done, true) + MusicServerClient.fetch_song_list(fetch_song_list_at_start_done) func play_next_song() -> void: print("play_next_song ", Playlist.latest_played_song) @@ -277,11 +277,6 @@ func play_next_song() -> void: func fetch_song() -> void: print("fetch_song") Playlist.is_song_downloading = true - var url: String = "" - if Settings.use_low_played_mode: - url = "/music/rand/low" - else: - url = "/music/rand" var new_song_object: SongObject = SongObject.new() var fetch_song_done: Callable = func fetch_song_done(body: PackedByteArray) -> void: print("fetch_song_done") @@ -306,11 +301,11 @@ func fetch_song() -> void: Playlist.is_song_downloading = false if Settings.is_debug: update_song_list() - Settings.make_request2("/music/info", fetch_song_info_done, true) + MusicServerClient.fetch_song_info(fetch_song_info_done) - Settings.make_request2("/music/addQue", added_to_que_done, false) + MusicServerClient.add_to_queue(added_to_que_done) - Settings.make_request2(url, fetch_song_done, true) + MusicServerClient.fetch_random_song(Settings.use_low_played_mode, fetch_song_done) func update_song_list() -> void: print("update_song_list") @@ -335,7 +330,7 @@ func song_object_clicked(event: InputEvent, song_no: int) -> void: sound.data = body Playlist.add_song_to_song_object(song_no, sound) music_player_container.play_song_object(song_no) - Settings.make_request2("/music?song=" + str(song_no), song_downloaded, true) + MusicServerClient.fetch_song(song_no, song_downloaded) else: print("song already downloaded") music_player_container.play_song_object(song_no) @@ -347,7 +342,7 @@ func reset_playlist() -> void: fetch_song_list_at_start() fetch_song() - Settings.make_request2("/music/reset", playlist_reset_done, false) + MusicServerClient.reset_playlist(playlist_reset_done) func reset_points() -> void: print("reset_points") @@ -412,7 +407,7 @@ func get_suggestion_list() -> void: inspiration_list.add_child(inspiration_label) else: print("Unexpected data") - Settings.make_request2("/music/all", populate_list, true) + MusicServerClient.fetch_all_soundtracks(populate_list) func add_players() -> void: print("add_players") @@ -423,15 +418,15 @@ func add_players() -> void: func add_player() -> void: print("add_player") if new_player_name_field.text != "" && Settings.player_array.size() < Settings.MAX_NUMBER_OF_PLAYERS: - var new_player_object: PlayerObject = PlayerObject.new(new_player_name_field.text, Settings.player_array.size()) + var new_player_object: PlayerObject = PlayerObject.new(new_player_name_field.text) new_player_name_field.text = "" Settings.player_array.append(new_player_object) new_player_object.connect("first_point_triggerd", music_player_container._on_point_triggered.bind("first")) new_player_object.connect("match_point_triggerd", music_player_container._on_point_triggered.bind("match")) - new_player_object.connect("winner_triggerd", _on_player_won.bind(new_player_object.id)) + new_player_object.connect("winner_triggerd", _on_player_won.bind(new_player_object)) new_player_object.connect("point_given_sound", _on_make_point_given_sound) - new_player_object.connect("player_point_given", _on_point_given.bind(new_player_object.id)) - new_player_object.connect("player_point_taken", _on_point_taken.bind(new_player_object.id)) + new_player_object.connect("player_point_given", _on_point_given.bind(new_player_object)) + new_player_object.connect("player_point_taken", _on_point_taken.bind(new_player_object)) load_players() func load_players() -> void: @@ -442,43 +437,44 @@ func load_players() -> void: print(player_object.player_name) var new_player: Player = player.instantiate() - new_player.id = counter - + new_player.player_object = player_object + new_player.list_index = counter + new_player.connect("change_character_clicked", _on_player_change_character_clicked.bind(new_player, player_object.player_name)) - new_player.connect("player_removed", _on_player_removed.bind(counter)) + new_player.connect("player_removed", _on_player_removed.bind(player_object)) players_list_container.add_child(new_player) counter += 1 -func _on_point_given(player_given_point: int) -> void: +func _on_point_given(player_object: PlayerObject) -> void: print("_on_point_given") - log_window.add_log_row(Settings.player_array[player_given_point].player_name + " got a point") + log_window.add_log_row(player_object.player_name + " got a point") if Playlist.currently_playing_song >= 0: - Playlist.add_point(player_given_point) + Playlist.add_point(player_object.id) update_song_list() func _on_make_point_given_sound() -> void: music_player_container.play_sound_effect(preload("res://sounds/itemopen.wav")) -func _on_point_taken(player_taken_point: int) -> void: +func _on_point_taken(player_object: PlayerObject) -> void: print("_on_point_taken") - log_window.add_log_row(Settings.player_array[player_taken_point].player_name + " lost a point") + log_window.add_log_row(player_object.player_name + " lost a point") music_player_container.play_sound_effect(preload("res://sounds/itemequip.wav")) if Playlist.currently_playing_song >= 0: - Playlist.remove_point(player_taken_point) + Playlist.remove_point(player_object.id) update_song_list() -func _on_player_won(winning_player_id: int) -> void: +func _on_player_won(player_object: PlayerObject) -> void: print("_on_player_won") - winner_window.show_winner(winning_player_id) + winner_window.show_winner(player_object) music_player_container.play_song(preload("res://sounds/winning.mp3")) Settings.add_to_stats = false -func _on_player_removed(player_to_remove: int) -> void: - print("_on_player_removed ", player_to_remove) - Settings.player_array.remove_at(player_to_remove) +func _on_player_removed(player_object: PlayerObject) -> void: + print("_on_player_removed ", player_object.player_name) + Settings.player_array.erase(player_object) load_players() - Playlist.remove_player_from_list(player_to_remove) + Playlist.remove_player_from_list(player_object.id) update_song_list() func _on_player_change_character_clicked(new_player: Player, player_name: String) -> void: diff --git a/MusicPlayer.gd b/MusicPlayer.gd index 16062ef..7c12607 100644 --- a/MusicPlayer.gd +++ b/MusicPlayer.gd @@ -63,12 +63,12 @@ func _process(_delta: float) -> void: music_time_label.text = format_text(progress_slider.value, 0.0) func format_time(time: float) -> String: - var mins: String = "%02d" % floor(time / 60) - var sec: String = "%02d" % round(fmod(time, 60)) - return mins + ":" + sec + var total_seconds: int = roundi(time) + @warning_ignore("integer_division") + return "%02d:%02d" % [total_seconds / 60, total_seconds % 60] func format_text(part: float, total: float) -> String: - if (Settings.hide_beginning && part <= Settings.hide_for_seconds) && Settings.hide_length: + if (Settings.hide_beginning && part < Settings.hide_for_seconds) && Settings.hide_length: return "??:?? / ??:??" elif Settings.hide_length: return format_time(part) + " / ??:??" @@ -209,7 +209,7 @@ func get_sound_test_song() -> void: var sound: AudioStream = AudioStreamMP3.new() sound.data = body play_song(sound) - Settings.make_request2("/music/soundTest", test_sound, true) + MusicServerClient.fetch_sound_test_song(test_sound) ##### LOCAL var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3' diff --git a/Player.gd b/Player.gd index 5a29adb..3f616a6 100644 --- a/Player.gd +++ b/Player.gd @@ -19,15 +19,19 @@ var remove_player := $HBoxContainer/RemovePlayer signal change_character_clicked signal player_removed -@export -var id: int +## The player shown in this row. +var player_object: PlayerObject + +## Position in Settings.player_array. Only used for the keybinds: Alt+ +## gives this player a point and Ctrl+Alt+ takes one away. +var list_index: int # Called when the node enters the scene tree for the first time. func _ready() -> void: add_button.pressed.connect(add_point) - add_button.set_shortcut(create_button_shortcut(id, false)) + add_button.set_shortcut(create_button_shortcut(list_index, false)) minus_button.pressed.connect(minus_point) - minus_button.set_shortcut(create_button_shortcut(id, true)) + minus_button.set_shortcut(create_button_shortcut(list_index, true)) character.pressed.connect(change_character) remove_player.pressed.connect(func() -> void: player_removed.emit()) update_score() @@ -36,21 +40,21 @@ func _ready() -> void: func _process(delta: float) -> void: remove_player.visible = Settings.edit_players -func create_button_shortcut(scancode : int, ctrl_pressed: bool) -> Shortcut: +func create_button_shortcut(index: int, ctrl_pressed: bool) -> Shortcut: var button_shortcut: Shortcut = Shortcut.new() var button_event : InputEventKey = InputEventKey.new() - button_event.keycode = scancode + 49 + button_event.keycode = KEY_1 + index button_event.alt_pressed = true button_event.ctrl_pressed = ctrl_pressed button_shortcut.events.append(button_event) return button_shortcut func add_point() -> void: - Settings.player_array[id].add_point() + player_object.add_point() update_score() func minus_point() -> void: - Settings.player_array[id].minus_point() + player_object.minus_point() update_score() func change_character() -> void: @@ -58,22 +62,22 @@ func change_character() -> void: func character_selected(file_name: String) -> void: print("Back in player list with: " + file_name) - Settings.player_array[id].character = load("res://characters/" + file_name) + player_object.character = load("res://characters/" + file_name) set_player_character() func new_character_selected(character_texture: ImageTexture) -> void: print("Back in player list with new") - Settings.player_array[id].character = character_texture + player_object.character = character_texture set_player_character() func set_player_character() -> void: character.custom_minimum_size = Vector2(80, 40) character.ignore_texture_size = true character.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED - character.texture_normal = Settings.player_array[id].character + character.texture_normal = player_object.character func update_score() -> void: if OS.has_feature("debug"): - player_name_field.text = str(Settings.player_array[id].id) + " " + Settings.player_array[id].player_name + ": " + str(Settings.player_array[id].player_score) + player_name_field.text = str(list_index + 1) + " " + player_object.player_name + ": " + str(player_object.player_score) else: - player_name_field.text = Settings.player_array[id].player_name + ": " + str(Settings.player_array[id].player_score) + player_name_field.text = player_object.player_name + ": " + str(player_object.player_score) diff --git a/PlayerObject.gd b/PlayerObject.gd index 7ff496c..2267b00 100644 --- a/PlayerObject.gd +++ b/PlayerObject.gd @@ -1,4 +1,4 @@ -extends Node +extends RefCounted class_name PlayerObject signal first_point_triggerd @@ -8,8 +8,8 @@ signal point_given_sound signal player_point_given signal player_point_taken -@export -var id: int +## Unique id that never changes, unlike the player's position in Settings.player_array. +var id: String = _generate_uuid() @export var player_name: String @@ -21,11 +21,18 @@ var is_first_point: bool = true var character: Texture = load("res://noCharacter.png") -func _init(new_name: String, new_id: int) -> void: +func _init(new_name: String) -> void: player_name = new_name - id = new_id player_score = 0 +## Returns a random version 4 UUID, e.g. "3f2b8c1e-9a4d-4e7f-b2c6-5d8e1f0a7b93". +static func _generate_uuid() -> String: + var bytes: PackedByteArray = Crypto.new().generate_random_bytes(16) + bytes[6] = (bytes[6] & 0x0f) | 0x40 + bytes[8] = (bytes[8] & 0x3f) | 0x80 + var hex: String = bytes.hex_encode() + return "%s-%s-%s-%s-%s" % [hex.substr(0, 8), hex.substr(8, 4), hex.substr(12, 4), hex.substr(16, 4), hex.substr(20, 12)] + func add_point() -> void: if player_score < Settings.winning_score: player_score += 1 diff --git a/SearchWindow.gd b/SearchWindow.gd index 497307c..6de885b 100644 --- a/SearchWindow.gd +++ b/SearchWindow.gd @@ -66,12 +66,18 @@ func perform_search(search_text: String) -> void: child.visible = false label_pool.append(child) + for soundtrack: String in find_matches(search_text): + add_soundtrack_reuse(soundtrack) + +## Returns the soundtracks matching [param search_text], best matches first: plain +## case-insensitive matches, then matches ignoring spaces and special characters, +## then matches with the letters in order but not next to each other. +func find_matches(search_text: String) -> Array[String]: var seen: Dictionary = {} var search_text_lower: String = search_text.to_lower() var clean_search_text: String = clean_term(search_text) - # Collect all matches first, then display in order - var matches: Array = [] + var matches: Array[String] = [] # Pass 1: Exact case-insensitive contains match on lowercase versions for i in range(precomputed_soundtracks.size()): @@ -98,9 +104,7 @@ func perform_search(search_text: String) -> void: seen[st_data["original"]] = true matches.append(st_data["original"]) - # Display all matches in the order they appear in soundtracks - for soundtrack: String in matches: - add_soundtrack_reuse(soundtrack) + return matches # Cleans a term by removing spaces and replacing special characters. # Matches MusicServer's clean_term() function. @@ -113,6 +117,9 @@ func compile_regex_cached(search_term: String) -> RegEx: var regex = RegEx.new() var regText: String = ".*" for letter: String in search_term: + # Escape regex metacharacters so searches like "Q*bert" or "(Remix)" work + if "\\^$.|?*+()[]{}".contains(letter): + regText += "\\" regText += letter + ".*" regex.compile(regText) regex_cache[search_term] = regex @@ -149,26 +156,28 @@ func get_list_of_soundtracks() -> void: print("get_list_of_soundtracks") var handle_soundtracks: Callable = func handle_soundtracks(array) -> void: if typeof(array) == TYPE_ARRAY: - soundtracks = [] - precomputed_soundtracks = [] # Reuse existing labels: hide all and add to pool for child: Label in search_list.get_children(): child.visible = false label_pool.append(child) - - soundtracks.append_array(array) - # Precompute cleaned versions for all soundtracks + set_soundtracks(array) for soundtrack: String in soundtracks: - var cleaned = clean_term(soundtrack) - precomputed_soundtracks.append({ - "original": soundtrack, - "lowercase": soundtrack.to_lower(), - "cleaned": cleaned - }) add_soundtrack_reuse(soundtrack) else: print("Unexpected data") - Settings.make_request2("/music/all/order", handle_soundtracks, true) + MusicServerClient.fetch_all_soundtracks_ordered(handle_soundtracks) + +## Replaces the searchable soundtracks and precomputes their search forms. +func set_soundtracks(list: Array) -> void: + soundtracks = [] + precomputed_soundtracks = [] + soundtracks.append_array(list) + for soundtrack: String in soundtracks: + precomputed_soundtracks.append({ + "original": soundtrack, + "lowercase": soundtrack.to_lower(), + "cleaned": clean_term(soundtrack) + }) func _input(event: InputEvent) -> void: if self.visible == true: diff --git a/Settings.gd b/Settings.gd index 8b28d29..3a98896 100644 --- a/Settings.gd +++ b/Settings.gd @@ -30,66 +30,6 @@ var character_select_open: bool = false var version: String = "1.9.0" -func make_request2(address: String, func_name: Callable, expect_data: bool) -> void: - var error_handling: Callable = func(_result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void: - print("request done to address: ", default_path + address + " response_code: ", response_code) - if response_code == 200: - print("func_name: ", func_name.get_method()) - if !expect_data: - func_name.call() - else: - var json: JSON = JSON.new() - var error: int = json.parse(body.get_string_from_utf8()) - if error == OK: - var data_received = json.get_data() - print("data_received type: ", type_string(typeof(data_received))) - if typeof(data_received) == TYPE_ARRAY: - func_name.call(data_received) - elif func_name != null: - func_name.call(data_received) - else: - print("data_received type: ", type_string(typeof(body))) - func_name.call(body) - - var http_request: HTTPRequest = HTTPRequest.new() - add_child(http_request) - http_request.request_completed.connect(error_handling) - - # Perform a GET request. The URL below returns JSON as of writing. - print("address: ", default_path + address) - var request_error: int = http_request.request(default_path + address) - if request_error != OK: - push_error("An error occurred in the HTTP request.") - -func make_request3(address: String) -> void: - var error_handling: Callable = func(_result: int, response_code: int, _headers: PackedStringArray, _body: PackedByteArray) -> void: - print("request done to address: ", default_path + address + " response_code: ", response_code) - if response_code != 200: - print("Error: " + str(response_code)) - - var http_request: HTTPRequest = HTTPRequest.new() - add_child(http_request) - http_request.request_completed.connect(error_handling) - # Perform a GET request. The URL below returns JSON as of writing. - var request_error: int = http_request.request(default_path + address) - if request_error != OK: - push_error("An error occurred in the HTTP request.") - -func make_put_request(address: String) -> void: - var error_handling: Callable = func(_result: int, response_code: int, _headers: PackedStringArray, _body: PackedByteArray) -> void: - print("request done to address: ", default_path + address + " response_code: ", response_code) - if response_code != 200: - print("Error: " + str(response_code)) - - var http_request: HTTPRequest = HTTPRequest.new() - add_child(http_request) - http_request.request_completed.connect(error_handling) - # Perform a GET request. The URL below returns JSON as of writing. - var headers: PackedStringArray = ["Content-Type: application/json"] - var request_error: int = http_request.request(default_path + address, headers, HTTPClient.METHOD_PUT) - if request_error != OK: - push_error("An error occurred in the HTTP request.") - func delete_children(node: Node) -> void: for n: Node in node.get_children(): node.remove_child(n) diff --git a/SongObject.gd b/SongObject.gd index 69b41bf..48d9290 100644 --- a/SongObject.gd +++ b/SongObject.gd @@ -1,4 +1,4 @@ -extends Node +extends RefCounted class_name SongObject @@ -8,7 +8,8 @@ var song_title: String var soundtrack_title: String -var players_given_point: Array[int] = [] +## Ids of the players who got a point for this song. +var players_given_point: Array[String] = [] var is_answered: bool = false @@ -40,16 +41,14 @@ func get_song_info() -> String: else: return "??? - ???" -func add_point(id: int) -> void: +func add_point(player_id: String) -> void: print("add_point") - players_given_point.append(id) - players_given_point.sort() + if !players_given_point.has(player_id): + players_given_point.append(player_id) -func remove_point(id: int) -> void: +func remove_point(player_id: String) -> void: print("remove_point") - if players_given_point.has(id): - players_given_point.remove_at(players_given_point.find(id)) - players_given_point.sort() + players_given_point.erase(player_id) func _to_string() -> String: var format_string: String = "song_number: %d | soundtrack_title: %s | song_title: %s | is_answered: %s | has_played: %s | is_playing: %s" diff --git a/character.gd b/character.gd index 3cde46b..3de4ac0 100644 --- a/character.gd +++ b/character.gd @@ -35,7 +35,7 @@ func set_character_name(character_name: String) -> void: character_image.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL character_image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED character_image.texture = new_image - Settings.make_request2("/character?name=" + character_name.uri_encode(), image_fetched, true) + MusicServerClient.fetch_character_image(character_name, image_fetched) func _input(event: InputEvent) -> void: if self.visible == true && Settings.character_select_open == true: diff --git a/http_transport.gd b/http_transport.gd new file mode 100644 index 0000000..d63be82 --- /dev/null +++ b/http_transport.gd @@ -0,0 +1,37 @@ +class_name HttpTransport +extends RefCounted +## Sends HTTP requests for [code]MusicServerClient[/code] using [HTTPRequest] nodes. +## +## Tests subclass this to answer requests without a network connection. + +var _owner: Node + + +func _init(owner: Node) -> void: + _owner = owner + + +## Requests [param url] and calls [param on_complete] with +## [code](response_code: int, body: PackedByteArray)[/code]. Connection errors are +## reported with response code 0. +func request(url: String, method: int, on_complete: Callable) -> void: + var http_request: HTTPRequest = HTTPRequest.new() + _owner.add_child(http_request) + http_request.request_completed.connect( + func( + result: int, + response_code: int, + _headers: PackedStringArray, + body: PackedByteArray, + ) -> void: + http_request.queue_free() + on_complete.call(response_code if result == HTTPRequest.RESULT_SUCCESS else 0, body) + ) + var headers: PackedStringArray = [] + if method == HTTPClient.METHOD_PUT: + headers.append("Content-Type: application/json") + var error: int = http_request.request(url, headers, method) + if error != OK: + http_request.queue_free() + push_error("Could not send request to %s (error %d)" % [url, error]) + on_complete.call(0, PackedByteArray()) diff --git a/http_transport.gd.uid b/http_transport.gd.uid new file mode 100644 index 0000000..13bf9e1 --- /dev/null +++ b/http_transport.gd.uid @@ -0,0 +1 @@ +uid://uinuvqrxeppl diff --git a/music_server_client.gd b/music_server_client.gd new file mode 100644 index 0000000..bee7efa --- /dev/null +++ b/music_server_client.gd @@ -0,0 +1,124 @@ +extends Node +## Client for the MusicServer REST API. +## +## Registered as the [code]MusicServerClient[/code] autoload, so every script can call it +## directly, e.g. [code]MusicServerClient.fetch_song_list(on_done)[/code]. All HTTP traffic +## goes through [member transport]; tests replace it with a fake to answer requests +## without a running server. + +## Sends the actual HTTP requests. Swap it out in tests. +var transport: HttpTransport + + +func _ready() -> void: + if transport == null: + transport = HttpTransport.new(self) + + +## Fetches the songs already in the server playlist, as an Array of +## [code]{SongNo, Soundtrack, Song}[/code] dictionaries. +func fetch_song_list(on_done: Callable) -> void: + get_json("/music/list", on_done) + + +## Downloads a random song as MP3 bytes. With [param low_played] the server prefers +## soundtracks that have been played the least. +func fetch_random_song(low_played: bool, on_done: Callable) -> void: + get_bytes("/music/rand/low" if low_played else "/music/rand", on_done) + + +## Adds the most recently fetched random song to the server playlist. +func add_to_queue(on_done: Callable) -> void: + send("/music/addQue", on_done) + + +## Fetches [code]{SongNo, Soundtrack, Song}[/code] for the most recently queued song. +func fetch_song_info(on_done: Callable) -> void: + get_json("/music/info", on_done) + + +## Downloads a song from the server playlist as MP3 bytes. +func fetch_song(song_no: int, on_done: Callable) -> void: + get_bytes("/music?song=" + str(song_no), on_done) + + +## Downloads the sound test song as MP3 bytes. +func fetch_sound_test_song(on_done: Callable) -> void: + get_bytes("/music/soundTest", on_done) + + +## Clears the server playlist. +func reset_playlist(on_done: Callable) -> void: + send("/music/reset", on_done) + + +## Fetches every soundtrack name as an Array of Strings. +func fetch_all_soundtracks(on_done: Callable) -> void: + get_json("/music/all", on_done) + + +## Fetches every soundtrack name, sorted, as an Array of Strings. +func fetch_all_soundtracks_ordered(on_done: Callable) -> void: + get_json("/music/all/order", on_done) + + +## Records in the server statistics that a song has been played. +func mark_song_played(song_no: int) -> void: + send("/music/played?song=" + str(song_no), Callable(), HTTPClient.METHOD_PUT) + + +## Fetches the names of all character images. +func fetch_character_list(on_done: Callable) -> void: + get_json("/characters", on_done) + + +## Downloads a character image as PNG bytes. +func fetch_character_image(character_name: String, on_done: Callable) -> void: + get_bytes("/character?name=" + character_name.uri_encode(), on_done) + + +## Starts syncing the server's soundtrack library with its music folder. +func start_sync(on_done: Callable) -> void: + send("/sync", on_done) + + +## Fetches the sync state: [code]{progress, time_spent}[/code] while running, or the +## sync result once finished. +func fetch_sync_progress(on_done: Callable) -> void: + get_json("/sync/progress", on_done) + + +## Requests [param path] and calls [param on_done] with the parsed JSON. +## Nothing is called if the request fails or the body is not valid JSON. +func get_json(path: String, on_done: Callable) -> void: + _request(path, HTTPClient.METHOD_GET, func(body: PackedByteArray) -> void: + var json: JSON = JSON.new() + if json.parse(body.get_string_from_utf8()) != OK: + push_warning("Invalid JSON from %s: %s" % [path, json.get_error_message()]) + return + on_done.call(json.data) + ) + + +## Requests [param path] and calls [param on_done] with the raw response body. +func get_bytes(path: String, on_done: Callable) -> void: + _request(path, HTTPClient.METHOD_GET, on_done) + + +## Requests [param path] and calls [param on_done] with no arguments when it succeeds. +## [param on_done] may be an empty Callable when the result does not matter. +func send(path: String, on_done: Callable, method: int = HTTPClient.METHOD_GET) -> void: + _request(path, method, func(_body: PackedByteArray) -> void: + if on_done.is_valid(): + on_done.call() + ) + + +func _request(path: String, method: int, on_success: Callable) -> void: + var url: String = Settings.default_path + path + transport.request(url, method, func(response_code: int, body: PackedByteArray) -> void: + if response_code != 200: + push_warning("Request to %s failed with response code %d" % [url, response_code]) + return + on_success.call(body) + ) diff --git a/music_server_client.gd.uid b/music_server_client.gd.uid new file mode 100644 index 0000000..f382d06 --- /dev/null +++ b/music_server_client.gd.uid @@ -0,0 +1 @@ +uid://cbb0fpkj7k38w diff --git a/new_character_select.gd b/new_character_select.gd index a7cbe94..e3ac9a0 100644 --- a/new_character_select.gd +++ b/new_character_select.gd @@ -45,7 +45,7 @@ func load_characters() -> void: character_box.size = Vector2(235, 145) character_box.set_character_name(character_name) character_box.connect("new_character_selected", _on_character_selected) - Settings.make_request2("/characters", fetch_character_list, true) + MusicServerClient.fetch_character_list(fetch_character_list) func show_grid(player_name: String) -> void: print("show_grid") diff --git a/playlist.gd b/playlist.gd index 735829d..13b459a 100644 --- a/playlist.gd +++ b/playlist.gd @@ -40,7 +40,7 @@ func song_is_answered(song_no: int = currently_playing_song) -> void: song_object_array[song_no].is_answered = true if Settings.add_to_stats: print("add to stats: ", song_no) - Settings.make_put_request("/music/played?song="+str(song_no)) + MusicServerClient.mark_song_played(song_no) func add_to_playlist(new_song_object: SongObject) -> void: song_object_array.append(new_song_object) @@ -76,15 +76,16 @@ func get_current_soundtrack_title() -> String: func get_current_song_title() -> String: return song_object_array[currently_playing_song].get_song_title() -func add_point(player_id: int) -> void: +func add_point(player_id: String) -> void: song_object_array[currently_playing_song].add_point(player_id) -func remove_point(player_id: int) -> void: +func remove_point(player_id: String) -> void: song_object_array[currently_playing_song].remove_point(player_id) -func remove_player_from_list(player_to_remove: int) -> void: - for song_list_object: SongObject in song_object_array: - song_object_array[song_list_object.song_number].players_given_point.remove_at(song_object_array[song_list_object.song_number].players_given_point.find(player_to_remove)) +## Removes a player's points from every song. +func remove_player_from_list(player_id: String) -> void: + for song_object: SongObject in song_object_array: + song_object.remove_point(player_id) func get_song(song_no: int) -> AudioStream: return song_object_array[song_no].song diff --git a/project.godot b/project.godot index 32930b2..dea3cc7 100644 --- a/project.godot +++ b/project.godot @@ -23,6 +23,7 @@ config/icon="res://icon.svg" Settings="*res://Settings.gd" Playlist="*res://playlist.gd" +MusicServerClient="*res://music_server_client.gd" [debug] diff --git a/song_list_object.gd b/song_list_object.gd index fa29cce..7966f07 100644 --- a/song_list_object.gd +++ b/song_list_object.gd @@ -39,12 +39,12 @@ func set_label() -> void: func update_players() -> void: Settings.delete_children(song_list_players) if song_object != null: - for player_id: int in song_object.players_given_point: - for player: PlayerObject in Settings.player_array: - if player.id == player_id: - var texture: TextureRect = TextureRect.new() - texture.texture = player.character - texture.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL - texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED - texture.custom_minimum_size = Vector2(50, 25) - song_list_players.add_child(texture) + # Show players in the same order as the player list + for player: PlayerObject in Settings.player_array: + if song_object.players_given_point.has(player.id): + var texture: TextureRect = TextureRect.new() + texture.texture = player.character + texture.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL + texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + texture.custom_minimum_size = Vector2(50, 25) + song_list_players.add_child(texture) diff --git a/sync_window.gd b/sync_window.gd index 382d3f4..f71784b 100644 --- a/sync_window.gd +++ b/sync_window.gd @@ -105,7 +105,7 @@ func start_sync() -> void: sync_started.emit() show_sync_progress() if !Settings.currently_syncing: - Settings.make_request2("/sync", sync_request_sent, false) + MusicServerClient.start_sync(sync_request_sent) func _on_sync_timeout() -> void: print("_on_sync_timeout!") @@ -118,7 +118,7 @@ func _on_sync_timeout() -> void: sync_time.text = data_received.time_spent if data_received.has("time_spent") else "" else: on_sync_finished(data_received) - Settings.make_request2("/sync/progress", get_progress, true) + MusicServerClient.fetch_sync_progress(get_progress) func on_sync_finished(data_received: Dictionary) -> void: has_synced = true diff --git a/winner_window.gd b/winner_window.gd index 029c3e8..7e9a1c2 100644 --- a/winner_window.gd +++ b/winner_window.gd @@ -17,10 +17,9 @@ func _ready() -> void: visible = false -func show_winner(winning_player_id: int) -> void: +func show_winner(winner: PlayerObject) -> void: visible = true # Set winner info - var winner: PlayerObject = Settings.player_array[winning_player_id] winner_label.text = winner.player_name + " won with " + str(winner.player_score) + \ " points!!" winner_picture.texture = winner.character @@ -41,7 +40,7 @@ func show_winner(winning_player_id: int) -> void: var placing: int = 2 for player: PlayerObject in sorted_players: - if player.id == winning_player_id: + if player == winner: continue # Skip the winner, already shown above var label: Label = Label.new() label.text = str(placing) + ". " + player.player_name + " - " + \