From a7e1378f5594a7abfbda4bef340f06665287a4ba Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Wed, 7 Oct 2026 22:54:42 -0500 Subject: [PATCH] Application updating and repair is now possible, save news and applications listing to cache directory instead of current working directory, added support for application unlinking, misc. other changes --- Gemfile | 2 +- Gemfile.lock | 65 ++++++++++++----- lib/application_helper.rb | 4 ++ lib/dialogs/import_application.rb | 2 +- lib/pages/boot/start_up.rb | 2 +- lib/pages/games.rb | 86 ++++++++++++++++------ lib/states/interface.rb | 4 +- lib/theme.rb | 13 ++++ lib/worker.rb | 94 ++++++++++++++++++++----- lib/worker/api.rb | 5 ++ lib/worker/api/application.rb | 4 ++ lib/worker/api/settings.rb | 16 +++++ lib/worker/task.rb | 26 ++++++- lib/worker/tasks/install_application.rb | 1 + lib/worker/tasks/repair_application.rb | 11 +++ lib/worker/tasks/update_application.rb | 11 +++ w3d_hub_linux_launcher.rb | 3 + 17 files changed, 285 insertions(+), 64 deletions(-) diff --git a/Gemfile b/Gemfile index 0d91535..53782b2 100644 --- a/Gemfile +++ b/Gemfile @@ -9,7 +9,7 @@ gem "json" # "game" library gem gem "cyberarm_engine" gem "sdl3-bindings" -gem "gosu", '1.5.0' # temporary, manually built, stand-in for SDL3 supporting gosu +gem "gosu", '1.5.0', path: "../gosu" # temporary, manually built, stand-in for SDL3 supporting gosu # networking libs gem "async" diff --git a/Gemfile.lock b/Gemfile.lock index 4ccc712..f433342 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,8 @@ +PATH + remote: ../gosu + specs: + gosu (1.5.0) + GEM remote: https://gem.coop/ specs: @@ -27,20 +32,28 @@ GEM fiber-annotation fiber-local (~> 1.1) json - cyberarm_engine (0.25.1) + cyberarm_engine (0.30.0) gosu (~> 1.1) digest-crc (0.7.0) rake (>= 12.0.0, < 14.0.0) + ffi (1.17.4) + ffi (1.17.4-aarch64-linux-gnu) + ffi (1.17.4-aarch64-linux-musl) + ffi (1.17.4-arm-linux-gnu) + ffi (1.17.4-arm-linux-musl) + ffi (1.17.4-arm64-darwin) + ffi (1.17.4-x86-linux-gnu) + ffi (1.17.4-x86-linux-musl) + ffi (1.17.4-x86_64-darwin) ffi (1.17.4-x86_64-linux-gnu) + ffi (1.17.4-x86_64-linux-musl) fiber-annotation (0.2.0) fiber-local (1.1.0) fiber-storage fiber-storage (1.0.1) - gosu (1.5.0-x86_64-linux) - ffi (~> 1.12) - io-endpoint (0.18.0) + io-endpoint (0.19.0) openssl (>= 3.3.0) - io-event (1.22.0) + io-event (1.22.1) io-stream (0.14.0) openssl (>= 3.3) ircparser (1.0.0) @@ -51,7 +64,7 @@ GEM protocol-http (0.72.0) protocol-http1 (0.41.0) protocol-http (~> 0.68) - protocol-http2 (0.28.0) + protocol-http2 (0.29.1) protocol-hpack (~> 1.4) protocol-http (~> 0.62) protocol-rack (0.23.0) @@ -65,12 +78,22 @@ GEM rack (3.2.7) rake (13.4.2) rexml (3.4.4) - rubyzip (3.6.0) + rubyzip (3.7.0) sdl3-bindings (1.0.4) ffi (~> 1.16) PLATFORMS - x86_64-linux + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + ruby + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES async @@ -80,7 +103,7 @@ DEPENDENCIES chunky_png cyberarm_engine digest-crc - gosu (= 1.5.0) + gosu (= 1.5.0)! ircparser json logger @@ -96,15 +119,25 @@ CHECKSUMS base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b chunky_png (1.4.0) sha256=89d5b31b55c0cf4da3cf89a2b4ebc3178d8abe8cbaf116a1dba95668502fdcfe console (1.38.0) sha256=35b82f59130e95e9be6b832d9e539c8b3988fc192e4a8e9a86ab4922f44ad5de - cyberarm_engine (0.25.1) sha256=6f851a2ed0a32bdd25fc619172b546a8e26eefeb13a62c509f226337170d0efe + cyberarm_engine (0.30.0) sha256=f67d7410c78ad69c865400f9ea23abae74fdf5f9dadae19006cc4fd706064b0f digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07 + ffi (1.17.4) sha256=bcd1642e06f0d16fc9e09ac6d49c3a7298b9789bcb58127302f934e437d60acf + ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df + ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39 + ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564 + ffi (1.17.4-arm-linux-musl) sha256=9d4838ded0465bef6e2426935f6bcc93134b6616785a84ffd2a3d82bc3cf6f95 + ffi (1.17.4-arm64-darwin) sha256=19071aaf1419251b0a46852abf960e77330a3b334d13a4ab51d58b31a937001b + ffi (1.17.4-x86-linux-gnu) sha256=38e150df5f4ca555e25beca4090823ae09657bceded154e3c52f8631c1ed72cf + ffi (1.17.4-x86-linux-musl) sha256=fbeec0fc7c795bcf86f623bb18d31ea1820f7bd580e1703a3d3740d527437809 + ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496 ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d + ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e fiber-annotation (0.2.0) sha256=7abfadf1d119f508867d4103bf231c0354d019cc39a5738945dec2edadaf6c03 fiber-local (1.1.0) sha256=c885f94f210fb9b05737de65d511136ea602e00c5105953748aa0f8793489f06 fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1 - gosu (1.5.0-x86_64-linux) - io-endpoint (0.18.0) sha256=720d186bcd584e8ba490bfc91dab332cc3c5ffee848b5b03894555cb275151aa - io-event (1.22.0) sha256=4936d38dfcf2291d897707fd94690f1a63fdb1ea3ab4fa63c6739fc6c61e1534 + gosu (1.5.0) + io-endpoint (0.19.0) sha256=547aaefe9a78bc57fe25f9f7661b460395ebdfda506f70fdf4cc71d2ae5ca578 + io-event (1.22.1) sha256=0dd054cefe4a29f1b8286604f332c14f3208d75f54902961e91e94d45e5e1600 io-stream (0.14.0) sha256=07d9b13ea2066ccbbc761fff8d69ad5a31e47561d31e02cc271f3ed9c1a8f1cd ircparser (1.0.0) sha256=0d4598f691172e2ad078207c50ba62c0d3428ae841836b0a682e6cd01d62878d json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df @@ -113,15 +146,15 @@ CHECKSUMS protocol-hpack (1.5.1) sha256=6feca238b8078da1cd295677d6f306c6001af92d75fe0643d33e6956cbc3ad91 protocol-http (0.72.0) sha256=b153474785979495b9f2df78f0fdc2e02edc7e92c1f7ea00425eed73bd02194c protocol-http1 (0.41.0) sha256=9863874a1228948a896c3e7ea16d13a680c8f3ed50706007c78b7033018b0ea3 - protocol-http2 (0.28.0) sha256=b7c6195dcfa80ec2484670e9fd8fc21024cc654a391aa0792433aff1188c1691 + protocol-http2 (0.29.1) sha256=3f4ee667b5730b7d05c70d3771bd5d5cdbb31beedaa9d05f2bdac68c9c984d19 protocol-rack (0.23.0) sha256=63eec3341b20565b3dd155345fd1426ecbbed322cdc8a6accc993905388c03d7 protocol-url (0.19.0) sha256=3f6b9f6aa742a259a0b3610fbbd9685dcd9506adf6734f428ff96259faabfb21 protocol-websocket (0.21.2) sha256=cbe35be8e045597b221874fba5928d7cd2483884126e3e36faece4741dc88eb7 rack (3.2.7) sha256=93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7 rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 - rubyzip (3.6.0) sha256=268994d44d62282d1cfd99bf10eae48d7267199158ad7ea3e1fee2da9458b695 + rubyzip (3.7.0) sha256=65c19294da75297a939006f3516deacc33185fbd721ff1954f7a231db6d3e121 sdl3-bindings (1.0.4) sha256=326f497c30e6a5cab402e6178403b30c9df4a11a4e109e08c5da6e920c7bb55b BUNDLED WITH - 4.0.8 + 4.1.0.beta1 diff --git a/lib/application_helper.rb b/lib/application_helper.rb index 7b3604e..c996db2 100644 --- a/lib/application_helper.rb +++ b/lib/application_helper.rb @@ -71,5 +71,9 @@ module W3DHubLauncher found_server end + + def self.announce_application_changed(application, state) + CyberarmEngine::EventBus.publish("application_changed", { application: application, state: state }) + end end end diff --git a/lib/dialogs/import_application.rb b/lib/dialogs/import_application.rb index 4c161b3..451a683 100644 --- a/lib/dialogs/import_application.rb +++ b/lib/dialogs/import_application.rb @@ -67,7 +67,7 @@ module W3DHubLauncher Worker::Api.update_settings(MemCache[:settings]) - # FIXME: broadcast app installed on CyberarmEngine::EventBus + ApplicationHelper.announce_application_changed(app, :installed) end end end diff --git a/lib/pages/boot/start_up.rb b/lib/pages/boot/start_up.rb index 135f1b9..6c241b5 100644 --- a/lib/pages/boot/start_up.rb +++ b/lib/pages/boot/start_up.rb @@ -187,7 +187,7 @@ module W3DHubLauncher Worker::Api.applications do |result| if result.okay? - File.write("applications.json", result.data) + File.write("#{CACHE_PATH}/applications.json", result.data) hash = JSON.parse(result.data) applications = hash["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || [] diff --git a/lib/pages/games.rb b/lib/pages/games.rb index 127eec4..44629de 100644 --- a/lib/pages/games.rb +++ b/lib/pages/games.rb @@ -35,11 +35,19 @@ module W3DHubLauncher end def focus - @parent.show_battleview_panel + # @parent.show_battleview_panel + + CyberarmEngine::EventBus.subscribe("application_changed") do |hash| + if @current_app&.id == hash[:application].id && @current_channel&.id == hash[:application].channel + populate_game_info + end + end end def blur @parent.hide_battleview_panel + + CyberarmEngine::EventBus.unsubscribe("application_changed") end def populate_game_content_container @@ -115,7 +123,7 @@ module W3DHubLauncher unless MemCache[:"events_#{app_id}"] Worker::Api.events(app_id) do |result| if result.okay? - File.write("events_#{app_id}.json", result.data) + File.write("#{CACHE_PATH}/events_#{app_id}.json", result.data) MemCache[:"events_#{app_id}"] = JSON.parse(result.data)&.map { |item| Worker::Api::ServerEvent.new(item) } || [] populate_game_event if app_id == @current_app.id @@ -126,7 +134,7 @@ module W3DHubLauncher unless MemCache[:"news_#{app_id}"] Worker::Api.news(app_id) do |result| if result.okay? - File.write("news_#{app_id}.json", result.data) + File.write("#{CACHE_PATH}/news_#{app_id}.json", result.data) MemCache[:"news_#{app_id}"] = JSON.parse(result.data)["news"]&.map { |item| Worker::Api::NewsItem.new(item) } || [] populate_game_news if app_id == @current_app.id @@ -186,15 +194,25 @@ module W3DHubLauncher flow(width: 1.0, height: 60) do if application - button "JOIN", tip: "Join most populated or lowest ping server", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn| - server = ApplicationHelper.play_now_server(application) + if @current_channel.update_available?(application.version) + button "Update", tip: "Update application from v#{application.version} to v#{@current_channel.version}", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **UPDATE_BUTTON_THEME do |btn| + btn.enabled = false - if server - ApplicationHelper.join_server(application, server) + Worker::Api.update_application(@current_app.id, @current_channel.id) do |result, status| + handle_task_update(result, status) + end + end else - puts "No server available." + button "JOIN", tip: "Join most populated or lowest ping server", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn| + server = ApplicationHelper.play_now_server(application) + + if server + ApplicationHelper.join_server(application, server) + else + puts "No server available." + end + end end - end button safe_get_image("#{ROOT_PATH}/media/icons/singleplayer.png"), tip: "Single player", image_height: 1.0, background_nine_slice: NINE_SLICE_SQUARE, **CTA_BUTTON_THEME do ApplicationHelper.run(application) end @@ -206,26 +224,48 @@ module W3DHubLauncher menu_item("Screenshots Folder", tip: "Open application installation folder:\n#{Dir.home}/Documents/W3D Hub/#{application.id}-#{application.channel}/Screenshots") stack(width: 1.0, height: 2, background: 0xff_bbbbbb) menu_item("Application Settings", tip: "Edit application launch options") - menu_item("Repair Installation", tip: "Attempt to fix and repair installation") - menu_item("Check for Updates", tip: "Manually check for application updates") + if @current_app.servicable? + menu_item("Repair Installation", tip: "Attempt to fix and repair installation") do + Worker::Api.repair_application(@current_app.id, @current_channel.id) do |result, status| + handle_task_update(result, status) + end + end + menu_item("Check for Updates", tip: "Manually check for application updates") + end stack(width: 1.0, height: 2, background: 0xff_bbbbbb) - menu_item("Move Installation", tip: "Move application installation to a different directory or disk") - menu_item("Unlink Installation", tip: "The application will be removed from the launcher\nbut the application's files will not be touched") - menu_item("Uninstall", tip: "Uninstall application and delete files") + if @current_app.servicable? + menu_item("Move Installation", tip: "Move application installation to a different directory or disk") do + # FIXME: Show a prompt dialog for choosing new location + # Worker::Api.move_application(@current_app.id, @current_channel.id) do |result, status| + # handle_task_update(result, status) + # end + end + end + menu_item("Unlink Installation", tip: "The application will be removed from the launcher\nbut the application's files will not be touched") do + Worker::Api.unlink_application(@current_app.id, @current_channel.id) do |result, status| + if result.okay? + MemCache[:settings] = Worker::Api::Settings.new(JSON.parse(result.data)) + populate_game_info + end + end + end + if @current_app.servicable? + menu_item("Uninstall", tip: "Uninstall application and delete files") + end end.show end elsif @current_app.servicable? # pp @current_app - button "Import", tip: "Import existing application installation", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn| - dialog(Dialog::ImportApplication, application: @current_app, channel: @current_channel) - end - button "Download", tip: "Download and install application", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_RIGHT, **CTA_BUTTON_THEME do |btn| + button "Download", tip: "Download and install application", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn| btn.enabled = false - Worker::Api.install_application(@current_app.id, @current_channel.id) do |status, result| - handle_task_update(status, result) + Worker::Api.install_application(@current_app.id, @current_channel.id) do |result, status| + handle_task_update(result, status) end end + button "Import", tip: "Import existing application installation", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_RIGHT, **CTA_BUTTON_THEME do |btn| + dialog(Dialog::ImportApplication, application: @current_app, channel: @current_channel) + end else button "Import", tip: "Import existing application installation", fill: true, height: 1.0, **CTA_BUTTON_THEME do |btn| dialog(Dialog::ImportApplication, application: @current_app, channel: @current_channel) @@ -297,10 +337,12 @@ module W3DHubLauncher when Worker::Request::STATUS_COMPLETE status_bar.hide - MemCache[:settings].applications << Worker::Api::Settings::Application.new(data["application"]) + # FIXME: Application might NOT be installed + application = MemCache[:settings].add_or_update_application(Worker::Api::Settings::Application.new(data["application"])) Worker::Api.update_settings(MemCache[:settings]) - populate_game_info + # FIXME: Application might NOT be installed + ApplicationHelper.announce_application_changed(application, :installed) when Worker::Request::STATUS_IN_PROGRESS status_bar.show diff --git a/lib/states/interface.rb b/lib/states/interface.rb index cc4133a..8b74ed2 100644 --- a/lib/states/interface.rb +++ b/lib/states/interface.rb @@ -93,7 +93,7 @@ module W3DHubLauncher end # battleview/friends container - @battleview_container = stack(width: 400, height: 1.0, margin_left: LARGE_PADDING) do + @battleview_container = stack(width: 400, height: 1.0, margin_left: LARGE_PADDING, visible: false) do # friend management container flow(width: 1.0, height: 60) do flow(width: 1.0, v_align: :center) do @@ -358,7 +358,7 @@ module W3DHubLauncher def button_up(id) super - @battleview_container.toggle if id == Gosu::KB_F8 + # @battleview_container.toggle if id == Gosu::KB_F8 end end end diff --git a/lib/theme.rb b/lib/theme.rb index 8784459..b15a888 100644 --- a/lib/theme.rb +++ b/lib/theme.rb @@ -39,6 +39,19 @@ module W3DHubLauncher } } + UPDATE_BUTTON_THEME = { + color: 0xff_ffffff, + background_nine_slice_color: 0xff_e66100, + hover: { + color: 0xff_ffffff, + background_nine_slice_color: 0xff_ff7800 + }, + active: { + color: 0xff_ffffff, + background_nine_slice_color: 0xff_c64600 + } + } + THEME = { TextBlock: { text_static: true, diff --git a/lib/worker.rb b/lib/worker.rb index a606c89..7223f31 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -49,24 +49,38 @@ module W3DHubLauncher @w3dhub_api = W3DHubLauncher::W3DHubApi.new Async do |task| - UNIXServer.open(IPC_PATH) do |server| - while(socket = server.accept) - @client = socket + task.async do + while true + @tasks.delete_if { |t| [Request::STATUS_ERROR, Request::STATUS_COMPLETE].include?(t.status) } - task.async do - while(data = socket.gets) - json = JSON.parse(data) - query = Request::Query.new(type: json["type"].to_sym, request_id: json["request_id"], data: json["data"]) + if @tasks.first && @tasks.all? { |t| t.status == Request::STATUS_PENDING } + @tasks.first.start(self) + end - # pp [:server_incoming, data, query] + sleep 0.25 + end + end - if respond_to?(query.type) - response = send(query.type, query) - # pp [:server_to_client, response] - payload = { status: response.status, request_id: response.request_id, data: response.result.data, error: response.result.error }.to_json - socket.write(payload) - socket.write(RESPONSE_SEPARATOR) - socket.flush + task.async do |subtask| + UNIXServer.open(IPC_PATH) do |server| + while(socket = server.accept) + @client = socket + + task.async do + while(data = socket.gets) + json = JSON.parse(data) + query = Request::Query.new(type: json["type"].to_sym, request_id: json["request_id"], data: json["data"]) + + # pp [:server_incoming, data, query] + + if respond_to?(query.type) + response = send(query.type, query) + # pp [:server_to_client, response] + payload = { status: response.status, request_id: response.request_id, data: response.result.data, error: response.result.error }.to_json + socket.write(payload) + socket.write(RESPONSE_SEPARATOR) + socket.flush + end end end end @@ -269,8 +283,8 @@ module W3DHubLauncher total_downloaded_bytes: total_downloaded_bytes, content_length: content_length }) - # FIXME: Send intermediate response to requester - # send(query, Response.new(Request::STATUS_IN_PROGRESS, query.request_id progress_result)) + + message_requester(query.request_id, Request::STATUS_IN_PROGRESS, progress_result.data, progress_result.error) end end @@ -453,15 +467,44 @@ module W3DHubLauncher installed_version: nil, target_version: channel.version ) - @tasks.last.start(self) Response.new(Request::STATUS_PENDING, query.request_id, CyberarmEngine::Result.new(data: true)) end def task_update_application(query) + application = @w3dhub_api.applications.find { |app| app.id == query.data["app_id"] } + channel = application.channels.find { |chan| chan.id == query.data["channel_id"] } + + settings_application = @settings.application_installed?(application.id, channel.id) + + @tasks << Task::UpdateApplication.new( + request_id: query.request_id, + application: application, + channel: channel, + installed_version: settings_application.version, + target_version: channel.version + ) + + Response.new(Request::STATUS_PENDING, query.request_id, CyberarmEngine::Result.new(data: true)) end def task_repair_application(query) + application = @w3dhub_api.applications.find { |app| app.id == query.data["app_id"] } + channel = application.channels.find { |chan| chan.id == query.data["channel_id"] } + + settings_application = @settings.application_installed?(application.id, channel.id) + + @tasks << Task::RepairApplication.new( + request_id: query.request_id, + application: application, + channel: channel, + installed_version: settings_application.version, + target_version: settings_application.version + ) + + pp @tasks.last + + Response.new(Request::STATUS_PENDING, query.request_id, CyberarmEngine::Result.new(data: true)) end def task_move_application(query) @@ -469,5 +512,20 @@ module W3DHubLauncher def task_uninstall_application(query) end + + def unlink_application(query) + result = CyberarmEngine::Result.new + + application = @settings.application_installed?(query.data["app_id"], query.data["channel_id"]) + + if application + @settings.remove_application(application) + + query = Request::Query.new(query.type, query.request_id, @settings.to_json) + return update_settings(query) + end + + deliver_response(result, query) + end end end diff --git a/lib/worker/api.rb b/lib/worker/api.rb index 4522ea0..b71d340 100644 --- a/lib/worker/api.rb +++ b/lib/worker/api.rb @@ -100,6 +100,11 @@ module W3DHubLauncher def self.uninstall_application(app_id, channel_id, &block) Worker::Request.new(:task_uninstall_application, { app_id: app_id, channel_id: channel_id }, &block) end + + # request unlinking of application + def self.unlink_application(app_id, channel_id, &block) + Worker::Request.new(:unlink_application, { app_id: app_id, channel_id: channel_id }, &block) + end end end end diff --git a/lib/worker/api/application.rb b/lib/worker/api/application.rb index 49de01c..53e5cad 100644 --- a/lib/worker/api/application.rb +++ b/lib/worker/api/application.rb @@ -80,6 +80,10 @@ module W3DHubLauncher extended_data("serverchannel", @id) end + def update_available?(current_version) + current_version < @version + end + def to_json(context = nil) { "id": @id, diff --git a/lib/worker/api/settings.rb b/lib/worker/api/settings.rb index 851c8a7..0a5876c 100644 --- a/lib/worker/api/settings.rb +++ b/lib/worker/api/settings.rb @@ -41,6 +41,22 @@ module W3DHubLauncher applications.find { |app| app.id == app_id && app.channel == channel_id } end + def add_or_update_application(application) + index = applications.index { |app| app.id == application.id && app.channel == application.channel } + + if index + applications[index] = application + else + applications << application + end + + application + end + + def remove_application(application) + applications.delete(application) + end + # User explictly set options class Preferences SCHEMA = 0 diff --git a/lib/worker/task.rb b/lib/worker/task.rb index 3556304..c9e33c8 100644 --- a/lib/worker/task.rb +++ b/lib/worker/task.rb @@ -6,6 +6,7 @@ module W3DHubLauncher IndexedFile = Data.define(:type, :path) attr_reader :request_id, :application, :channel, :installed_version, :target_version + attr_reader :status def initialize(request_id:, application:, channel:, installed_version:, target_version:) @request_id = request_id @@ -16,6 +17,8 @@ module W3DHubLauncher pp self + @status = Worker::Request::STATUS_PENDING + # caching mechinism for file path normalization @file_index = {} # manifests! @@ -36,19 +39,28 @@ module W3DHubLauncher end def start(worker) + return unless status == Worker::Request::STATUS_PENDING + @worker = worker @installation_directory ||= application_target_installation_directory update_status(label: "Starting...", fraction: 0.0) - execute + + @status = Worker::Request::STATUS_BUSY + + v = execute + + @status = Worker::Request::STATUS_COMPLETE + v end def abort_task!(reason = "") puts reason message_requester(Worker::Request::STATUS_ERROR, data: nil, error: reason) + @status = Worker::Request::STATUS_ERROR raise reason end @@ -120,6 +132,8 @@ module W3DHubLauncher @required_manifest_files = @manifest_files.clone + cached_checksums = {} + # Process manifest game files in NEWEST to OLDEST order so that we don't erroneously flag # valid files as invalid due to an OLDER version of the file being checked FIRST. @manifest_files.reverse.each do |file| @@ -133,13 +147,17 @@ module W3DHubLauncher next unless File.exist?(file_path) # && !File.directory?(file_path) puts "verifying #{file_path}..." - checksum = Digest::SHA256.file(file_path).hexdigest.upcase + checksum = cached_checksums[file_path] || Digest::SHA256.file(file_path).hexdigest.upcase + cached_checksums[file_path] ||= checksum if checksum == file.checksum @required_manifest_files.delete(file) # remove irrelevant older versions of the file from consideration @required_manifest_files.delete_if { |f| f.name.casecmp?(file.name) && f.version < file.version } + puts " verified:#{file.version}" + else + puts " failed verification:#{file.version}" end end end @@ -279,12 +297,14 @@ module W3DHubLauncher timestamp: Time.now.to_i ) + @status = Worker::Request::STATUS_COMPLETE message_requester(Worker::Request::STATUS_COMPLETE, data: { application: app }) puts "APPLICATION: #{@application.name} #{@application.id}:#{@channel.id}:#{@target_version} installed." end def mark_application_uninstalled + @status = Worker::Request::STATUS_COMPLETE message_requester(Worker::Request::STATUS_COMPLETE, data: {}) end @@ -295,7 +315,7 @@ module W3DHubLauncher end def update_status(label:, fraction:) - message_requester(data: { status: { title: "TASK #{@application.name} (#{@channel.name})", label: label, fraction: fraction } } ) + message_requester(data: { status: { title: "#{@task_verb} #{@application.name} (#{@channel.name})", label: label, fraction: fraction } } ) end def application_target_installation_directory diff --git a/lib/worker/tasks/install_application.rb b/lib/worker/tasks/install_application.rb index 86e5ed5..3b543c3 100644 --- a/lib/worker/tasks/install_application.rb +++ b/lib/worker/tasks/install_application.rb @@ -2,6 +2,7 @@ module W3DHubLauncher class Task class InstallApplication < Task def setup + @task_verb = "Installing".freeze end def execute diff --git a/lib/worker/tasks/repair_application.rb b/lib/worker/tasks/repair_application.rb index e69de29..68cf100 100644 --- a/lib/worker/tasks/repair_application.rb +++ b/lib/worker/tasks/repair_application.rb @@ -0,0 +1,11 @@ +module W3DHubLauncher + class Task + class RepairApplication < InstallApplication + def setup + super + + @task_verb = "Repairing".freeze + end + end + end +end diff --git a/lib/worker/tasks/update_application.rb b/lib/worker/tasks/update_application.rb index e69de29..9ce6894 100644 --- a/lib/worker/tasks/update_application.rb +++ b/lib/worker/tasks/update_application.rb @@ -0,0 +1,11 @@ +module W3DHubLauncher + class Task + class UpdateApplication < InstallApplication + def setup + super + + @task_verb = "Updating".freeze + end + end + end +end diff --git a/w3d_hub_linux_launcher.rb b/w3d_hub_linux_launcher.rb index 8ddd280..59baa2b 100644 --- a/w3d_hub_linux_launcher.rb +++ b/w3d_hub_linux_launcher.rb @@ -77,6 +77,9 @@ module W3DHubLauncher W3DHubLauncher::Worker.new.listen end + # Prevent race condition where server IPC isn't ready before the client attempts to connect + sleep 0.1 + # UNIXSocket / client WORKER = W3DHubLauncher::Worker.new WORKER.connect