mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2026-03-22 20:26:16 +00:00
Compare commits
6 Commits
v0.9.0
...
9cb41a8693
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cb41a8693 | |||
| f024109327 | |||
| 287022f2b8 | |||
| 68df923bea | |||
| ddbec8d72c | |||
| 70d4e0c40f |
@@ -5,6 +5,7 @@ class W3DHub
|
|||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@tasks = [] # :installer, :importer, :repairer, :uninstaller
|
@tasks = [] # :installer, :importer, :repairer, :uninstaller
|
||||||
|
@running_applications = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def install(app_id, channel)
|
def install(app_id, channel)
|
||||||
@@ -22,9 +23,7 @@ class W3DHub
|
|||||||
# unpack packages
|
# unpack packages
|
||||||
# install dependencies (e.g. visual C runtime)
|
# install dependencies (e.g. visual C runtime)
|
||||||
|
|
||||||
installer = Installer.new(app_id, channel)
|
@tasks.push(Installer.new(app_id, channel))
|
||||||
|
|
||||||
@tasks.push(installer)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(app_id, channel)
|
def update(app_id, channel)
|
||||||
@@ -32,9 +31,7 @@ class W3DHub
|
|||||||
|
|
||||||
return false unless installed?(app_id, channel)
|
return false unless installed?(app_id, channel)
|
||||||
|
|
||||||
updater = Updater.new(app_id, channel)
|
@tasks.push(Updater.new(app_id, channel))
|
||||||
|
|
||||||
@tasks.push(updater)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def import(app_id, channel)
|
def import(app_id, channel)
|
||||||
@@ -169,11 +166,16 @@ class W3DHub
|
|||||||
def wine_command(app_id, channel)
|
def wine_command(app_id, channel)
|
||||||
return "" if W3DHub.windows?
|
return "" if W3DHub.windows?
|
||||||
|
|
||||||
if !Store.settings[:wine_prefix].to_s.empty?
|
"\"#{Store.settings[:wine_command]}\" "
|
||||||
"WINEPREFIX=\"#{Store.settings[:wine_prefix]}\" \"#{Store.settings[:wine_command]}\" "
|
end
|
||||||
else
|
|
||||||
"#{Store.settings[:wine_command]} "
|
def wine_enviroment_variables(app_id, channel)
|
||||||
end
|
vars = {}
|
||||||
|
return vars if W3DHub.windows?
|
||||||
|
|
||||||
|
vars["WINEPREFIX"] = Store.settings[:wine_prefix] unless Store.settings[:wine_prefix].to_s.empty?
|
||||||
|
|
||||||
|
vars
|
||||||
end
|
end
|
||||||
|
|
||||||
def mangohud_command(app_id, channel)
|
def mangohud_command(app_id, channel)
|
||||||
@@ -188,6 +190,13 @@ class W3DHub
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mangohud_enviroment_variables(app_id, channel)
|
||||||
|
vars = {}
|
||||||
|
return vars if W3DHub.windows?
|
||||||
|
|
||||||
|
vars
|
||||||
|
end
|
||||||
|
|
||||||
def dxvk_command(app_id, channel)
|
def dxvk_command(app_id, channel)
|
||||||
return "" if W3DHub.windows?
|
return "" if W3DHub.windows?
|
||||||
|
|
||||||
@@ -201,6 +210,13 @@ class W3DHub
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def dxvk_enviroment_variables(app_id, channel)
|
||||||
|
vars = {}
|
||||||
|
return vars if W3DHub.windows?
|
||||||
|
|
||||||
|
vars
|
||||||
|
end
|
||||||
|
|
||||||
def start_command(path, exe)
|
def start_command(path, exe)
|
||||||
if W3DHub.windows?
|
if W3DHub.windows?
|
||||||
"start /D \"#{path}\" /B #{exe}"
|
"start /D \"#{path}\" /B #{exe}"
|
||||||
@@ -212,16 +228,32 @@ class W3DHub
|
|||||||
def run(app_id, channel, *args)
|
def run(app_id, channel, *args)
|
||||||
if (app_data = installed?(app_id, channel))
|
if (app_data = installed?(app_id, channel))
|
||||||
install_directory = app_data[:install_directory]
|
install_directory = app_data[:install_directory]
|
||||||
exe_path = app_id == "ecw" ? "#{install_directory}/game500.exe" : "#{install_directory}/game.exe"
|
exe_path = app_id == "ecw" ? "#{install_directory}/game500.exe" : app_data[:install_path]
|
||||||
exe_path.gsub!("/", "\\") if W3DHub.windows?
|
exe_path.gsub!("/", "\\") if W3DHub.windows?
|
||||||
exe_path.gsub!("\\", "/") if W3DHub.unix?
|
exe_path.gsub!("\\", "/") if W3DHub.unix?
|
||||||
|
|
||||||
exe = File.basename(exe_path)
|
exe = File.basename(exe_path)
|
||||||
path = File.dirname(exe_path)
|
path = File.dirname(exe_path)
|
||||||
|
|
||||||
|
env = {}
|
||||||
|
if W3DHub.unix?
|
||||||
|
env.merge!(
|
||||||
|
dxvk_enviroment_variables(app_id, channel),
|
||||||
|
mangohud_enviroment_variables(app_id, channel),
|
||||||
|
wine_enviroment_variables(app_id, channel)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
attempted = false
|
attempted = false
|
||||||
begin
|
begin
|
||||||
pid = Process.spawn("#{dxvk_command(app_id, channel)}#{mangohud_command(app_id, channel)}#{wine_command(app_id, channel)}#{attempted ? start_command(path, exe) : "\"#{exe_path}\""} -launcher #{args.join(' ')}")
|
pid = Process.spawn(
|
||||||
|
env,
|
||||||
|
"#{dxvk_command(app_id, channel)}"\
|
||||||
|
"#{mangohud_command(app_id, channel)}"\
|
||||||
|
"#{wine_command(app_id, channel)}"\
|
||||||
|
"#{attempted ? start_command(path, exe) : "\"#{exe_path}\""} "\
|
||||||
|
"-launcher #{args.join(' ')}"
|
||||||
|
)
|
||||||
Process.detach(pid)
|
Process.detach(pid)
|
||||||
rescue Errno::EINVAL => e
|
rescue Errno::EINVAL => e
|
||||||
retryable = !attempted
|
retryable = !attempted
|
||||||
@@ -236,12 +268,14 @@ class W3DHub
|
|||||||
end
|
end
|
||||||
|
|
||||||
def join_server(app_id, channel, server, username = Store.settings[:server_list_username], password = nil, multi = false)
|
def join_server(app_id, channel, server, username = Store.settings[:server_list_username], password = nil, multi = false)
|
||||||
if installed?(app_id, channel) && username.to_s.length.positive?
|
return unless installed?(app_id, channel) && username.to_s.length.positive?
|
||||||
run(
|
|
||||||
app_id, channel,
|
run(
|
||||||
"+connect #{server.address}:#{server.port} +netplayername #{username}#{password ? " +password \"#{password}\"" : ""}#{multi ? " +multi" : ""}"
|
app_id, channel,
|
||||||
)
|
"+connect #{server.address}:#{server.port} "\
|
||||||
end
|
"+netplayername #{username}#{password ? " +password \"#{password}\"" : ""}"\
|
||||||
|
"#{multi ? " +multi" : ""}"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def play_now_server(app_id, channel)
|
def play_now_server(app_id, channel)
|
||||||
@@ -251,9 +285,14 @@ class W3DHub
|
|||||||
|
|
||||||
server_options = Store.server_list.select do |server|
|
server_options = Store.server_list.select do |server|
|
||||||
server.game == app_id &&
|
server.game == app_id &&
|
||||||
server.channel == channel &&
|
server.channel == channel &&
|
||||||
!server.status.password &&
|
!server.status.password &&
|
||||||
server.status.player_count < server.status.max_players
|
server.status.player_count < server.status.max_players
|
||||||
|
end
|
||||||
|
# sort by player count HIGH to LOW
|
||||||
|
# and by ping LOW to HIGH
|
||||||
|
server_options.sort! do |a, b|
|
||||||
|
[b.status.player_count, a.ping] <=> [a.status.player_count, b.ping]
|
||||||
end
|
end
|
||||||
|
|
||||||
# try to find server with lowest ping and matching version
|
# try to find server with lowest ping and matching version
|
||||||
@@ -261,7 +300,7 @@ class W3DHub
|
|||||||
# try to find server with lowest ping and undefined version
|
# try to find server with lowest ping and undefined version
|
||||||
found_server ||= server_options.find { |server| server.version == Api::ServerListServer::NO_OR_DEFAULT_VERSION }
|
found_server ||= server_options.find { |server| server.version == Api::ServerListServer::NO_OR_DEFAULT_VERSION }
|
||||||
|
|
||||||
found_server ? found_server : nil
|
found_server
|
||||||
end
|
end
|
||||||
|
|
||||||
def play_now(app_id, channel)
|
def play_now(app_id, channel)
|
||||||
|
|||||||
@@ -746,6 +746,8 @@ class W3DHub
|
|||||||
end
|
end
|
||||||
patch_entry = patch_mix.entries.find { |e| e.name.casecmp?(".w3dhub.patch") || e.name.casecmp?(".bhppatch") }
|
patch_entry = patch_mix.entries.find { |e| e.name.casecmp?(".w3dhub.patch") || e.name.casecmp?(".bhppatch") }
|
||||||
patch_entry.read
|
patch_entry.read
|
||||||
|
# "remove" patch meta file from patch before copying patch data
|
||||||
|
patch_mix.entries.delete(patch_entry)
|
||||||
|
|
||||||
patch_info = JSON.parse(patch_entry.blob, symbolize_names: true)
|
patch_info = JSON.parse(patch_entry.blob, symbolize_names: true)
|
||||||
|
|
||||||
@@ -765,20 +767,15 @@ class W3DHub
|
|||||||
patch_info[:updatedFiles].each do |file|
|
patch_info[:updatedFiles].each do |file|
|
||||||
logger.debug(LOG_TAG) { " #{file}" }
|
logger.debug(LOG_TAG) { " #{file}" }
|
||||||
|
|
||||||
patch = patch_mix.entries.find { |e| e.name.casecmp?(file) }
|
patch_mix.entries.each do |entry|
|
||||||
target = target_mix.entries.find { |e| e.name.casecmp?(file) }
|
target_mix.add_entry(entry: entry, replace: true)
|
||||||
|
|
||||||
if target
|
|
||||||
target_mix.entries[target_mix.entries.index(target)] = patch
|
|
||||||
else
|
|
||||||
target_mix.entries << patch
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
logger.info(LOG_TAG) { " Writing updated #{file_path}..." } if patch_info[:updatedFiles].size.positive?
|
logger.info(LOG_TAG) { " Writing updated #{file_path}..." } if patch_info[:updatedFiles].size.positive?
|
||||||
temp_mix_path = "#{temp_path}/#{File.basename(file_path)}"
|
temp_mix_path = "#{temp_path}/#{File.basename(file_path)}"
|
||||||
temp_mix = W3DHub::WWMix.new(path: temp_mix_path)
|
temp_mix = W3DHub::WWMix.new(path: temp_mix_path, encrypted: target_mix.encrypted?)
|
||||||
target_mix.entries.each { |e| temp_mix.add_entry(entry: e) }
|
target_mix.entries.each { |e| temp_mix.add_entry(entry: e, replace: true) }
|
||||||
unless temp_mix.save
|
unless temp_mix.save
|
||||||
raise temp_mix.error_reason
|
raise temp_mix.error_reason
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -54,51 +54,49 @@ class W3DHub
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.prompt_for_nickname(accept_callback: nil, cancel_callback: nil)
|
def self.prompt_for_nickname(accept_callback: nil, cancel_callback: nil)
|
||||||
CyberarmEngine::Window.instance.push_state(
|
CyberarmEngine::Window.instance.push_state(
|
||||||
W3DHub::States::PromptDialog,
|
W3DHub::States::PromptDialog,
|
||||||
title: I18n.t(:"server_browser.set_nickname"),
|
title: I18n.t(:"server_browser.set_nickname"),
|
||||||
message: I18n.t(:"server_browser.set_nickname_message"),
|
message: I18n.t(:"server_browser.set_nickname_message"),
|
||||||
prefill: Store.settings[:server_list_username],
|
prefill: Store.settings[:server_list_username],
|
||||||
accept_callback: accept_callback,
|
accept_callback: accept_callback,
|
||||||
cancel_callback: cancel_callback,
|
cancel_callback: cancel_callback,
|
||||||
# See: https://gitlab.com/danpaul88/brenbot/-/blob/master/Source/renlog.pm#L136-175
|
valid_callback: proc do |entry|
|
||||||
valid_callback: proc do |entry|
|
entry.length.between?(3, 40) && (entry =~ /^[a-z0-9_\-\[\]]+$/i)
|
||||||
entry.length > 1 && entry.length < 30 && (entry =~ /(:|!|&|%| )/i).nil? &&
|
|
||||||
(entry =~ /[\001\002\037]/).nil? && (entry =~ /\\/).nil?
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def self.prompt_for_password(accept_callback: nil, cancel_callback: nil)
|
def self.prompt_for_password(accept_callback: nil, cancel_callback: nil)
|
||||||
CyberarmEngine::Window.instance.push_state(
|
CyberarmEngine::Window.instance.push_state(
|
||||||
W3DHub::States::PromptDialog,
|
W3DHub::States::PromptDialog,
|
||||||
title: I18n.t(:"server_browser.enter_password"),
|
title: I18n.t(:"server_browser.enter_password"),
|
||||||
message: I18n.t(:"server_browser.enter_password_message"),
|
message: I18n.t(:"server_browser.enter_password_message"),
|
||||||
input_type: :password,
|
input_type: :password,
|
||||||
accept_callback: accept_callback,
|
accept_callback: accept_callback,
|
||||||
cancel_callback: cancel_callback,
|
cancel_callback: cancel_callback,
|
||||||
valid_callback: proc { |entry| entry.length.positive? }
|
valid_callback: proc { |entry| entry.length.positive? }
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.join_server(server:, username: Store.settings[:server_list_username], password: nil, multi: false)
|
def self.join_server(server:, username: Store.settings[:server_list_username], password: nil, multi: false)
|
||||||
if (
|
if (
|
||||||
(server.status.password && password.length.positive?) ||
|
(server.status.password && password.length.positive?) ||
|
||||||
!server.status.password) &&
|
!server.status.password) &&
|
||||||
username.to_s.length.positive?
|
username.to_s.length.positive?
|
||||||
|
|
||||||
Store.application_manager.join_server(
|
Store.application_manager.join_server(
|
||||||
server.game,
|
server.game,
|
||||||
server.channel,
|
server.channel,
|
||||||
server,
|
server,
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
multi
|
multi
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
CyberarmEngine::Window.instance.push_state(W3DHub::States::MessageDialog, type: "?", title: "?", message: "?")
|
CyberarmEngine::Window.instance.push_state(W3DHub::States::MessageDialog, type: "?", title: "?", message: "?")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.command(command, &block)
|
def self.command(command, &block)
|
||||||
if windows?
|
if windows?
|
||||||
@@ -123,7 +121,6 @@ class W3DHub
|
|||||||
process_info = Process.create(**hash)
|
process_info = Process.create(**hash)
|
||||||
|
|
||||||
pid = process_info.process_id
|
pid = process_info.process_id
|
||||||
status = -1
|
|
||||||
|
|
||||||
until (status = Process.get_exitcode(pid))
|
until (status = Process.get_exitcode(pid))
|
||||||
if block
|
if block
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
class W3DHub
|
class W3DHub
|
||||||
DIR_NAME = "W3DHubAlt".freeze
|
DIR_NAME = "W3DHubAlt".freeze
|
||||||
VERSION = "0.9.0".freeze
|
VERSION = "0.9.2".freeze
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -228,27 +228,34 @@ class W3DHub
|
|||||||
@encrypted
|
@encrypted
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_file(path:)
|
def add_file(path:, replace: false)
|
||||||
return false unless File.exist?(path)
|
return false unless File.exist?(path)
|
||||||
return false if File.directory?(path)
|
return false if File.directory?(path)
|
||||||
|
|
||||||
info = EntryInfoHeader.new(0, 0, File.size(path))
|
entry = Entry.new(name: File.basename(path), path: path, info: EntryInfoHeader.new(0, 0, File.size(path)))
|
||||||
@entries << Entry.new(name: File.basename(path), path: path, info: info)
|
add_entry(entry: entry, replace: replace)
|
||||||
|
|
||||||
true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_blob(path:, blob:)
|
def add_blob(path:, blob:, replace: false)
|
||||||
info = EntryInfoHeader.new(0, 0, blob.size)
|
info = EntryInfoHeader.new(0, 0, blob.size)
|
||||||
@entries << Entry.new(name: File.basename(path), path: path, info: info, blob: blob)
|
entry = Entry.new(name: File.basename(path), path: path, info: info, blob: blob)
|
||||||
into.crc32 = @entries.last.calculate_crc32
|
into.crc32 = @entries.last.calculate_crc32
|
||||||
|
|
||||||
true
|
add_entry(entry: entry, replace: replace)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_entry(entry:)
|
def add_entry(entry:, replace: false)
|
||||||
@entries << entry
|
duplicate = @entries.find { |e| e.name.upcase == entry.name.upcase }
|
||||||
|
|
||||||
|
if duplicate
|
||||||
|
if replace
|
||||||
|
@entries.delete(duplicate)
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@entries << entry
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user