diff --git a/lib/dialogs/about.rb b/lib/dialogs/about.rb index 2ff69ab..ca0728d 100644 --- a/lib/dialogs/about.rb +++ b/lib/dialogs/about.rb @@ -47,8 +47,10 @@ module W3DHubLauncher tagline item.name, margin_left: PADDING end para item.description, margin_left: LARGE_PADDING - link item.license, tip: item.license_url, margin_left: PADDING + LARGE_PADDING unless item.license.empty? do - SDL.OpenURL(item.license_url) + unless item.license.empty? + link format("%s license", item.license), tip: item.license_url, margin_left: PADDING + LARGE_PADDING do + SDL.OpenURL(item.license_url) + end end end end diff --git a/lib/gui_ext.rb b/lib/gui_ext.rb index 6a4b7ae..9dc55a0 100644 --- a/lib/gui_ext.rb +++ b/lib/gui_ext.rb @@ -3,7 +3,7 @@ module W3DHubLauncher BLACK_IMAGE = Gosu.render(64, 64, retro: true) { Gosu.draw_rect(0, 0, 32, 32, Gosu::Color::BLACK) } WHITE_IMAGE = Gosu.render(64, 64, retro: true) { Gosu.draw_rect(0, 0, 32, 32, Gosu::Color::WHITE) } - def safe_get_image(path, retro: false) + def safe_get_image(path, fallback_path: "#{ROOT_PATH}/media/default.png", retro: false) raise RuntimeError, "Images may only be loaded from the main thread!" unless Thread.current == Thread.main begin @@ -12,8 +12,11 @@ module W3DHubLauncher pp e end - path = "#{ROOT_PATH}/media/default.png" - return get_image(path, retro: retro) if File.exist?(path) + begin + return get_image(fallback_path, retro: retro) if File.exist?(fallback_path) + rescue RuntimeError => e + pp e + end WHITE_IMAGE end diff --git a/lib/states/interface.rb b/lib/states/interface.rb index d8f65e2..357b3a3 100644 --- a/lib/states/interface.rb +++ b/lib/states/interface.rb @@ -165,9 +165,33 @@ module W3DHubLauncher @server_details_container.clear do tagline server.name, width: 1.0, text_wrap: :none, tip: server.name - image safe_get_image("#{ROOT_PATH}/media/default_map_preview.png"), width: 1.0, aspect_ratio: 16 / 9.0, tip: server.current_map, margin_bottom: PADDING + map_image_container = stack(width: 1.0, height: (@server_details_container.content_width / 640.0) * 360, background_image: safe_get_image(server.map_preview_image_path, fallback_path: "#{ROOT_PATH}/media/default_map_preview.png")) do + caption( + server.current_map, + text_border: false, + text_border_size: 1, + text_border_color: Gosu::Color::RED, + text_shadow: false, + width: 1.0, + height: 1.0, + padding_bottom: PADDING, + text_align: :center, + text_v_align: :bottom, + tip: server.current_map + ) + end + unless File.exist?(server.map_preview_image_path) + Worker::Api.server_map_image(server) do |result, status| + # If the requested image is still the needed image? + next unless @server_details_container.children.include?(map_image_container) + next unless result.okay? - flow(width: 1.0) do + post_process_map_preview_image(result.data["image_path"]) + map_image_container.background_image = safe_get_image(result.data["image_path"]) + end + end + + flow(width: 1.0, margin_top: PADDING) do button "JOIN SERVER", **CTA_BUTTON_THEME, width: 1.0, enabled: !application.nil?, tip: application.nil? ? "Application not installed" : "" do ApplicationHelper.join_server(application, server) end @@ -303,6 +327,34 @@ module W3DHubLauncher end end + # Get them rounded corners! + def post_process_map_preview_image(path) + image = Gosu::Image.new(path) + + target_width = 640 + target_height= 360 + + nine_slice = CyberarmEngine::BackgroundNineSlice.new( + image_path: NINE_SLICE_ROUNDED, + mode: :stretch, + width: target_width, + height: target_height, + left: NINE_SLICE_EDGE, + right: NINE_SLICE_EDGE, + top: NINE_SLICE_EDGE, + bottom: NINE_SLICE_EDGE, + render_mode: :multiply + ) + + composite = Gosu.render(target_width, target_height) do + image_scale = [target_width / image.width.to_f, target_height / image.height.to_f].max + image.draw_rot(target_width / 2, target_height / 2, 0, 0, 0.5, 0.5, image_scale, image_scale) + nine_slice.draw + end + + composite.save(path) + end + def button_up(id) super diff --git a/lib/worker.rb b/lib/worker.rb index f2fc6e6..a606c89 100644 --- a/lib/worker.rb +++ b/lib/worker.rb @@ -301,6 +301,66 @@ module W3DHubLauncher deliver_response(result, query) end + def server_map_image(query) + result = CyberarmEngine::Result.new + + # TODO: Ideally battleview would host readily accessible map previews + server = @game_servers.find { |s| s.address == query.data["server"]["address"] && s.port == query.data["server"]["port"] } + return deliver_response(result, query) unless server + + # is application installed to source data from? + application = @settings.application_installed?(server.game, server.channel) + return deliver_response(result, query) unless application + + save_path = server.map_preview_image_path + if File.exist?(save_path) + result.data = { image_path: save_path } + + return deliver_response(result, query) + end + + map_mix = Dir.glob("#{application.installation_path}/**/#{query.data["server"]["map"]}")&.first + return deliver_response(result, query) unless map_mix + + mix = W3DHubLauncher::WWMix.new(path: map_mix) + return deliver_response(result, query) unless mix.load + + entry = nil + canvas = nil + + entry = mix.entries.find { |e| e.name.match?(/_map_preview\.png/i) } + entry ||= mix.entries.find { |e| e.name.match?(/_map_preview\.dds/i) } + entry ||= mix.entries.find { |e| e.name.match?(/screenshot\.png/i) } + entry ||= mix.entries.find { |e| e.name.match?(/screenshot\.dds/i) } + entry ||= mix.entries.find { |e| e.name.match?(/_map\.png/i) } + entry ||= mix.entries.find { |e| e.name.match?(/_map\.dds/i) } + + return deliver_response(result, query) unless entry + return deliver_response(result, query) unless entry.read + + if entry.name.match?(/\.dds/i) + begin + dds = W3DHubLauncher::DDS.new(io: StringIO.new(entry.blob)) + image_data = dds.images.first + + canvas = ChunkyPNG::Canvas.from_rgba_stream(image_data.width, image_data.height, image_data.data) + canvas.save(save_path) + rescue StandardError => e + result.error = e + + return deliver_response(result, query) + end + + elsif entry.name.match?(/\.png/i) + # canvas = ChunkyPNG::Canvas.from_blob(entry.blob) + File.binwrite(save_path, entry.blob) + end + + result.data = { image_path: save_path } + + deliver_response(result, query) + end + def dns_resolution(query) result = CyberarmEngine::Result.new diff --git a/lib/worker/api.rb b/lib/worker/api.rb index f6607a0..4522ea0 100644 --- a/lib/worker/api.rb +++ b/lib/worker/api.rb @@ -16,6 +16,10 @@ module W3DHubLauncher Worker::Request.new(:ico_to_png, { ico_path: ico_path, png_path: png_path }, &block) end + def self.server_map_image(server, &block) + Worker::Request.new(:server_map_image, { server: { address: server.address, port: server.port, map: server.current_map } }, &block) + end + def self.dns_resolution(&block) Worker::Request.new(:dns_resolution, "", &block) end diff --git a/lib/worker/api/game_server.rb b/lib/worker/api/game_server.rb index eaf80d2..7694cb8 100644 --- a/lib/worker/api/game_server.rb +++ b/lib/worker/api/game_server.rb @@ -89,6 +89,10 @@ module W3DHubLauncher 0xff_3d3846 end end + + def map_preview_image_path + format("%s/map_preview_%s.png", W3DHubLauncher::CACHE_PATH, Digest::SHA256.hexdigest("#{@game}_#{@channel}_#{@current_map}")) + end end class Team diff --git a/lib/worker/api/settings.rb b/lib/worker/api/settings.rb index a6e40db..851c8a7 100644 --- a/lib/worker/api/settings.rb +++ b/lib/worker/api/settings.rb @@ -35,7 +35,10 @@ module W3DHubLauncher end def application_installed?(application, channel) - applications.find { |app| app.id == application&.id && app.channel == channel&.id } + app_id = application.is_a?(String) ? application : application.id + channel_id = channel.is_a?(String) ? channel : channel.id + + applications.find { |app| app.id == app_id && app.channel == channel_id } end # User explictly set options diff --git a/media/default_map_preview.png b/media/default_map_preview.png index a15a2d8..585006d 100644 Binary files a/media/default_map_preview.png and b/media/default_map_preview.png differ diff --git a/w3d_hub_linux_launcher.rb b/w3d_hub_linux_launcher.rb index 4dd4ff5..8ddd280 100644 --- a/w3d_hub_linux_launcher.rb +++ b/w3d_hub_linux_launcher.rb @@ -47,6 +47,7 @@ require_relative "lib/window" require_relative "lib/application_helper" require_relative "lib/ico" +require_relative "lib/dds" require_relative "lib/ww_mix" require_relative "lib/worker"