mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2026-09-20 23:33:54 +00:00
Added initial support for extracting map preview images from game level mixes that include a screenshot or overhead map
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user