mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2026-09-21 15:53:58 +00:00
Compare commits
3 Commits
17614f4416
...
53e7a72ceb
| Author | SHA1 | Date | |
|---|---|---|---|
| 53e7a72ceb | |||
| 0a1909a84c | |||
| b7739de136 |
@@ -12,7 +12,7 @@ module W3DHubLauncher
|
|||||||
LIBRARIES = [
|
LIBRARIES = [
|
||||||
Item.new("Ruby", "Programming language. A Programmer's Best Friend", "https://ruby-lang.org", "BSD 2-Clause", "https://www.ruby-lang.org/en/about/license.txt"),
|
Item.new("Ruby", "Programming language. A Programmer's Best Friend", "https://ruby-lang.org", "BSD 2-Clause", "https://www.ruby-lang.org/en/about/license.txt"),
|
||||||
Item.new("gosu", "Light-weight game library", "https://libgosu.org", "MIT", "https://github.com/gosu/gosu/blob/master/COPYING"),
|
Item.new("gosu", "Light-weight game library", "https://libgosu.org", "MIT", "https://github.com/gosu/gosu/blob/master/COPYING"),
|
||||||
Item.new("SDL2", "Simple DirectMedia Layer", "https://libsdl.org", "MIT", "https://github.com/libsdl-org/SDL/blob/SDL2/LICENSE.txt"),
|
Item.new("SDL3", "Simple DirectMedia Layer", "https://libsdl.org", "zlib", "https://github.com/libsdl-org/SDL/blob/main/LICENSE.txt"),
|
||||||
Item.new("MojoAL", "OpenAL sound library implementation in a single C file", "https://icculus.org/mojoAL/", "MIT", "https://github.com/icculus/mojoAL/blob/main/LICENSE.txt"),
|
Item.new("MojoAL", "OpenAL sound library implementation in a single C file", "https://icculus.org/mojoAL/", "MIT", "https://github.com/icculus/mojoAL/blob/main/LICENSE.txt"),
|
||||||
|
|
||||||
Item.new("async", "Asynchronous event-driven reactor for Ruby", "https://github.com/socketry/async", "MIT", "https://github.com/socketry/async/blob/main/license.md"),
|
Item.new("async", "Asynchronous event-driven reactor for Ruby", "https://github.com/socketry/async", "MIT", "https://github.com/socketry/async/blob/main/license.md"),
|
||||||
|
|||||||
280
lib/dds.rb
Normal file
280
lib/dds.rb
Normal file
@@ -0,0 +1,280 @@
|
|||||||
|
require "stringio"
|
||||||
|
|
||||||
|
module W3DHubLauncher
|
||||||
|
class DDS
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-header
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-pixelformat
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dx-graphics-dds-pguide
|
||||||
|
# https://docs.microsoft.com/en-us/windows/win32/direct3ddds/dds-file-layout-for-textures
|
||||||
|
|
||||||
|
DXT1 = 827611204
|
||||||
|
DXT2 = 844388420
|
||||||
|
DXT3 = 861165636
|
||||||
|
DXT4 = 877942852
|
||||||
|
DXT5 = 894720068
|
||||||
|
|
||||||
|
DDSD_CAPS = 0x1
|
||||||
|
DDSD_HEIGHT = 0x2
|
||||||
|
DDSD_WIDTH = 0x4
|
||||||
|
DDSD_PITCH = 0x8
|
||||||
|
DDSD_PIXELFORMAT = 0x1000
|
||||||
|
DDSD_MIPMAPCOUNT = 0x20_000
|
||||||
|
DDSD_LINEARSIZE = 0x80_000
|
||||||
|
DDSD_DEPTH = 0x800_000
|
||||||
|
|
||||||
|
DDSCAPS_COMPLEX = 0x8
|
||||||
|
DDSCAPS_MIPMAP = 0x400_000
|
||||||
|
DDSCAPS_TEXTURE = 0x1000
|
||||||
|
|
||||||
|
DDSCAPS2_CUBEMAP = 0x200
|
||||||
|
DDSCAPS2_CUBEMAP_POSITIVEX = 0x400
|
||||||
|
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800
|
||||||
|
DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000
|
||||||
|
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000
|
||||||
|
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000
|
||||||
|
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000
|
||||||
|
DDSCAPS2_VOLUME = 0x200_000
|
||||||
|
|
||||||
|
DDPF_ALPHAPIXELS = 0x1
|
||||||
|
DDPF_ALPHA = 0x2
|
||||||
|
DDPF_FOURCC = 0x4
|
||||||
|
DDPF_RGB = 0x40
|
||||||
|
DDPF_YUV = 0x200
|
||||||
|
DDPF_LUMINANCE = 0x20_000
|
||||||
|
|
||||||
|
Header = Struct.new(
|
||||||
|
:byte_size, :flags, :height, :width, :pitch_or_linear_size,
|
||||||
|
:depth, :mipmap_count, :reserved1, :pixel_format,
|
||||||
|
:caps, :caps2, :caps3, :caps4, :reserved2
|
||||||
|
)
|
||||||
|
|
||||||
|
HeaderX10 = Struct.new(:value)
|
||||||
|
|
||||||
|
PixelFormat = Struct.new(
|
||||||
|
:byte_size, :flags, :four_cc, :rgb_bit_count,
|
||||||
|
:red_bit_mask, :green_bit_mask, :blue_bit_mask, :alpha_bit_mask
|
||||||
|
)
|
||||||
|
|
||||||
|
Image = Struct.new(:data, :width, :height)
|
||||||
|
|
||||||
|
attr_reader :header, :images
|
||||||
|
def initialize(path: nil, io: nil, shallow: true)
|
||||||
|
if path
|
||||||
|
@data = StringIO.new(File.binread(path))
|
||||||
|
elsif io
|
||||||
|
@data = io
|
||||||
|
else
|
||||||
|
raise "Nah man."
|
||||||
|
end
|
||||||
|
|
||||||
|
@shallow = shallow
|
||||||
|
@images = []
|
||||||
|
|
||||||
|
decode_header
|
||||||
|
decode_images
|
||||||
|
end
|
||||||
|
|
||||||
|
# typedef struct {
|
||||||
|
# DWORD dwSize;
|
||||||
|
# DWORD dwFlags;
|
||||||
|
# DWORD dwHeight;
|
||||||
|
# DWORD dwWidth;
|
||||||
|
# DWORD dwPitchOrLinearSize;
|
||||||
|
# DWORD dwDepth;
|
||||||
|
# DWORD dwMipMapCount;
|
||||||
|
# DWORD dwReserved1[11];
|
||||||
|
# DDS_PIXELFORMAT ddspf;
|
||||||
|
# DWORD dwCaps;
|
||||||
|
# DWORD dwCaps2;
|
||||||
|
# DWORD dwCaps3;
|
||||||
|
# DWORD dwCaps4;
|
||||||
|
# DWORD dwReserved2;
|
||||||
|
# } DDS_HEADER;
|
||||||
|
|
||||||
|
def decode_header
|
||||||
|
@data.pos = 0
|
||||||
|
|
||||||
|
raise "File is not a DDS" unless read_u32 == 0x20534444 # magic byte "DDS"
|
||||||
|
|
||||||
|
size = read_u32
|
||||||
|
flags = read_u32
|
||||||
|
height = read_u32
|
||||||
|
width = read_u32
|
||||||
|
pitch_or_linear_size = read_u32
|
||||||
|
depth = read_u32
|
||||||
|
mipmap_count = read_u32
|
||||||
|
reserved1 = 11.times.map { read_u32 }
|
||||||
|
|
||||||
|
pixel_format = read_pixelformat
|
||||||
|
|
||||||
|
caps = read_u32
|
||||||
|
caps2 = read_u32
|
||||||
|
caps3 = read_u32
|
||||||
|
caps4 = read_u32
|
||||||
|
reserved2 = read_u32
|
||||||
|
|
||||||
|
@header = Header.new(
|
||||||
|
size, flags, height, width, pitch_or_linear_size, depth, mipmap_count,
|
||||||
|
reserved1, pixel_format, caps, caps2, caps3, caps4, reserved2
|
||||||
|
)
|
||||||
|
|
||||||
|
@header.freeze
|
||||||
|
|
||||||
|
raise "Invalid dds header size: #{size}, expected 124" unless size == 124
|
||||||
|
raise "Invalid pixel format header size: #{pixel_format.byte_size}, expected 32" unless pixel_format.byte_size == 32
|
||||||
|
end
|
||||||
|
|
||||||
|
def decode_images
|
||||||
|
pixel_format = @header.pixel_format
|
||||||
|
|
||||||
|
raise "Unsupported dds format: #{pixel_format.four_cc}" unless (pixel_format.four_cc == DXT1 || pixel_format.four_cc == DXT3 || pixel_format.four_cc == DXT5)
|
||||||
|
raise "Unsupported pixel format: #{pixel_format.flags}" unless pixel_format.flags == DDPF_FOURCC
|
||||||
|
|
||||||
|
width = @header.width
|
||||||
|
height = @header.height
|
||||||
|
mipmap_count = [1, @header.mipmap_count].max
|
||||||
|
mipmap_count = 1 if @shallow
|
||||||
|
block_bytes = 8
|
||||||
|
block_bytes = 16 if pixel_format.four_cc == DXT3 || pixel_format.four_cc == DXT5
|
||||||
|
data_offset = @header.byte_size + 4
|
||||||
|
|
||||||
|
mipmap_count.times do
|
||||||
|
data_length = [4, width].max / 4 * [4, height].max / 4 * block_bytes
|
||||||
|
|
||||||
|
@data.pos = data_offset
|
||||||
|
image = Image.new(
|
||||||
|
to_rgba(@data.read(data_length), width, height, pixel_format.four_cc == DXT1),
|
||||||
|
width,
|
||||||
|
height
|
||||||
|
)
|
||||||
|
|
||||||
|
@images << image
|
||||||
|
|
||||||
|
data_offset += data_length
|
||||||
|
width /= 2
|
||||||
|
height /= 2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_u32
|
||||||
|
@data.read(4).unpack1("L")
|
||||||
|
end
|
||||||
|
|
||||||
|
# struct DDS_PIXELFORMAT {
|
||||||
|
# DWORD dwSize;
|
||||||
|
# DWORD dwFlags;
|
||||||
|
# DWORD dwFourCC;
|
||||||
|
# DWORD dwRGBBitCount;
|
||||||
|
# DWORD dwRBitMask;
|
||||||
|
# DWORD dwGBitMask;
|
||||||
|
# DWORD dwBBitMask;
|
||||||
|
# DWORD dwABitMask;
|
||||||
|
# };
|
||||||
|
|
||||||
|
def read_pixelformat
|
||||||
|
|
||||||
|
size = read_u32
|
||||||
|
flags = read_u32
|
||||||
|
four_cc = read_u32
|
||||||
|
rgb_bit_count = read_u32
|
||||||
|
red_bit_mask = read_u32
|
||||||
|
green_bit_mask = read_u32
|
||||||
|
blue_bit_mask = read_u32
|
||||||
|
alpha_bit_mask = read_u32
|
||||||
|
|
||||||
|
PixelFormat.new(
|
||||||
|
size, flags, four_cc, rgb_bit_count,
|
||||||
|
red_bit_mask, green_bit_mask, blue_bit_mask, alpha_bit_mask
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# https://github.com/kchapelier/decode-dxt/
|
||||||
|
def to_rgba(data, width, height, dxt1 = false)
|
||||||
|
conversion_started = Gosu.milliseconds
|
||||||
|
|
||||||
|
data = StringIO.new(data)
|
||||||
|
rgba = Array.new(width * height * 4, 0) # StringIO.new("\0" * (width * height * 4), "wb") #
|
||||||
|
width_4 = (width / 4) | 0
|
||||||
|
height_4 = (height / 4) | 0
|
||||||
|
stride = 4
|
||||||
|
|
||||||
|
height_4.times do |h|
|
||||||
|
width_4.times do |w|
|
||||||
|
a, b, index = data.read(8).unpack("vvV")
|
||||||
|
color_values = interpolate_color_values(a, b, dxt1)
|
||||||
|
color_indices = index
|
||||||
|
|
||||||
|
4.times do |y|
|
||||||
|
4.times do |x|
|
||||||
|
pixel_index = (3 - x) + (y * 4)
|
||||||
|
rgba_index = (h * 4 + 3 - y) * width * 4 + (w * 4 + x) * 4
|
||||||
|
color_index = (color_indices >> (2 * (15 - pixel_index))) & 0x03
|
||||||
|
|
||||||
|
rgba[rgba_index] = color_values[color_index * 4]
|
||||||
|
rgba[rgba_index + 1] = color_values[color_index * 4 + 1]
|
||||||
|
rgba[rgba_index + 2] = color_values[color_index * 4 + 2]
|
||||||
|
rgba[rgba_index + 3] = color_values[color_index * 4 + 3]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
puts "Inital set complete after: #{Gosu.milliseconds - conversion_started}ms"
|
||||||
|
v = rgba.pack("C*") #.map { |v| v.chr }.join
|
||||||
|
puts "type set complete after: #{Gosu.milliseconds - conversion_started}ms"
|
||||||
|
|
||||||
|
v
|
||||||
|
end
|
||||||
|
|
||||||
|
def interpolate_color_values(a, b, dxt1 = false)
|
||||||
|
first_color = convert_565_byte_to_rgb(a)
|
||||||
|
second_color = convert_565_byte_to_rgb(b)
|
||||||
|
|
||||||
|
color_values = [
|
||||||
|
first_color[0], first_color[1], first_color[2], 255,
|
||||||
|
second_color[0], second_color[1], second_color[2], 255
|
||||||
|
]
|
||||||
|
|
||||||
|
if (dxt1 && a <= b)
|
||||||
|
color_values.push(
|
||||||
|
(first_color[0] + second_color[0]) / 2,
|
||||||
|
(first_color[1] + second_color[1]) / 2,
|
||||||
|
(first_color[2] + second_color[2]) / 2,
|
||||||
|
255,
|
||||||
|
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
else
|
||||||
|
color_values.push(
|
||||||
|
lerp(first_color[0], second_color[0], 1 / 3),
|
||||||
|
lerp(first_color[1], second_color[1], 1 / 3),
|
||||||
|
lerp(first_color[2], second_color[2], 1 / 3),
|
||||||
|
255,
|
||||||
|
|
||||||
|
lerp(first_color[0], second_color[0], 2 / 3),
|
||||||
|
lerp(first_color[1], second_color[1], 2 / 3),
|
||||||
|
lerp(first_color[2], second_color[2], 2 / 3),
|
||||||
|
255
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
color_values
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_565_byte_to_rgb(byte)
|
||||||
|
[
|
||||||
|
((byte >> 11) & 31) * (255 / 31),
|
||||||
|
((byte >> 5) & 63) * (255 / 63),
|
||||||
|
(byte & 31) * (255 / 31)
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def lerp(a, b, r)
|
||||||
|
a * (1 - r) + b * r
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -47,10 +47,12 @@ module W3DHubLauncher
|
|||||||
tagline item.name, margin_left: PADDING
|
tagline item.name, margin_left: PADDING
|
||||||
end
|
end
|
||||||
para item.description, margin_left: LARGE_PADDING
|
para item.description, margin_left: LARGE_PADDING
|
||||||
link item.license, tip: item.license_url, margin_left: PADDING + LARGE_PADDING unless item.license.empty? do
|
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)
|
SDL.OpenURL(item.license_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
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) }
|
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) }
|
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
|
raise RuntimeError, "Images may only be loaded from the main thread!" unless Thread.current == Thread.main
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@@ -12,8 +12,11 @@ module W3DHubLauncher
|
|||||||
pp e
|
pp e
|
||||||
end
|
end
|
||||||
|
|
||||||
path = "#{ROOT_PATH}/media/default.png"
|
begin
|
||||||
return get_image(path, retro: retro) if File.exist?(path)
|
return get_image(fallback_path, retro: retro) if File.exist?(fallback_path)
|
||||||
|
rescue RuntimeError => e
|
||||||
|
pp e
|
||||||
|
end
|
||||||
|
|
||||||
WHITE_IMAGE
|
WHITE_IMAGE
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -165,9 +165,33 @@ module W3DHubLauncher
|
|||||||
|
|
||||||
@server_details_container.clear do
|
@server_details_container.clear do
|
||||||
tagline server.name, width: 1.0, text_wrap: :none, tip: server.name
|
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
|
button "JOIN SERVER", **CTA_BUTTON_THEME, width: 1.0, enabled: !application.nil?, tip: application.nil? ? "Application not installed" : "" do
|
||||||
ApplicationHelper.join_server(application, server)
|
ApplicationHelper.join_server(application, server)
|
||||||
end
|
end
|
||||||
@@ -303,6 +327,34 @@ module W3DHubLauncher
|
|||||||
end
|
end
|
||||||
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)
|
def button_up(id)
|
||||||
super
|
super
|
||||||
|
|
||||||
|
|||||||
@@ -301,6 +301,66 @@ module W3DHubLauncher
|
|||||||
deliver_response(result, query)
|
deliver_response(result, query)
|
||||||
end
|
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)
|
def dns_resolution(query)
|
||||||
result = CyberarmEngine::Result.new
|
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)
|
Worker::Request.new(:ico_to_png, { ico_path: ico_path, png_path: png_path }, &block)
|
||||||
end
|
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)
|
def self.dns_resolution(&block)
|
||||||
Worker::Request.new(:dns_resolution, "", &block)
|
Worker::Request.new(:dns_resolution, "", &block)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ module W3DHubLauncher
|
|||||||
0xff_3d3846
|
0xff_3d3846
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
class Team
|
class Team
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ module W3DHubLauncher
|
|||||||
end
|
end
|
||||||
|
|
||||||
def application_installed?(application, channel)
|
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
|
end
|
||||||
|
|
||||||
# User explictly set options
|
# User explictly set options
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 16 KiB |
@@ -47,6 +47,7 @@ require_relative "lib/window"
|
|||||||
require_relative "lib/application_helper"
|
require_relative "lib/application_helper"
|
||||||
|
|
||||||
require_relative "lib/ico"
|
require_relative "lib/ico"
|
||||||
|
require_relative "lib/dds"
|
||||||
require_relative "lib/ww_mix"
|
require_relative "lib/ww_mix"
|
||||||
|
|
||||||
require_relative "lib/worker"
|
require_relative "lib/worker"
|
||||||
|
|||||||
Reference in New Issue
Block a user