diff --git a/lib/pages/boot/start_up.rb b/lib/pages/boot/start_up.rb index 868d31b..a6728e9 100644 --- a/lib/pages/boot/start_up.rb +++ b/lib/pages/boot/start_up.rb @@ -55,6 +55,17 @@ module W3DHubLauncher caption "Fatal: Failed to retrieve applications list and no local cache exists. Cannot continue.", height: 1.0, text_v_align: :center, color: 0xff_ff0000 end after(3000) do + puts "HELLO" + W3DHubLauncher::Worker::Request.new(:w3dhub_api_call, { call: :fetch_applications }) do |result| + pp [:CALLBACK, result] + File.write("applications.json", result.data) + hash = JSON.parse(result.data) + pp applications = hash["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || [] + pp applications.to_json + + MemCache[:applications] = applications + end + f.show @progress_bar.type = :linear @progress_bar.value = 0.0 diff --git a/lib/pages/games.rb b/lib/pages/games.rb index b7abf7b..25246f5 100644 --- a/lib/pages/games.rb +++ b/lib/pages/games.rb @@ -4,6 +4,11 @@ module W3DHubLauncher include GuiExt def setup + @games = MemCache[:applications]&.select { |app| app.type.include?("game") } || [] + + @current_app = @games.first + @current_channel = @current_app&.channels&.first + # game bar container flow(width: 1.0, height: 60) do widget(width: 220, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, hover: { background_nine_slice_color: ALPHA_BLACK }, active: { background_nine_slice_color: ALPHA_GRAY }) do @@ -13,65 +18,119 @@ module W3DHubLauncher end end - flow(fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, margin_left: PADDING) do - image safe_get_image("#{ROOT_PATH}/data/cache/apb.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0x88_5e5c64, border_thickness_bottom: 3, border_color_bottom: 0xff_3584e4, tip: "Red Alert: A Path Beyond" - image safe_get_image("#{ROOT_PATH}/data/cache/ren.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Command & Conquer: Renegade" - image safe_get_image("#{ROOT_PATH}/data/cache/tsr.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Tiberian Sun: Reborn" - image safe_get_image("#{ROOT_PATH}/data/cache/woa.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: "Battle for Dune: War of Assassins" + @games_list_container = flow(fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, margin_left: PADDING) do end end # game content container flow(width: 1.0, fill: true, margin_top: LARGE_PADDING) do # game info container - stack(width: 340, height: 1.0) do - # logo - image safe_get_image("#{ROOT_PATH}/media/default.png"), width: 1.0, max_height: 124 - - # web links - stack(width: 1.0, fill: true, padding: 0, padding_top: LARGE_PADDING) do - link "Modifications", text_size: 24 - link "Bug Tracker", text_size: 24 - link "Player Statistics", text_size: 24 - end - - # launching ta game - caption "Game Version" - list_box items: [ "Release", "Open Testing" ], width: 1.0, margin_bottom: PADDING - flow(width: 1.0, height: 60) do - button "PLAY", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME - button safe_get_image("#{ROOT_PATH}/media/icons/singleplayer.png"), image_height: 1.0, background_nine_slice: NINE_SLICE_SQUARE, **CTA_BUTTON_THEME - button safe_get_image("#{ROOT_PATH}/media/icons/gear.png"), image_height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_RIGHT, **CTA_BUTTON_THEME - end - inscription "Version: 3.9.2.15", margin_top: PADDING + @game_info_container = stack(width: 340, height: 1.0) do end # game events and news container stack(fill: true, height: 1.0, margin_left: LARGE_PADDING, scroll: true) do - flow(width: 1.0, height: 1.0, max_height: 380, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do - image safe_get_image("#{ROOT_PATH}/media/background.png"), fill: true, aspect_ratio: 16.0 / 9.0 - - stack(fill: true, height: 1.0, margin_left: PADDING) do - caption "Upcoming Event".upcase, color: 0xff_22aa11 - title "Red Alert: A Path Beyond Game Night" - tagline "July 11, 2028" - - flow(fill: true) - - button "Read More", margin_left: PADDING, margin_right: LARGE_PADDING, margin_bottom: PADDING, width: 1.0 - end + @event_container = flow(width: 1.0, height: 1.0, max_height: 380, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY) do end # news container - flow(width: 1.0, margin_top: PADDING) do - 9.times do - stack(width: 1.0 / 3, min_width: 345, height: 345, aspect_ratio: 1, margin_left: HALF_PADDING, margin_right: HALF_PADDING, margin_bottom: PADDING) do - stack(width: 1.0, fill: true, background_image: safe_get_image("#{ROOT_PATH}/media/background.png"), background_image_mode: :fill) - stack(width: 1.0, height: 1.0 / 3, padding: PADDING, v_align: :bottom, background_nine_slice: NINE_SLICE_ROUNDED_BOTTOM, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, border_thickness_top: 1, border_color_top: Gosu::Color::BLACK) do - caption "NEWS", color: 0x88_ffffff - tagline "A News Item Post A News Item Post" - end - end + @news_container = flow(width: 1.0, margin_top: PADDING) do + end + end + end + + populate_games_list + populate_game_info + populate_game_event + populate_game_news + end + + def populate_games_list + @games_list_container.clear do + @games.each_with_index do |game, i| + if i.zero? + image(safe_get_image("#{ROOT_PATH}/data/cache/#{game.id}.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0x88_5e5c64, border_thickness_bottom: 3, border_color_bottom: 0xff_3584e4, tip: game.name)do + @current_app = game + @current_channel = game&.channels&.first + + populate_games_list + populate_game_info + populate_game_event + populate_game_news + end + else + image(safe_get_image("#{ROOT_PATH}/data/cache/#{game.id}.png"), height: 1.0, padding: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED_TOP, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: 0, tip: game.name)do + @current_app = game + @current_channel = game&.channels&.first + + populate_games_list + populate_game_info + populate_game_event + populate_game_news + end + end + end + end + end + + def populate_game_info + @game_info_container.clear do + # logo + image safe_get_image("#{ROOT_PATH}/media/#{@current_app.id}.png"), width: 1.0, max_height: 124 + + # web links + stack(width: 1.0, fill: true, padding: 0, padding_top: LARGE_PADDING) do + @current_app.web_links.each do |link| + link link.name, text_size: 24, tip: link.uri + end + end + + # launching ta game + if (@current_app&.channels&.size || 0) > 1 + caption "Game Version" + list_box(items: @current_app.channels.map(&:name), choose: @current_channel&.name, width: 1.0, margin_bottom: PADDING) do |item| + @current_channel = @current_app.channels.find { |c| c.name == item } + populate_game_info + end + end + flow(width: 1.0, height: 60) do + 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 + 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 + button safe_get_image("#{ROOT_PATH}/media/icons/gear.png"), tip: "Options", image_height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_RIGHT, **CTA_BUTTON_THEME + end + # FIXME: the reported version should be the _installed_ version, not the current channel version + inscription "Version: #{@current_channel.version}", margin_top: PADDING, tip: "Installed version" + end + end + + def populate_game_event + # TODO: Hide event container if there is no event + @event_container.show if false + @event_container.hide if true + + @event_container.clear do + image safe_get_image("#{ROOT_PATH}/media/background.png"), fill: true, aspect_ratio: 16.0 / 9.0 + + stack(fill: true, height: 1.0, margin_left: PADDING) do + caption "Upcoming Event".upcase, color: 0xff_22aa11 + title "Red Alert: A Path Beyond Game Night" + tagline "July 11, 2028" + + flow(fill: true) + + button "Read More", margin_left: PADDING, margin_right: LARGE_PADDING, margin_bottom: PADDING, width: 1.0 + end + end + end + + def populate_game_news + @news_container.clear do + 9.times do + stack(width: 1.0 / 3, min_width: 345, height: 345, aspect_ratio: 1, margin_left: HALF_PADDING, margin_right: HALF_PADDING, margin_bottom: PADDING) do + stack(width: 1.0, fill: true, background_image: safe_get_image("#{ROOT_PATH}/media/background.png"), background_image_mode: :fill) + stack(width: 1.0, height: 1.0 / 3, padding: PADDING, v_align: :bottom, background_nine_slice: NINE_SLICE_ROUNDED_BOTTOM, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_color: ALPHA_GRAY, border_thickness_top: 1, border_color_top: Gosu::Color::BLACK) do + caption "NEWS", color: 0x88_ffffff + tagline "A News Item Post A News Item Post" end end end diff --git a/lib/theme.rb b/lib/theme.rb index cd8a0fb..42e7145 100644 --- a/lib/theme.rb +++ b/lib/theme.rb @@ -77,6 +77,8 @@ module W3DHubLauncher text_shadow: false, color: 0xff_ffffff, background: 0,#x88_5e5c64, + padding_left: HALF_PADDING, + padding_right: HALF_PADDING, background_nine_slice: NINE_SLICE_ROUNDED, background_nine_slice_from_edge: NINE_SLICE_EDGE, background_nine_slice_mode: :stretched, diff --git a/lib/worker.rb b/lib/worker.rb index dd07756..6671b49 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -36,17 +36,18 @@ module W3DHubLauncher UNIXServer.open(IPC_PATH) do |server| while(socket = server.accept) task.async do - data = socket.gets - json = JSON.parse(data) - query = Request::Query.new(type: json["type"].to_sym, request_id: json["request_id"], data: json["data"]) + 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] + 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.data.data }.to_json - socket.puts(payload) + 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.data.data }.to_json + socket.puts(payload) + end end end end diff --git a/lib/worker/api/application.rb b/lib/worker/api/application.rb new file mode 100644 index 0000000..f549556 --- /dev/null +++ b/lib/worker/api/application.rb @@ -0,0 +1,92 @@ +module W3DHubLauncher + class Worker + class Api + class Application + attr_reader :id, :category, :name, :type, :channels, :extended_data, :web_links + + def initialize(data) + pp data + + @id = data["id"] + @category = data["category"] + @name = data["name"] + @type = data["type"] + + @channels = data["channels"]&.map { |c| Channel.new(c) } || [] + + @extended_data = data["extended-data"]&.map { |d| ExtendedData.new(d) } || nil + @web_links = data["web-links"]&.map { |d| WebLink.new(d) } || nil + end + + # libgosu friendly color + def color + "ff_#{(@extended_data["colour"] || "#000000").sub("#", "")}".to_i(16) + end + + def to_json(context = nil) + { + "id": @id, + "category": @category, + "name": @name, + "type": @type, + "channels": @channels, + "extended-data": @extended_data, + "web-links": @web_links + }.to_json(context) + end + + class Channel + attr_reader :id, :name, :user_level, :version + + def initialize(data) + @id = data["id"] + @name = data["name"] + @user_level = data["user-level"] + @version = Gem::Version.new(data["current-version"]) + end + + def to_json(context = nil) + { + "id": @id, + "name": @name, + "user-level": @user_level, + "current-version": @version.to_s + }.to_json(context) + end + end + + class ExtendedData + attr_reader :name, :value + + def initialize(data) + @name = data["name"] + @value = data["value"] + end + + def to_json(context = nil) + { + "name": @name, + "value": @value + }.to_json(context) + end + end + + class WebLink + attr_reader :name, :uri + + def initialize(data) + @name = data["name"] + @uri = data["uri"] + end + + def to_json(context = nil) + { + "name": @name, + "uri": @uri + }.to_json(context) + end + end + end + end + end +end diff --git a/w3d_hub_linux_launcher.rb b/w3d_hub_linux_launcher.rb index ca40b8c..bbde029 100644 --- a/w3d_hub_linux_launcher.rb +++ b/w3d_hub_linux_launcher.rb @@ -36,6 +36,7 @@ require_relative "lib/window" require_relative "lib/worker" require_relative "lib/worker/api" +require_relative "lib/worker/api/application" require_relative "lib/worker/request" require_relative "lib/worker/w3dhub_api" require_relative "lib/worker/task" @@ -45,6 +46,8 @@ require_relative "lib/worker/tasks/repair_application" require_relative "lib/worker/tasks/update_application" module W3DHubLauncher + MemCache = {} + # UNIXServer Thread.new do W3DHubLauncher::Worker.new.listen @@ -55,12 +58,6 @@ module W3DHubLauncher WORKER.connect end -# 10.times do - # W3DHubLauncher::Worker::Request.new(:w3dhub_api_call, { call: :fetch_applications }) do |result| - # pp [:CALLBACK, result] - # end -# end - window = W3DHubLauncher::Window.new(width: 1280, height: 800, resizable: true) # window = W3DHubLauncher::Window.new(width: 1920, height: 1080, resizable: true) window.show