From 7da254fd619a69d32550e115eee1db32148addec Mon Sep 17 00:00:00 2001 From: cyberarm Date: Fri, 4 Feb 2022 08:59:15 -0600 Subject: [PATCH] Changed how account data is stored, authorization header is now sent when logged in, adjusted spacing game filters for server list, fixed crashing when trying to load icon for game whos icon is not present, a bit of code cleanup to Api to use Async::HTTP::Client over Async::Internet directly (for everything except get requests), probably a few misc. changes --- .gitignore | 3 +- lib/api.rb | 52 ++++++++++++++++++++------------- lib/api/account.rb | 10 ++++++- lib/application_manager/task.rb | 8 ++--- lib/cache.rb | 7 +++-- lib/pages/community.rb | 8 ++--- lib/pages/download_manager.rb | 3 +- lib/pages/games.rb | 16 +++++----- lib/pages/login.rb | 20 ++++++++----- lib/pages/server_browser.rb | 12 +++++--- lib/states/boot.rb | 49 ++++++++++++++++++------------- 11 files changed, 114 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index fa300ab..f090514 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.json data/cache/* !data/cache/.gitkeep -_*.* \ No newline at end of file +_*.* +*.log \ No newline at end of file diff --git a/lib/api.rb b/lib/api.rb index 286171d..157a379 100644 --- a/lib/api.rb +++ b/lib/api.rb @@ -13,6 +13,20 @@ class W3DHub ENDPOINT = "https://secure.w3dhub.com".freeze + def self.post(url, headers = DEFAULT_HEADERS, body = nil) + @client ||= Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(ENDPOINT, protocol: Async::HTTP::Protocol::HTTP10)) + + # TODO: Check if session has expired and attempt to refresh session before submitting request + + # Inject Authorization header if account data is populated + if Store.account + headers = headers.dup + headers << ["Authorization", "Bearer #{Store.account.access_token}"] + end + + @client.post(url, headers, body) + end + # Method: POST # FORMAT: JSON @@ -28,9 +42,9 @@ class W3DHub # # On a failed login the service responds with: # {"error":"login-failed"} - def self.refresh_user_login(internet, refresh_token) + def self.refresh_user_login(refresh_token) body = "data=#{JSON.dump({refreshToken: refresh_token})}" - response = internet.post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body) + response = post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body) if response.success?#status == 200 user_data = JSON.parse(response.read, symbolize_names: true) @@ -38,7 +52,7 @@ class W3DHub return false if user_data[:error] body = "data=#{JSON.dump({ id: user_data[:userid] })}" - user_details = internet.post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body) + user_details = post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body) if user_details.success? user_details_data = JSON.parse(user_details.read, symbolize_names: true) @@ -51,9 +65,9 @@ class W3DHub end # See #user_refresh_token - def self.user_login(internet, username, password) + def self.user_login(username, password) body = "data=#{JSON.dump({username: username, password: password})}" - response = internet.post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body) + response = post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body) if response.success? user_data = JSON.parse(response.read, symbolize_names: true) @@ -61,7 +75,7 @@ class W3DHub return false if user_data[:error] body = "data=#{JSON.dump({ id: user_data[:userid] })}" - user_details = internet.post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body) + user_details = post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body) if user_details.success? user_details_data = JSON.parse(user_details.read, symbolize_names: true) @@ -77,14 +91,14 @@ class W3DHub # Client sends an Authorization header bearer token which is received from logging in (Required?) # # Response: avatar-uri (Image download uri), id, username - def self.user_details(internetn, id) + def self.user_details(id) end # /apis/w3dhub/1/get-service-status # Service response: # {"services":{"authentication":true,"packageDownload":true}} - def self.service_status(internet) - response = internet.post("#{ENDPOINT}/apis/w3dhub/1/get-service-status", DEFAULT_HEADERS) + def self.service_status + response = post("#{ENDPOINT}/apis/w3dhub/1/get-service-status", DEFAULT_HEADERS) if response.success? ServiceStatus.new(response.read) @@ -97,8 +111,8 @@ class W3DHub # Client sends an Authorization header bearer token which is received from logging in (Optional) # Launcher sends an empty data request: data={} # Response is a list of applications/games - def self.applications(internet) - response = internet.post("#{ENDPOINT}/apis/launcher/1/get-applications", DEFAULT_HEADERS) + def self.applications + response = post("#{ENDPOINT}/apis/launcher/1/get-applications") if response.success? Applications.new(response.read) @@ -111,11 +125,11 @@ class W3DHub # Client sends an Authorization header bearer token which is received from logging in (Optional) # Client requests news for a specific application/game e.g.: data={"category":"ia"} ("launcher-home" retrieves the weekly hub updates) # Response is a JSON hash with a "highlighted" and "news" keys; the "news" one seems to be the desired one - def self.news(internet, category) + def self.news(category) body = "data=#{JSON.dump({category: category})}" - response = internet.post("#{ENDPOINT}/apis/w3dhub/1/get-news", FORM_ENCODED_HEADERS, body) + response = post("#{ENDPOINT}/apis/w3dhub/1/get-news", FORM_ENCODED_HEADERS, body) - if response.success?#status == 200 + if response.success? News.new(response.read) else false @@ -126,11 +140,9 @@ class W3DHub # /apis/launcher/1/get-package-details # client requests package details: data={"packages":[{"category":"games","name":"apb.ico","subcategory":"apb","version":""}]} - def self.package_details(internet, packages) + def self.package_details(packages) body = URI.encode_www_form("data": JSON.dump({ packages: packages })) - endpoint = Async::HTTP::Endpoint.parse("#{ENDPOINT}/apis/launcher/1/get-package-details", protocol: Async::HTTP::Protocol::HTTP10) - client = Async::HTTP::Client.new(endpoint) - response = client.post("#{ENDPOINT}/apis/launcher/1/get-package-details", FORM_ENCODED_HEADERS, body) + response = post("#{ENDPOINT}/apis/launcher/1/get-package-details", FORM_ENCODED_HEADERS, body) if response.success? hash = JSON.parse(response.read, symbolize_names: true) @@ -144,8 +156,8 @@ class W3DHub # client requests package: data={"category":"games","name":"ECW_Asteroids.zip","subcategory":"ecw","version":"1.0.0.0"} # # server responds with download bytes, probably supports chunked download and resume - def self.package(internet, package, &block) - Cache.fetch_package(internet, package, block) + def self.package(package, &block) + Cache.fetch_package(package, block) end #! === Server List API === !# diff --git a/lib/api/account.rb b/lib/api/account.rb index ce78dbf..740d1c2 100644 --- a/lib/api/account.rb +++ b/lib/api/account.rb @@ -11,7 +11,7 @@ class W3DHub @username = @data[:username] @displayname = @data[:displayname] - @avatar_uri = user_details[:"avatar-uri"] + @avatar_uri = user_details[:"avatar-uri"] || @data[:avatar_uri] @user_level = @data[:userlevel] @session_token = @data[:"session-token"] @@ -21,6 +21,14 @@ class W3DHub @studio_user_level = @data[:"studio-userlevel"] # Dunno? end + + def to_json(env) + d = @data.dup + d[:avatar_uri] = @avatar_uri + d[:access_token_expiry] = d[:access_token_expiry].to_i + + d.to_json(env) + end end end end diff --git a/lib/application_manager/task.rb b/lib/application_manager/task.rb index 865ec59..42180b7 100644 --- a/lib/application_manager/task.rb +++ b/lib/application_manager/task.rb @@ -325,8 +325,7 @@ class W3DHub } end - internet = Async::HTTP::Internet.instance - package_details = Api.package_details(internet, hashes) + package_details = Api.package_details(hashes) if package_details @packages = [package_details].flatten @@ -515,10 +514,9 @@ class W3DHub def fetch_manifest(category, subcategory, name, version, &block) # Check for and integrity of local manifest - internet = Async::HTTP::Internet.instance package = nil - array = Api.package_details(internet, [{ category: category, subcategory: subcategory, name: name, version: version }]) + array = Api.package_details([{ category: category, subcategory: subcategory, name: name, version: version }]) if array.is_a?(Array) package = array.first else @@ -543,7 +541,7 @@ class W3DHub internet = Async::HTTP::Internet.instance - Api.package(internet, package) do |chunk, remaining_bytes, total_bytes| + Api.package(package) do |chunk, remaining_bytes, total_bytes| block&.call(chunk, remaining_bytes, total_bytes) end end diff --git a/lib/cache.rb b/lib/cache.rb index 393a377..7b780cd 100644 --- a/lib/cache.rb +++ b/lib/cache.rb @@ -7,12 +7,14 @@ class W3DHub end # Fetch a generic uri - def self.fetch(internet, uri, force_fetch = false) + def self.fetch(uri, force_fetch = false) path = path(uri) if !force_fetch && File.exist?(path) path else + internet = Async::HTTP::Internet.instance + response = internet.get(uri, W3DHub::Api::DEFAULT_HEADERS) if response.success? @@ -46,9 +48,10 @@ class W3DHub end # Download a W3D Hub package - def self.fetch_package(internet, package, block) + def self.fetch_package(package, block) path = package_path(package.category, package.subcategory, package.name, package.version) headers = { "Content-Type": "application/x-www-form-urlencoded", "User-Agent": Api::USER_AGENT } + headers["Authorization"] = "Bearer #{Store.account.access_token}" if Store.account start_from_bytes = package.custom_partially_valid_at_bytes puts " Start from bytes: #{start_from_bytes} of #{package.size}" diff --git a/lib/pages/community.rb b/lib/pages/community.rb index 5e81158..e9195da 100644 --- a/lib/pages/community.rb +++ b/lib/pages/community.rb @@ -63,18 +63,18 @@ class W3DHub Async do internet = Async::HTTP::Internet.instance - fetch_w3dhub_news(internet) + fetch_w3dhub_news populate_w3dhub_news end end end - def fetch_w3dhub_news(internet) - news = Api.news(internet, "launcher-home") + def fetch_w3dhub_news + news = Api.news("launcher-home") if news news.items[0..9].each do |item| - Cache.fetch(internet, item.image) + Cache.fetch(item.image) end @w3dhub_news = news diff --git a/lib/pages/download_manager.rb b/lib/pages/download_manager.rb index 3c58c77..52d4a80 100644 --- a/lib/pages/download_manager.rb +++ b/lib/pages/download_manager.rb @@ -28,7 +28,8 @@ class W3DHub background task.application.color flow(width: 0.70, height: 1.0) do - @application_image = image "#{GAME_ROOT_PATH}/media/icons/#{task.app_id}.png", height: 1.0 + image_path = File.exist?("#{GAME_ROOT_PATH}/media/icons/#{task.app_id}.png") ? "#{GAME_ROOT_PATH}/media/icons/#{task.app_id}.png" : "#{GAME_ROOT_PATH}/media/icons/app.png" + @application_image = image image_path, height: 1.0 stack(margin_left: 8, width: 0.75) do @application_name_label = tagline "#{task.application.name}" diff --git a/lib/pages/games.rb b/lib/pages/games.rb index e31baef..3a6984b 100644 --- a/lib/pages/games.rb +++ b/lib/pages/games.rb @@ -4,7 +4,9 @@ class W3DHub def setup @game_news ||= {} @focused_game ||= Store.applications.games.find { |g| g.id == Store.settings[:last_selected_app] } + @focused_game ||= Store.applications.games.find { |g| g.id == "ren" } @focused_channel ||= @focused_game.channels.find { |c| c.id == Store.settings[:last_selected_channel] } + @focused_channel ||= @focused_game.channels.first body.clear do # Games List @@ -38,7 +40,9 @@ class W3DHub image "#{GAME_ROOT_PATH}/media/ui_icons/return.png", width: 1.0, color: Gosu::Color::GRAY if Store.application_manager.updateable?(game.id, game.channels.first.id) image "#{GAME_ROOT_PATH}/media/ui_icons/import.png", width: 0.5, color: 0x88_ffffff unless Store.application_manager.installed?(game.id, game.channels.first.id) end - image "#{GAME_ROOT_PATH}/media/icons/#{game.id}.png", height: 48, color: Store.application_manager.installed?(game.id, game.channels.first.id) ? 0xff_ffffff : 0x88_ffffff + image_path = File.exist?("#{GAME_ROOT_PATH}/media/icons/#{game.id}.png") ? "#{GAME_ROOT_PATH}/media/icons/#{game.id}.png" : "#{GAME_ROOT_PATH}/media/icons/app.png" + + image image_path, height: 48, color: Store.application_manager.installed?(game.id, game.channels.first.id) ? 0xff_ffffff : 0x88_ffffff end inscription game.name, width: 1.0, text_align: :center end @@ -168,20 +172,18 @@ class W3DHub end Async do - internet = Async::HTTP::Internet.instance - - fetch_game_news(internet, game) + fetch_game_news(game) populate_game_news(game) end end end - def fetch_game_news(internet, game) - news = Api.news(internet, game.id) + def fetch_game_news(game) + news = Api.news(game.id) if news news.items[0..9].each do |item| - Cache.fetch(internet, item.image) + Cache.fetch(item.image) end @game_news[game.id] = news diff --git a/lib/pages/login.rb b/lib/pages/login.rb index f07a261..96c4ca7 100644 --- a/lib/pages/login.rb +++ b/lib/pages/login.rb @@ -37,18 +37,20 @@ class W3DHub # Do network stuff Async do - internet = Async::HTTP::Internet.instance - - account = Api.user_login(internet, @username.value, @password.value) + account = Api.user_login(@username.value, @password.value) if account Store.account = account - Store.settings[:account][:refresh_token] = account.refresh_token + Store.settings[:account][:data] = account Store.settings.save_settings - Cache.fetch(internet, account.avatar_uri, true) + internet = Async::HTTP::Internet.instance + Cache.fetch(account.avatar_uri, true) populate_account_info + applications = Api.applications + Store.applications = applications if applications + page(W3DHub::Pages::Games) else # An error occurred, enable account entry @@ -70,8 +72,7 @@ class W3DHub if Store.account Async do - internet = Async::HTTP::Internet.instance - Cache.fetch(internet, Store.account.avatar_uri) + Cache.fetch(Store.account.avatar_uri) populate_account_info page(W3DHub::Pages::Games) @@ -98,10 +99,13 @@ class W3DHub end def depopulate_account_info - Store.settings[:account][:refresh_token] = nil + Store.settings[:account] = {} Store.settings.save_settings Store.account = nil + applications = Api.applications + Store.applications if applications + @host.instance_variable_get(:"@account_container").clear do stack(width: 0.7, height: 1.0) do # background 0xff_222222 diff --git a/lib/pages/server_browser.rb b/lib/pages/server_browser.rb index f14dd07..0f3f219 100644 --- a/lib/pages/server_browser.rb +++ b/lib/pages/server_browser.rb @@ -26,9 +26,11 @@ class W3DHub @filters.each do |app_id, enabled| app = Store.applications.games.find { |a| a.id == app_id.to_s } - image "#{GAME_ROOT_PATH}/media/icons/#{app_id}.png", tip: "#{app.name}", height: 1.0, + image_path = File.exist?("#{GAME_ROOT_PATH}/media/icons/#{app_id}.png") ? "#{GAME_ROOT_PATH}/media/icons/#{app_id}.png" : "#{GAME_ROOT_PATH}/media/icons/app.png" + + image image_path, tip: "#{app.name}", height: 1.0, border_thickness_bottom: 1, border_color_bottom: 0x00_000000, - color: enabled ? 0xff_ffffff : 0xff_444444, hover: { border_color_bottom: 0xff_aaaaaa }, margin_right: 32 do |img| + color: enabled ? 0xff_ffffff : 0xff_444444, hover: { border_color_bottom: 0xff_aaaaaa }, margin_right: 16 do |img| @filters[app_id] = !@filters[app_id] Store.settings[:server_list_filters] = @filters Store.settings.save_settings @@ -359,9 +361,11 @@ class W3DHub end def game_icon(server) + image_path = File.exist?("#{GAME_ROOT_PATH}/media/icons/#{server.game.nil? ? 'ren' : server.game}.png") ? "#{GAME_ROOT_PATH}/media/icons/#{server.game.nil? ? 'ren' : server.game}.png" : "#{GAME_ROOT_PATH}/media/icons/app.png" + if server.status.password @server_locked_icons[server.game] ||= Gosu.render(96, 96) do - i = get_image("#{GAME_ROOT_PATH}/media/icons/#{server.game.nil? ? 'ren' : server.game}.png") + i = get_image(image_path) lock = get_image("#{GAME_ROOT_PATH}/media/ui_icons/locked.png") scale = [96.0 / i.width, 96.0 / i.height].min @@ -369,7 +373,7 @@ class W3DHub lock.draw(96 - lock.width * 0.5, 96 - lock.height * 0.5, 0, 0.5, 0.5, 0xff_ff8800) end else - "#{GAME_ROOT_PATH}/media/icons/#{server.game.nil? ? 'ren' : server.game}.png" + image_path end end diff --git a/lib/states/boot.rb b/lib/states/boot.rb index c477045..82d22e0 100644 --- a/lib/states/boot.rb +++ b/lib/states/boot.rb @@ -32,11 +32,9 @@ class W3DHub end Async do - internet = Async::HTTP::Internet.instance - @tasks.keys.each do |key| Sync do - send(key, internet) + send(key) end end end @@ -56,26 +54,29 @@ class W3DHub @progressbar.value = @fraction - if @progressbar.value >= 1.0 && @task_index == @tasks.size - Store.account = @account - Store.service_status = @service_status - Store.applications = @applications - - push_state(States::Interface) - end + push_state(States::Interface) if @progressbar.value >= 1.0 && @task_index == @tasks.size @task_index += 1 if @tasks.dig(@tasks.keys[@task_index], :complete) end - def refresh_user_token(internet) - if Store.settings[:account, :refresh_token] - @account = Api.refresh_user_login(internet, Store.settings[:account, :refresh_token]) + def refresh_user_token + if Store.settings[:account, :data] + account = Api::Account.new(Store.settings[:account, :data], {}) + + if (Time.now.to_i - account.access_token_expiry.to_i) >= 60 * 3 # Older than 3 hours then refresh + @account = Api.refresh_user_login(account.refresh_token) + else + @account = account + end if @account - Store.settings[:account][:refresh_token] = @account.refresh_token - Cache.fetch(internet, @account.avatar_uri, true) + Store.account = @account + + Store.settings[:account][:data] = @account + + Cache.fetch(@account.avatar_uri, true) else - Store.settings[:account][:refresh_token] = nil + Store.settings[:account] = {} end Store.settings.save_settings @@ -86,10 +87,12 @@ class W3DHub end end - def service_status(internet) - @service_status = Api.service_status(internet) + def service_status + @service_status = Api.service_status if @service_status + Store.service_status = @service_status + if !@service_status.authentication? || !@service_status.package_download? @status_label.value = "Authentication is #{@service_status.authentication? ? 'Okay' : 'Down'}. Package Download is #{@service_status.package_download? ? 'Okay' : 'Down'}." end @@ -100,22 +103,26 @@ class W3DHub end end - def applications(internet) + def applications @status_label.value = I18n.t(:"boot.checking_for_updates") - @applications = Api.applications(internet) + @applications = Api.applications if @applications + Store.applications = @applications + @tasks[:applications][:complete] = true else # FIXME: Failed to retreive! end end - def server_list(internet) + def server_list @status_label.value = I18n.t(:"server_browser.fetching_server_list") begin + internet = Async::HTTP::Internet.instance + list = Api.server_list(internet, 2) if list