diff --git a/src/data/sources/itch/ButlerClient.vala b/src/data/sources/itch/ButlerClient.vala index 2ddfbc53..b7ee2716 100644 --- a/src/data/sources/itch/ButlerClient.vala +++ b/src/data/sources/itch/ButlerClient.vala @@ -28,7 +28,7 @@ namespace GameHub.Data.Sources.Itch * with Content-Length headers, while butler daemon expects one message * per line, deliminated by LF */ - public class ButlerClient : Object, ForwardsServerMessage + public class ButlerClient: Object, ServerMessageListener, ServerMessageRelay { private SocketConnection socket_connection; private int message_id = 0; @@ -40,7 +40,7 @@ namespace GameHub.Data.Sources.Itch this.socket_connection = socket_connection; sender = new Sender(socket_connection.output_stream); receiver = new Receiver(socket_connection.input_stream, sender); - forward_server_messages_from(receiver); + relay_messages(receiver); } public async Json.Object? call(string method, Json.Node? params=null, out Json.Object? error = null) @@ -84,12 +84,12 @@ namespace GameHub.Data.Sources.Itch } } - public void respond(int message_id, Json.Node? result=null) + public void respond(int message_id, string? result_json=null) { var request = Parser.json(j => j .set_member_name("jsonrpc").add_string_value("2.0") .set_member_name("id").add_int_value(message_id) - .set_member_name("result").add_value(result ?? Parser.json()) + .set_member_name("result").add_value(Parser.parse_json(result_json)) ); try @@ -109,7 +109,7 @@ namespace GameHub.Data.Sources.Itch } } - private class Receiver : Object, ForwardsServerMessage + private class Receiver: Object, ServerMessageListener { private DataInputStream stream; private Sender sender; @@ -149,30 +149,28 @@ namespace GameHub.Data.Sources.Itch if(error_info != null && message_id != NO_ID) { // failure response - responses.set(message_id, {error_info, false}); warning("[ButlerClient: err %d] %s", message_id, json); + responses.set(message_id, {error_info, false}); } else if(result != null && message_id != NO_ID) { // success response - responses.set(message_id, {result, true}); if(Application.log_verbose) { debug("[ButlerClient: res %d] %s", message_id, json); } + responses.set(message_id, {result, true}); } else if(message_id != NO_ID) { // server request - server_call_received(method, params, (result) => { - sender.respond(message_id, result); - }); - warning("[ButlerClient: srv %d] %s", message_id, json); + debug("[ButlerClient: srv %d] %s", message_id, json); + sender.respond(message_id, server_call(method, params)); } else if(params != null && method != null) { // notification - notification_received(method, params); + notification(method, params); switch(method.down()) { @@ -248,21 +246,18 @@ namespace GameHub.Data.Sources.Itch } } - public interface ForwardsServerMessage: Object + public interface ServerMessageListener: Object { - public signal void notification_received(string method, Json.Object params); - - public delegate void ServerMessageResponder(Json.Node? result=null); - public signal void server_call_received(string method, Json.Object params, ServerMessageResponder respond); + public signal void notification(string method, Json.Object? args); + public signal string? server_call(string method, Json.Object? args); + } - public void forward_server_messages_from(ForwardsServerMessage source) + public interface ServerMessageRelay: ServerMessageListener + { + public void relay_messages(ServerMessageListener source) { - source.notification_received.connect((s, method, @params) => - notification_received(method, params) - ); - source.server_call_received.connect((s, method, @params, respond) => - server_call_received(method, params, respond) - ); + source.notification.connect((method, args) => notification(method, args)); + source.server_call.connect((method, args) => server_call(method, args)); } } } diff --git a/src/data/sources/itch/ButlerConnection.vala b/src/data/sources/itch/ButlerConnection.vala index 6adb8dcb..38dde111 100644 --- a/src/data/sources/itch/ButlerConnection.vala +++ b/src/data/sources/itch/ButlerConnection.vala @@ -22,7 +22,7 @@ using GameHub.Utils; namespace GameHub.Data.Sources.Itch { - public class ButlerConnection: Object, ForwardsServerMessage + public class ButlerConnection: Object, ServerMessageListener, ServerMessageRelay { private ButlerClient client; @@ -31,7 +31,7 @@ namespace GameHub.Data.Sources.Itch try { client = new ButlerClient(yield (new SocketClient().connect_to_host_async(address, 0, null))); - forward_server_messages_from(client); + relay_messages(client); var res = yield client.call("Meta.Authenticate", Parser.json(j => j.set_member_name("secret").add_string_value(secret))); return res != null && res.has_member("ok") && res.get_boolean_member("ok"); @@ -79,7 +79,7 @@ namespace GameHub.Data.Sources.Itch return items; } - public async HashMap> get_caves(int? game_id=null, out ArrayList installed_games=null) + public async HashMap> get_caves(int? game_id=null, out ArrayList installed_games=null) { var result = yield client.call("Fetch.Caves", Parser.json(j => { if(game_id != null) @@ -89,7 +89,7 @@ namespace GameHub.Data.Sources.Itch .end_object(); } })); - var caves = new HashMap>(); + var caves = new HashMap>(); var installed = new ArrayList(); var arr = result.has_member("items") ? result.get_array_member("items") : null; @@ -101,19 +101,22 @@ namespace GameHub.Data.Sources.Itch var game = cave.get_member("game"); var cave_game_id = (int) game.get_object().get_int_member("id"); + var install_info = cave.has_member("installInfo") ? cave.get_object_member("installInfo") : null; + var install_dir = install_info != null && install_info.has_member("installFolder") ? install_info.get_string_member("installFolder") : null; + installed.add(game); - ArrayList caves_for_game; + ArrayList caves_for_game; if(caves.has_key(cave_game_id)) { caves_for_game = caves.get(cave_game_id); } else { - caves_for_game = new ArrayList(); + caves_for_game = new ArrayList(); caves.set(cave_game_id, caves_for_game); } - caves_for_game.add(cave_id); + caves_for_game.add(new Cave(cave_id, install_dir)); }); } @@ -121,6 +124,16 @@ namespace GameHub.Data.Sources.Itch return caves; } + public async Cave? get_cave(string cave_id) + { + var result = yield client.call("Fetch.Cave", Parser.json(j => j + .set_member_name("caveId").add_string_value(cave_id) + )); + var install_info = Parser.json_nested_object(result, {"cave", "installInfo"}); + var install_dir = install_info != null && install_info.has_member("installFolder") ? install_info.get_string_member("installFolder") : null; + return install_dir != null ? new Cave(cave_id, install_dir) : null; + } + public async ArrayList? get_game_uploads(int game_id) { var result = yield client.call("Game.FindUploads", Parser.json(j => j @@ -208,4 +221,15 @@ namespace GameHub.Data.Sources.Itch )); } } + + public class Cave + { + public string id; + public string? install_dir; + public Cave(string id, string? install_dir) + { + this.id = id; + this.install_dir = install_dir; + } + } } diff --git a/src/data/sources/itch/Itch.vala b/src/data/sources/itch/Itch.vala index 4f902a1e..f2ae3308 100644 --- a/src/data/sources/itch/Itch.vala +++ b/src/data/sources/itch/Itch.vala @@ -207,7 +207,7 @@ namespace GameHub.Data.Sources.Itch foreach(var g in _games) { - ((ItchGame) g).update_caves(caves); + yield update_game_state((ItchGame) g, caves); } } @@ -224,39 +224,42 @@ namespace GameHub.Data.Sources.Itch public async void uninstall_game(ItchGame game) { - yield butler_connection.uninstall(game.get_cave()); + yield butler_connection.uninstall(game.cave_id); yield update_game_state(game); } - public async void update_game_state(ItchGame game) + public async void update_game_state(ItchGame game, HashMap>? caves_map=null) { - game.update_caves(yield butler_connection.get_caves(game.int_id)); + caves_map = caves_map ?? yield butler_connection.get_caves(game.int_id); + game.update_caves(caves_map); } public async void run_game(ItchGame game) { var connection = yield butler_daemon.create_connection(); - connection.server_call_received.connect((method, @params, respond) => { - string? pathUrl = null; - if(method == "ShellLaunch") + connection.server_call.connect((method, args) => { + File? file = null; + switch(method) { - pathUrl = params.get_string_member("itemPath"); - } - else if(method == "HTMLLaunch") - { - pathUrl = params.get_string_member("rootFolder") + "/" + params.get_string_member("indexPath"); - } - else if(method == "URLLaunch") - { - pathUrl = params.get_string_member("url"); + case "ShellLaunch": + file = FSUtils.file(args.get_string_member("itemPath")); + break; + + case "HTMLLaunch": + file = FSUtils.file(args.get_string_member("rootFolder"), args.get_string_member("indexPath")); + break; + + case "URLLaunch": + file = File.new_for_uri(args.get_string_member("url")); + break; } - if(pathUrl != null) + if(file != null) { - run_async({"xdg-open", pathUrl}); - respond(); + Utils.open_uri(file.get_uri()); } + return null; }); - yield connection.run(game.get_cave()); + yield connection.run(game.cave_id); } private async void butler_connect() diff --git a/src/data/sources/itch/ItchDownloader.vala b/src/data/sources/itch/ItchDownloader.vala index 4809f3dd..7476c76e 100644 --- a/src/data/sources/itch/ItchDownloader.vala +++ b/src/data/sources/itch/ItchDownloader.vala @@ -123,7 +123,7 @@ namespace GameHub.Data.Sources.Itch status = new Status(Download.State.STARTING); - butler_connection.notification_received.connect((s, method, @params) => { + butler_connection.notification.connect((s, method, @params) => { switch(method) { case "TaskStarted": diff --git a/src/data/sources/itch/ItchGame.vala b/src/data/sources/itch/ItchGame.vala index 8908d66e..eb626543 100644 --- a/src/data/sources/itch/ItchGame.vala +++ b/src/data/sources/itch/ItchGame.vala @@ -36,6 +36,7 @@ namespace GameHub.Data.Sources.Itch id = json_obj.get_int_member("id").to_string(); name = json_obj.get_string_member("title"); icon = json_obj.has_member("stillCoverUrl") ? json_obj.get_string_member("stillCoverUrl") : json_obj.get_string_member("coverUrl"); + description = json_obj.get_string_member("shortText"); store_page = json_obj.get_string_member("url"); image = icon; @@ -54,7 +55,6 @@ namespace GameHub.Data.Sources.Itch platforms.add(Platform.MACOS); } - description = json_obj.get_string_member("shortText"); info = Json.to_string(json_node, false); @@ -111,10 +111,8 @@ namespace GameHub.Data.Sources.Itch if(info_root != null && info_root.get_node_type() == Json.NodeType.OBJECT) { var info_root_obj = info_root.get_object(); - if(info_root_obj.has_member("shortText")) - { - description = info_root_obj.get_string_member("shortText"); - } + description = info_root_obj.get_string_member("shortText"); + store_page = info_root_obj.get_string_member("url"); } update_status(); @@ -125,8 +123,8 @@ namespace GameHub.Data.Sources.Itch update_status(); } - private ArrayList caves = new ArrayList(); - public void update_caves(HashMap> caves_map) + private ArrayList caves = new ArrayList(); + public void update_caves(HashMap> caves_map) { if(caves_map.has_key(int_id)) { @@ -136,16 +134,35 @@ namespace GameHub.Data.Sources.Itch { caves.clear(); } + + var cave = this.cave; + if(cave != null) + { + install_dir = FSUtils.file(cave.install_dir); + } + update_status(); } - public string? get_cave() + public Cave? cave { - if(caves.size > 0) + owned get + { + if(caves.size > 0) + { + return caves.first(); + } + return null; + } + } + + public string? cave_id + { + get { - return caves.first(); + var cave = this.cave; + return cave != null ? cave.id : null; } - return null; } public override void update_status() diff --git a/src/utils/Utils.vala b/src/utils/Utils.vala index 0e1fbfdb..cbd9b7c9 100644 --- a/src/utils/Utils.vala +++ b/src/utils/Utils.vala @@ -57,7 +57,7 @@ namespace GameHub.Utils } catch(Error e) { - warning(e.message); + warning("[Utils.open_uri] Error while opening '%s': %s", uri, e.message); } }