diff --git a/Gemfile b/Gemfile index ae4dc7c..1c053c7 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,4 @@ source "https://rubygems.org" gem "cyberarm_engine" -gem "sanitize" -gem "rss" gem "launchy" \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 5d15e1b..6be255f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,10 +1,9 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.7.0) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) clipboard (1.3.6) - crass (1.0.6) cyberarm_engine (0.19.1) clipboard (~> 1.3.5) excon (~> 0.78.0) @@ -15,21 +14,7 @@ GEM gosu_more_drawables (0.3.1) launchy (2.5.0) addressable (~> 2.7) - nokogiri (1.11.7-x64-mingw32) - racc (~> 1.4) - nokogiri (1.11.7-x86_64-linux) - racc (~> 1.4) - nokogumbo (2.0.5) - nokogiri (~> 1.8, >= 1.8.4) public_suffix (4.0.6) - racc (1.5.2) - rexml (3.2.5) - rss (0.2.9) - rexml - sanitize (5.2.3) - crass (~> 1.0.2) - nokogiri (>= 1.8.0) - nokogumbo (~> 2.0) PLATFORMS x64-mingw32 @@ -38,8 +23,6 @@ PLATFORMS DEPENDENCIES cyberarm_engine launchy - rss - sanitize BUNDLED WITH 2.2.28 diff --git a/lib/application_manager.rb b/lib/application_manager.rb index 9a2e1cd..999922a 100644 --- a/lib/application_manager.rb +++ b/lib/application_manager.rb @@ -1,26 +1,67 @@ class W3DHub class ApplicationManager - def install(app_id) - puts "Installation Request: #{app_id}" + def initialize + @tasks = [] # :installer, :importer, :repairer, :uninstaller end - def import(app_id, path) - puts "Import Request: #{app_id} -> #{path}" + def install(app_id, channel) + puts "Installation Request: #{app_id}-#{channel}" + + return false if installed?(app_id, channel) || installing?(app_id, channel) + + # if on unix: ask/make a wine prefix + # add install task to a list and mark as installing + # fetch manifests + # cycle through manifests and check package cache + # generate list of required packages to download + # download packages + # verify packages + # unpack packages + # install dependencies (e.g. visual C runtime) + + @tasks.push(Installer.new(app_id, channel)) end - def settings(app_id) - puts "Settings Request: #{app_id}" + def import(app_id, channel, path) + puts "Import Request: #{app_id}-#{channel} -> #{path}" + + # Check registry for auto-import if windows + # if auto-import fails ask user for path to game exe + # mark app as imported/installed + + @tasks.push(Importer.new(app_id, channel, path)) end - def repair(app_id) - puts "Repair Installation Request: #{app_id}" + def settings(app_id, channel) + puts "Settings Request: #{app_id}-#{channel}" + + # open wwconfig end - def uninstall(app_id) - puts "Uninstall Request: #{app_id}" + def repair(app_id, channel) + puts "Repair Installation Request: #{app_id}-#{channel}" + + return false if !installed?(app_id, channel) || installing?(app_id, channel) + + # verify/download manifests + # verify game files + # verify package cache packages of any files that failed verification + # re/download needed packages + # unpack packages + # install dependencies (e.g. visual C runtime) if appropriate + + @tasks.push(Repairer.new(app_id, channel)) end - def show_folder(app_id, type) + def uninstall(app_id, channel) + puts "Uninstall Request: #{app_idchannel}" + + return false if !installed?(app_id, channel) || installing?(app_id, channel) + + @tasks.push(Uninstaller.new(app_id, channel)) + end + + def show_folder(app_id, channel, type) puts "Show Folder Request: #{app_id} -> #{type.inspect}" case type @@ -32,13 +73,17 @@ class W3DHub end end - def installed?(app_id) + def installed?(app_id, channel) false end + def installing?(app_id, channel) + @tasks.find { |t| t.is_a?(Installer) && t.app_id == app_id } + end + # No application tasks are being done def idle? - true + @tasks.empty? end # Whether some operation is in progress diff --git a/lib/application_manager/task.rb b/lib/application_manager/task.rb new file mode 100644 index 0000000..6ca206b --- /dev/null +++ b/lib/application_manager/task.rb @@ -0,0 +1,145 @@ +class W3DHub + class ApplicationManager + class Task + include CyberarmEngine::Common + + attr_reader :app_id, :release_channel + + def initialize(app_id, release_channel) + @app_id = app_id + @release_channel = release_channel + + @task_state = :not_started # :not_started, :running, :paused, :halted, :complete, :failed + @task_steps = [] + @task_step_index = 0 + + @application = window.applications.games.find { |g| g.id == app_id } + @channel = @application.channels.find { |c| c.name == release_channel } + + setup + end + + def setup + end + + def state + @task_state + end + + # Start task, inside its own thread + def start + @task_state = :running + + Thread.new do + @task_steps.each_with_index do |step, i| + break if @task_state == :halted + + @task_step_index = i + + success = step.start + + failure!(step) unless success + break unless success + end + + @task_state = :complete unless @task_state == :failed + end + end + + # Suspend operation, if possible + def pause + @task_state = :paused if pauseable? + end + + # Halt operation, if possible + def stop + @task_state = :halted if stoppable? + end + + def pauseable? + false + end + + def stoppable? + false + end + + def complete? + @task_state == :complete + end + + def failed? + @task_state == :failed + end + + def failure_reason + @task_failure_reason || "" + end + + def failure!(step) + @task_state = :failed + @task_failure_reason = "Failed to complete: \"#{step.name}\" due to an error: #{step.error}" + end + + def run_on_main_thread(block) + window.main_thread_queue << block + end + + def add_step(name, method, *args) + @task_steps << Step.new(name, method, args) + end + + def fetch_manifests + # Do stuff + + package_fetch("games", app_id, "manifest.xml", @channel.version) + end + + def package_fetch(category, subcategory, package, version) + end + + class Step + attr_reader :name + + def initialize(name, method, args) + @name = name + @method = method + @args = args + + @step_state = :not_started # :not_started, :running, :paused, :halted, :complete, :failed + @success = false + end + + def start + # do work + # ensure that a boolean value is returned + ensure + @success + end + + def pause + end + + def stop + end + + def status + nil + end + + def progress + 0.0 + end + + def total_work + 1.0 + end + + # data to pass on to next step(s) + def result + nil + end + end + end + end +end \ No newline at end of file diff --git a/lib/application_manager/tasks/importer.rb b/lib/application_manager/tasks/importer.rb new file mode 100644 index 0000000..e18ce62 --- /dev/null +++ b/lib/application_manager/tasks/importer.rb @@ -0,0 +1,11 @@ +class W3DHub + class ApplicationManager + class Importer < Task + def initialize(app_id, channel, path) + super(app_id, channel) + + @path = path + end + end + end +end \ No newline at end of file diff --git a/lib/application_manager/tasks/installer.rb b/lib/application_manager/tasks/installer.rb new file mode 100644 index 0000000..b1cfad6 --- /dev/null +++ b/lib/application_manager/tasks/installer.rb @@ -0,0 +1,20 @@ +class W3DHub + class ApplicationManager + class Installer < Task + def setup + add_step("Fetching manifests...", :fetch_manifests) + add_step("Building package list...", :build_package_list) + + add_step("Downloading packages...", :fetch_packages) + add_step("Verifying packages...", :verify_packages) + add_step("Unpacking packages...", :unpack_packages) + + add_step("Crushing grapes...", :create_wine_prefix) + + add_step("Installing dependencies...", :install_dependencies) + + add_step("Completed.", :mark_application_installed) + end + end + end +end \ No newline at end of file diff --git a/lib/application_manager/tasks/repairer.rb b/lib/application_manager/tasks/repairer.rb new file mode 100644 index 0000000..fe4ba54 --- /dev/null +++ b/lib/application_manager/tasks/repairer.rb @@ -0,0 +1,6 @@ +class W3DHub + class ApplicationManager + class Repairer < Task + end + end +end \ No newline at end of file diff --git a/lib/application_manager/tasks/uninstaller.rb b/lib/application_manager/tasks/uninstaller.rb new file mode 100644 index 0000000..4fa24e3 --- /dev/null +++ b/lib/application_manager/tasks/uninstaller.rb @@ -0,0 +1,6 @@ +class W3DHub + class ApplicationManager + class Uninstaller < Task + end + end +end \ No newline at end of file diff --git a/lib/pages/download_manager.rb b/lib/pages/download_manager.rb index a983858..51d944a 100644 --- a/lib/pages/download_manager.rb +++ b/lib/pages/download_manager.rb @@ -35,7 +35,7 @@ class W3DHub # Available to download stack(width: 1.0, height: 0.8, padding: 8, scroll: true) do - @host.applications.games.reject { |g| g.id == "ren" }.each_with_index do |game, i| + window.applications.games.reject { |g| g.id == "ren" }.each_with_index do |game, i| flow(width: 1.0, height: 64, padding: 8) do background 0xff_333333 if i.odd? diff --git a/lib/pages/games.rb b/lib/pages/games.rb index 93b5664..38ccb9d 100644 --- a/lib/pages/games.rb +++ b/lib/pages/games.rb @@ -3,7 +3,7 @@ class W3DHub class Games < Page def setup @@game_news ||= {} - @focused_game ||= @host.applications.games.first + @focused_game ||= window.applications.games.first body.clear do # Games List @@ -15,7 +15,7 @@ class W3DHub end end - populate_game_page(@host.applications.games.first) + populate_game_page(window.applications.games.first, window.applications.games.first.channels.first) populate_games_list end @@ -23,7 +23,7 @@ class W3DHub @games_list_container.clear do background 0xff_121920 - @host.applications.games.each do |game| + window.applications.games.each do |game| selected = game == @focused_game game_button = stack(width: 1.0, border_thickness_left: 4, @@ -43,14 +43,14 @@ class W3DHub end game_button.subscribe(:clicked_left_mouse_button) do |e| - populate_game_page(game) + populate_game_page(game, game.channels.first) populate_games_list end end end end - def populate_game_page(game) + def populate_game_page(game, channel) @focused_game = game @game_page_container.clear do @@ -60,8 +60,11 @@ class W3DHub flow(width: 1.0, height: 0.03) do # background 0xff_444411 - game.channels.each do |channel| - button "#{channel.name}", text_size: 14, padding_top: 2, padding_bottom: 2, padding_left: 4, padding_right: 4, margin: 0, margin_right: 4 + puts "Generating..." + + inscription "Channel" + list_box items: game.channels.map { |c| c.name }, selected: channel, enabled: game.channels.count > 1, width: 128, padding_left: 4, padding_top: 2, padding_right: 4, padding_bottom: 2, text_size: 16 do |value| + populate_game_page(game, game.channels.find{ |c| c.name == value }) end end @@ -81,16 +84,16 @@ class W3DHub # end # end - if window.application_manager.installed?(game.id) + if window.application_manager.installed?(game.id, channel.name) Hash.new.tap { |hash| - hash["Game Settings"] = { icon: "gear", block: proc { window.application_manager.settings(game.id) } } + hash["Game Settings"] = { icon: "gear", block: proc { window.application_manager.settings(game.id, channel.name) } } if game.id != "ren" - hash["Repair Installation"] = { icon: "wrench", block: proc { window.application_manager.repair(game.id) } } - hash["Uninstall"] = { icon: "trashCan", block: proc { window.application_manager.uninstall(game.id) } } + hash["Repair Installation"] = { icon: "wrench", block: proc { window.application_manager.repair(game.id, channel.name) } } + hash["Uninstall"] = { icon: "trashCan", block: proc { window.application_manager.uninstall(game.id, channel.name) } } end - hash["Install Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, :installation) } } - hash["User Data Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, :user_data) } } - hash["View Screenshots"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, :screenshots) } } + hash["Install Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.name, :installation) } } + hash["User Data Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.name, :user_data) } } + hash["View Screenshots"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.name, :screenshots) } } }.each do |key, hash| flow(width: 1.0, height: 22, margin_bottom: 8) do image "#{GAME_ROOT_PATH}/media/ui_icons/#{hash[:icon]}.png", width: 0.11 if hash[:icon] @@ -128,18 +131,18 @@ class W3DHub # item.block&.call(game) # end # end - if window.application_manager.installed?(game.id) + if window.application_manager.installed?(game.id, channel.id) button "Play Now", margin_left: 24 button "Single Player", margin_left: 24 else unless game.id == "ren" button "Install", margin_left: 24 do - window.application_manager.install(game.id) + window.application_manager.install(game.id, channel.name) end end button "Import", margin_left: 24 do - window.application_manager.import(game.id, "?") + window.application_manager.import(game.id, channel.name, "?") end end end diff --git a/lib/states/boot.rb b/lib/states/boot.rb index f599211..6b3798c 100644 --- a/lib/states/boot.rb +++ b/lib/states/boot.rb @@ -42,16 +42,14 @@ class W3DHub @progressbar.value = @fraction if @progressbar.value >= 1.0 && @task_index == @tasks.size - push_state( - States::Interface, - account: @account, - service_status: @service_status, - applications: @applications - ) + window.account = @account + window.service_status = @service_status + window.applications = @applications + + push_state(States::Interface) end if @tasks.dig(@tasks.keys[@task_index], :started) == false - p @tasks.keys[@task_index] @tasks[@tasks.keys[@task_index]][:started] = true send(:"#{@tasks.keys[@task_index]}") diff --git a/lib/states/interface.rb b/lib/states/interface.rb index f5736dd..f1c50d0 100644 --- a/lib/states/interface.rb +++ b/lib/states/interface.rb @@ -2,7 +2,6 @@ class W3DHub class States class Interface < CyberarmEngine::GuiState attr_reader :main_thread_queue - attr_accessor :account, :service_status, :applications def setup window.show_cursor = true diff --git a/lib/window.rb b/lib/window.rb index 638ffe2..e824e42 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -1,6 +1,7 @@ class W3DHub class Window < CyberarmEngine::Window attr_reader :settings, :application_manager + attr_accessor :account, :service_status, :applications def setup self.caption = "#{W3DHub::NAME}" diff --git a/w3dhub.rb b/w3dhub.rb index ae86337..e4072b3 100644 --- a/w3dhub.rb +++ b/w3dhub.rb @@ -1,4 +1,11 @@ -require "cyberarm_engine" +begin + require_relative "../cyberarm_engine/lib/cyberarm_engine" +rescue LoadError => e + puts "Failed to load local cyberarm_engine:" + pp e + + require "cyberarm_engine" +end require "digest" require "zlib" require "launchy" @@ -17,6 +24,11 @@ require_relative "lib/window" require_relative "lib/cache" require_relative "lib/settings" require_relative "lib/application_manager" +require_relative "lib/application_manager/task" +require_relative "lib/application_manager/tasks/installer" +require_relative "lib/application_manager/tasks/uninstaller" +require_relative "lib/application_manager/tasks/repairer" +require_relative "lib/application_manager/tasks/importer" require_relative "lib/states/boot" require_relative "lib/states/interface"