mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2025-12-16 09:12:35 +00:00
Compare commits
19 Commits
4997cfabb0
...
developmen
| Author | SHA1 | Date | |
|---|---|---|---|
| 603328a51f | |||
| 48297ad9cd | |||
| 39fbb9df38 | |||
| bc9a524a55 | |||
| d92a8753d8 | |||
| b299593076 | |||
| ce10cdc658 | |||
| 5a3f350015 | |||
| d53299e904 | |||
| d12d3ff6b8 | |||
| d67ffa14a3 | |||
| 71047ce9e8 | |||
| 7da716dde4 | |||
| 3a72a2e094 | |||
| 3c565e6fee | |||
| 2dc750a686 | |||
| ed119a4925 | |||
| e4d99aac00 | |||
| e9b8638c27 |
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: cyberarm
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
2
.github/workflows/build-tebako.yml
vendored
2
.github/workflows/build-tebako.yml
vendored
@@ -2,7 +2,7 @@ name: Build Launcher Binary
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master, test ]
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
13
Gemfile
13
Gemfile
@@ -6,7 +6,6 @@ gem "cyberarm_engine"
|
||||
gem "sdl2-bindings"
|
||||
gem "libui", platforms: [:windows]
|
||||
gem "digest-crc"
|
||||
gem "i18n"
|
||||
gem "ircparser"
|
||||
gem "rexml"
|
||||
gem "rubyzip"
|
||||
@@ -19,9 +18,9 @@ gem "win32-security", platforms: [:windows]
|
||||
# use `bundle _x.y.z_ COMMAND` to use this one...
|
||||
# NOTE: Releasy needs to be installed as a system gem i.e. `rake install`
|
||||
# NOTE: contents of the `gemhome` folder in the packaged folder need to be moved into the lib/ruby/gems\<RUBY_VERSION> folder
|
||||
# group :windows_packaging do
|
||||
# gem "bundler", "~>2.4.3"
|
||||
# gem "rake"
|
||||
# gem "ocran"
|
||||
# gem "releasy"#, path: "../releasy"
|
||||
# end
|
||||
group :windows_packaging do
|
||||
gem "bundler", "~>2.4.3"
|
||||
gem "rake"
|
||||
gem "ocran"
|
||||
gem "releasy"#, path: "../releasy"
|
||||
end
|
||||
|
||||
34
Gemfile.lock
34
Gemfile.lock
@@ -3,27 +3,36 @@ GEM
|
||||
specs:
|
||||
base64 (0.3.0)
|
||||
concurrent-ruby (1.3.5)
|
||||
cri (2.15.12)
|
||||
cyberarm_engine (0.24.5)
|
||||
gosu (~> 1.1)
|
||||
digest-crc (0.7.0)
|
||||
rake (>= 12.0.0, < 14.0.0)
|
||||
event_emitter (0.2.6)
|
||||
excon (1.3.0)
|
||||
excon (1.3.2)
|
||||
logger
|
||||
ffi (1.17.2-x64-mingw-ucrt)
|
||||
ffi (1.17.2-x86_64-linux-gnu)
|
||||
ffi-win32-extensions (1.0.4)
|
||||
ffi
|
||||
ffi (1.17.0)
|
||||
ffi-win32-extensions (1.1.0)
|
||||
ffi (>= 1.15.5, <= 1.17.0)
|
||||
fiddle (1.1.8)
|
||||
gosu (1.4.6)
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
ircparser (1.0.0)
|
||||
libui (0.1.2-x64-mingw)
|
||||
libui (0.2.0-x64-mingw-ucrt)
|
||||
fiddle
|
||||
logger (1.7.0)
|
||||
mutex_m (0.3.0)
|
||||
rake (13.3.0)
|
||||
rexml (3.4.2)
|
||||
rubyzip (3.0.2)
|
||||
ocran (1.3.17)
|
||||
fiddle (~> 1.0)
|
||||
rake (13.3.1)
|
||||
releasy (0.2.4)
|
||||
bundler (>= 1.2.1)
|
||||
cri (~> 2.15.0)
|
||||
ocran (~> 1.3.0)
|
||||
rake (>= 0.9.2.2)
|
||||
rexml (3.4.4)
|
||||
rubyzip (3.2.2)
|
||||
sdl2-bindings (0.2.3)
|
||||
ffi (~> 1.15)
|
||||
websocket (1.2.11)
|
||||
@@ -40,16 +49,19 @@ GEM
|
||||
|
||||
PLATFORMS
|
||||
x64-mingw-ucrt
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
base64
|
||||
bundler (~> 2.4.3)
|
||||
cyberarm_engine
|
||||
digest-crc
|
||||
excon
|
||||
i18n
|
||||
ircparser
|
||||
libui
|
||||
ocran
|
||||
rake
|
||||
releasy
|
||||
rexml
|
||||
rubyzip
|
||||
sdl2-bindings
|
||||
@@ -58,4 +70,4 @@ DEPENDENCIES
|
||||
win32-security
|
||||
|
||||
BUNDLED WITH
|
||||
2.6.8
|
||||
2.4.22
|
||||
|
||||
@@ -3,7 +3,7 @@ It runs natively on Linux! No mucking about trying to get .NET 4.6.1 or somethin
|
||||
Only requires OpenGL, Ruby, and a few gems.
|
||||
|
||||
## Installing
|
||||
* Install Ruby 3.0+, from your package manager.
|
||||
* Install Ruby 3.4+, from your package manager.
|
||||
* Install Gosu's [dependencies](https://github.com/gosu/gosu/wiki/Getting-Started-on-Linux).
|
||||
* Install required gems: `bundle install`
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class W3DHub
|
||||
retry_interval: 1,
|
||||
retry_errors: [Excon::Error::Socket, Excon::Error::HTTPStatus] # Don't retry on timeout
|
||||
)
|
||||
rescue Excon::Errors::Timeout => e
|
||||
rescue Excon::Error::Timeout => e
|
||||
logger.error(LOG_TAG) { "Connection to \"#{url}\" timed out after: #{API_TIMEOUT} seconds" }
|
||||
|
||||
DummyResponse.new(e)
|
||||
@@ -135,7 +135,7 @@ class W3DHub
|
||||
retry_interval: 1,
|
||||
retry_errors: [Excon::Error::Socket, Excon::Error::HTTPStatus] # Don't retry on timeout
|
||||
)
|
||||
rescue Excon::Errors::Timeout => e
|
||||
rescue Excon::Error::Timeout => e
|
||||
logger.error(LOG_TAG) { "Connection to \"#{url}\" timed out after: #{API_TIMEOUT} seconds" }
|
||||
|
||||
DummyResponse.new(e)
|
||||
|
||||
@@ -24,8 +24,9 @@ class W3DHub
|
||||
|
||||
def to_json(env)
|
||||
d = @data.dup
|
||||
|
||||
d[:avatar_uri] = @avatar_uri
|
||||
d[:access_token_expiry] = d[:access_token_expiry].to_i
|
||||
d[:access_token_expiry] = @access_token_expiry.to_i
|
||||
|
||||
d.to_json(env)
|
||||
end
|
||||
|
||||
@@ -235,11 +235,11 @@ class W3DHub
|
||||
end
|
||||
end
|
||||
|
||||
def join_server(app_id, channel, server, password = nil)
|
||||
if installed?(app_id, channel) && Store.settings[:server_list_username].to_s.length.positive?
|
||||
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?
|
||||
run(
|
||||
app_id, channel,
|
||||
"+connect #{server.address}:#{server.port} +netplayername #{Store.settings[:server_list_username]}#{password ? " +password \"#{password}\"" : ""}"
|
||||
"+connect #{server.address}:#{server.port} +netplayername #{username}#{password ? " +password \"#{password}\"" : ""}#{multi ? " +multi" : ""}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -112,9 +112,36 @@ class W3DHub
|
||||
@task_state == :failed
|
||||
end
|
||||
|
||||
# Helper method to normalize file paths for case-insensitive comparison
|
||||
def normalize_path(path)
|
||||
path.to_s.gsub("\\", "/").downcase
|
||||
def normalize_path(path, base_path)
|
||||
path = path.to_s.gsub("\\", "/")
|
||||
return "#{base_path}/#{path}" if W3DHub.windows? # Windows is easy, or annoying, depending how you look at it...
|
||||
|
||||
constructed_path = base_path
|
||||
lowercase_full_path = "#{base_path}/#{path}".downcase.strip.freeze
|
||||
|
||||
accepted_parts = 0
|
||||
split_path = path.split("/")
|
||||
split_path.each do |segment|
|
||||
Dir.glob("#{constructed_path}/*").each do |part|
|
||||
next unless "#{constructed_path}/#{segment}".downcase == part.downcase
|
||||
|
||||
# Handle edge case where a file with the same name is in a higher directory
|
||||
next if File.file?(part) && part.downcase.strip != lowercase_full_path
|
||||
|
||||
constructed_path = part
|
||||
accepted_parts += 1
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
# Find file if it exists else use provided path as cased
|
||||
if constructed_path.downcase.strip == lowercase_full_path
|
||||
constructed_path
|
||||
elsif accepted_parts.positive?
|
||||
"#{constructed_path}/#{split_path[accepted_parts..].join('/')}"
|
||||
else
|
||||
"#{base_path}/#{path}" # File doesn't exist, case doesn't matter.
|
||||
end
|
||||
end
|
||||
|
||||
def failure_reason
|
||||
@@ -300,15 +327,13 @@ class W3DHub
|
||||
@files.reverse.each do |file|
|
||||
break unless folder_exists
|
||||
|
||||
# Normalize file paths to handle case-insensitive comparisons
|
||||
safe_file_name = normalize_path(file.name)
|
||||
file_path = "#{path}/#{safe_file_name}"
|
||||
file_path = normalize_path(file.name, path)
|
||||
|
||||
processed_files += 1
|
||||
@status.progress = processed_files.to_f / file_count
|
||||
|
||||
next if file.removed_since
|
||||
next if accepted_files.key?(safe_file_name)
|
||||
next if accepted_files.key?(file_path)
|
||||
|
||||
unless File.exist?(file_path)
|
||||
rejected_files << { file: file, manifest_version: file.version }
|
||||
@@ -328,7 +353,7 @@ class W3DHub
|
||||
logger.info(LOG_TAG) { file.inspect } if file.checksum.nil?
|
||||
|
||||
if digest.hexdigest.upcase == file.checksum.upcase
|
||||
accepted_files[safe_file_name] = file.version
|
||||
accepted_files[file_path] = file.version
|
||||
logger.info(LOG_TAG) { "[#{file.version}] Verified file: #{file_path}" }
|
||||
else
|
||||
rejected_files << { file: file, manifest_version: file.version }
|
||||
@@ -532,7 +557,7 @@ class W3DHub
|
||||
logger.info(LOG_TAG) { " #{file.name}" }
|
||||
|
||||
path = Cache.install_path(@application, @channel)
|
||||
file_path = "#{path}/#{file.name}".sub('Data/', 'data/')
|
||||
file_path = normalize_path(file.name, path)
|
||||
|
||||
File.delete(file_path) if File.exist?(file_path)
|
||||
|
||||
@@ -565,7 +590,7 @@ class W3DHub
|
||||
def write_paths_ini
|
||||
path = Cache.install_path(@application, @channel)
|
||||
|
||||
File.open("#{path}/data/paths.ini", "w") do |file|
|
||||
File.open(normalize_path("data/paths.ini", path), "w") do |file|
|
||||
file.puts("[paths]")
|
||||
file.puts("RegBase=W3D Hub")
|
||||
file.puts("RegClient=#{@application.category}\\#{@application.id}-#{@channel.id}")
|
||||
@@ -710,15 +735,15 @@ class W3DHub
|
||||
logger.info(LOG_TAG) { " Unpacking patch \"#{package_path}\" in \"#{temp_path}\"" }
|
||||
unzip(package_path, temp_path)
|
||||
|
||||
# Normalize the path to handle case-insensitivity consistently
|
||||
safe_file_name = normalize_path(manifest_file.name)
|
||||
file_path = normalize_path(manifest_file.name, path)
|
||||
temp_file_path = normalize_path(manifest_file.name, temp_path)
|
||||
|
||||
logger.info(LOG_TAG) { " Loading #{temp_path}/#{safe_file_name}.patch..." }
|
||||
patch_mix = W3DHub::Mixer::Reader.new(file_path: "#{temp_path}/#{safe_file_name}.patch", ignore_crc_mismatches: false)
|
||||
logger.info(LOG_TAG) { " Loading #{temp_file_path}.patch..." }
|
||||
patch_mix = W3DHub::Mixer::Reader.new(file_path: "#{temp_file_path}.patch", ignore_crc_mismatches: false)
|
||||
patch_info = JSON.parse(patch_mix.package.files.find { |f| f.name.casecmp?(".w3dhub.patch") || f.name.casecmp?(".bhppatch") }.data, symbolize_names: true)
|
||||
|
||||
logger.info(LOG_TAG) { " Loading #{path}/#{safe_file_name}..." }
|
||||
target_mix = W3DHub::Mixer::Reader.new(file_path: "#{path}/#{safe_file_name}", ignore_crc_mismatches: false)
|
||||
logger.info(LOG_TAG) { " Loading #{file_path}..." }
|
||||
target_mix = W3DHub::Mixer::Reader.new(file_path: "#{file_path}", ignore_crc_mismatches: false)
|
||||
|
||||
logger.info(LOG_TAG) { " Removing files..." } if patch_info[:removedFiles].size.positive?
|
||||
patch_info[:removedFiles].each do |file|
|
||||
@@ -740,8 +765,8 @@ class W3DHub
|
||||
end
|
||||
end
|
||||
|
||||
logger.info(LOG_TAG) { " Writing updated #{path}/#{safe_file_name}..." } if patch_info[:updatedFiles].size.positive?
|
||||
W3DHub::Mixer::Writer.new(file_path: "#{path}/#{safe_file_name}", package: target_mix.package, memory_buffer: true, encrypted: target_mix.encrypted?)
|
||||
logger.info(LOG_TAG) { " Writing updated #{file_path}..." } if patch_info[:updatedFiles].size.positive?
|
||||
W3DHub::Mixer::Writer.new(file_path: "#{file_path}", package: target_mix.package, memory_buffer: true, encrypted: target_mix.encrypted?)
|
||||
|
||||
FileUtils.remove_dir(temp_path)
|
||||
|
||||
@@ -753,14 +778,14 @@ class W3DHub
|
||||
|
||||
while (entry = stream.get_next_entry)
|
||||
# Normalize the path to handle case-insensitivity consistently
|
||||
safe_file_name = normalize_path(entry.name)
|
||||
file_path = normalize_path(entry.name, path)
|
||||
|
||||
dir_path = "#{path}/#{File.dirname(safe_file_name)}"
|
||||
dir_path = File.dirname(file_path)
|
||||
unless dir_path.end_with?("/.") || Dir.exist?(dir_path)
|
||||
FileUtils.mkdir_p(dir_path)
|
||||
end
|
||||
|
||||
File.open("#{path}/#{safe_file_name}", "wb") do |f|
|
||||
File.open(file_path, "wb") do |f|
|
||||
i = entry.get_input_stream
|
||||
|
||||
while (chunk = i.read(32_000_000)) # Read up to ~32 MB per chunk
|
||||
|
||||
@@ -53,4 +53,4 @@ class W3DHub
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
22
lib/cache.rb
22
lib/cache.rb
@@ -90,6 +90,10 @@ class W3DHub
|
||||
# Download a W3D Hub package
|
||||
def self.fetch_package(package, block)
|
||||
endpoint_download_url = package.download_url || "#{Api::W3DHUB_API_ENDPOINT}/apis/launcher/1/get-package"
|
||||
if package.download_url
|
||||
uri_path = package.download_url.split("/").last
|
||||
endpoint_download_url = package.download_url.sub(uri_path, URI.encode_uri_component(uri_path))
|
||||
end
|
||||
path = package_path(package.category, package.subcategory, package.name, package.version)
|
||||
headers = { "Content-Type": "application/x-www-form-urlencoded", "User-Agent": Api::USER_AGENT }
|
||||
headers["Authorization"] = "Bearer #{Store.account.access_token}" if Store.account && !package.download_url
|
||||
@@ -129,10 +133,24 @@ class W3DHub
|
||||
return true
|
||||
else
|
||||
logger.debug(LOG_TAG) { " Failed to retrieve package: (#{package.category}:#{package.subcategory}:#{package.name}:#{package.version})" }
|
||||
logger.debug(LOG_TAG) { " Download URL: #{endpoint_download_url}, response: #{response.status}" }
|
||||
logger.debug(LOG_TAG) { " Download URL: #{endpoint_download_url}, response: #{response&.status || -1}" }
|
||||
|
||||
false
|
||||
return false
|
||||
end
|
||||
rescue Excon::Error::Timeout => e
|
||||
logger.error(LOG_TAG) { " Connection to \"#{endpoint_download_url}\" timed out after: #{W3DHub::Api::API_TIMEOUT} seconds" }
|
||||
logger.error(LOG_TAG) { e }
|
||||
logger.debug(LOG_TAG) { " Failed to retrieve package: (#{package.category}:#{package.subcategory}:#{package.name}:#{package.version})" }
|
||||
logger.debug(LOG_TAG) { " Download URL: #{endpoint_download_url}, response: #{response&.status || -1}" }
|
||||
|
||||
return false
|
||||
rescue Excon::Error => e
|
||||
logger.error(LOG_TAG) { " Connection to \"#{endpoint_download_url}\" errored:" }
|
||||
logger.error(LOG_TAG) { e }
|
||||
logger.debug(LOG_TAG) { " Failed to retrieve package: (#{package.category}:#{package.subcategory}:#{package.name}:#{package.version})" }
|
||||
logger.debug(LOG_TAG) { " Download URL: #{endpoint_download_url}, response: #{response&.status || -1}" }
|
||||
|
||||
return false
|
||||
ensure
|
||||
file&.close
|
||||
end
|
||||
|
||||
@@ -81,15 +81,19 @@ class W3DHub
|
||||
)
|
||||
end
|
||||
|
||||
def self.join_server(server, password)
|
||||
def self.join_server(server:, username: Store.settings[:server_list_username], password: nil, multi: false)
|
||||
if (
|
||||
(server.status.password && password.length.positive?) ||
|
||||
!server.status.password) &&
|
||||
Store.settings[:server_list_username].to_s.length.positive?
|
||||
username.to_s.length.positive?
|
||||
|
||||
Store.application_manager.join_server(
|
||||
server.game,
|
||||
server.channel, server, password
|
||||
server.channel,
|
||||
server,
|
||||
username,
|
||||
password,
|
||||
multi
|
||||
)
|
||||
else
|
||||
CyberarmEngine::Window.instance.push_state(W3DHub::States::MessageDialog, type: "?", title: "?", message: "?")
|
||||
|
||||
102
lib/i18n.rb
Normal file
102
lib/i18n.rb
Normal file
@@ -0,0 +1,102 @@
|
||||
# The I18n gem is a real pain to work with when packaging with Ocra(n)
|
||||
# and we're not using its 'advanced' features so emulate its API here.
|
||||
|
||||
require "yaml"
|
||||
|
||||
class I18n
|
||||
class InvalidLocale < StandardError
|
||||
end
|
||||
|
||||
@locale = :en
|
||||
@default_locale = :en
|
||||
@load_path = []
|
||||
|
||||
@translations = {}
|
||||
|
||||
def self.load_path
|
||||
@load_path
|
||||
end
|
||||
|
||||
def self.default_locale
|
||||
@default_locale.to_sym
|
||||
end
|
||||
|
||||
def self.default_locale=(locale)
|
||||
@default_locale = locale.to_s
|
||||
end
|
||||
|
||||
def self.locale
|
||||
@locale.to_sym
|
||||
end
|
||||
|
||||
def self.locale=(locale)
|
||||
locale = locale.to_s
|
||||
|
||||
raise InvalidLocale unless valid_locale?(locale)
|
||||
|
||||
@locale = locale
|
||||
end
|
||||
|
||||
def self.t(symbol)
|
||||
return symbol.to_s unless valid_locale?(@locale)
|
||||
|
||||
@translations[@locale] || load_locale(@locale)
|
||||
|
||||
translations = @translations[@locale]
|
||||
return translations[symbol] if translations
|
||||
|
||||
translation = @translations.dig(@default_locale, symbol)
|
||||
return translation if translation
|
||||
|
||||
return symbol.to_s
|
||||
end
|
||||
|
||||
def self.available_locales
|
||||
@load_path.flatten.map { |f| File.basename(f, ".yml").to_s.downcase.to_sym }
|
||||
end
|
||||
|
||||
private
|
||||
def self.load_locale(locale)
|
||||
locale = locale.to_s
|
||||
|
||||
return if @translations[locale] && !@translations[locale].empty?
|
||||
|
||||
if (file = valid_locale?(locale))
|
||||
yaml = YAML.load_file(file)
|
||||
|
||||
raise InvalidLocale unless yaml[locale]
|
||||
|
||||
key = ""
|
||||
hash = yaml[locale]
|
||||
hash.each_pair do |key, v|
|
||||
if v.is_a?(String)
|
||||
@translations[locale] ||= {}
|
||||
@translations[locale][key.to_sym] = v
|
||||
else
|
||||
load_locale_part(locale, key, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.load_locale_part(locale, key, part)
|
||||
locale = locale.to_s
|
||||
|
||||
part.each_pair do |k, v|
|
||||
if v.is_a?(String)
|
||||
@translations[locale] ||= {}
|
||||
@translations[locale]["#{key}.#{k}".to_sym] = v
|
||||
else
|
||||
load_locale_part(locale, "#{key}.#{k}", v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.valid_locale?(locale)
|
||||
locale = locale.to_s
|
||||
|
||||
@load_path.flatten.find do |file|
|
||||
File.basename(file, ".yml").to_s.downcase.strip == locale
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -375,7 +375,7 @@ class W3DHub
|
||||
end
|
||||
|
||||
container = stack(fill: true, width: 1.0, padding: 8) do
|
||||
image_path = File.exist?("#{GAME_ROOT_PATH}/media/icons/#{game.id}.png") ? "#{GAME_ROOT_PATH}/media/icons/#{game.id}.png" : "#{GAME_ROOT_PATH}/media/icons/default_icon.png"
|
||||
image_path = File.exist?("#{CACHE_PATH}/#{game.id}.png") ? "#{CACHE_PATH}/#{game.id}.png" : "#{GAME_ROOT_PATH}/media/icons/default_icon.png"
|
||||
flow(width: 1.0, margin_top: 8) do
|
||||
flow(fill: true)
|
||||
image image_path, width: 0.5
|
||||
|
||||
@@ -410,11 +410,11 @@ class W3DHub
|
||||
if server.status.password
|
||||
W3DHub.prompt_for_password(
|
||||
accept_callback: proc do |password|
|
||||
W3DHub.join_server(server, password)
|
||||
W3DHub.join_server(server: server, password: password)
|
||||
end
|
||||
)
|
||||
else
|
||||
W3DHub.join_server(server, nil)
|
||||
W3DHub.join_server(server: server)
|
||||
end
|
||||
end
|
||||
)
|
||||
@@ -422,18 +422,24 @@ class W3DHub
|
||||
if server.status.password
|
||||
W3DHub.prompt_for_password(
|
||||
accept_callback: proc do |password|
|
||||
W3DHub.join_server(server, password)
|
||||
W3DHub.join_server(server: server, password: password)
|
||||
end
|
||||
)
|
||||
else
|
||||
W3DHub.join_server(server, nil)
|
||||
W3DHub.join_server(server: server)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if W3DHUB_DEVELOPER
|
||||
list_box(items: (1..12).to_a.map(&:to_s), margin_left: 16, width: 72, tip: "Number of game clients", enabled: (game_installed && !game_updatable), **TESTING_BUTTON)
|
||||
button "Multijoin", tip: "Launch multiple clients with configured username_\#{number}", enabled: (game_installed && !game_updatable), **TESTING_BUTTON
|
||||
client_instances = list_box(items: (1..12).to_a.map(&:to_s), margin_left: 16, width: 72, tip: "Number of game clients", enabled: (game_installed && !game_updatable), **TESTING_BUTTON)
|
||||
button("Multijoin", tip: "Launch multiple clients with configured username_\#{number}", enabled: (game_installed && !game_updatable), **TESTING_BUTTON) do
|
||||
username = Store.settings[:server_list_username]
|
||||
|
||||
client_instances.value.to_i.times do |i|
|
||||
W3DHub.join_server(server: server, username: format("%s_%d", username, i), multi: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
flow(fill: true)
|
||||
|
||||
@@ -51,10 +51,16 @@ class W3DHub
|
||||
end
|
||||
end
|
||||
|
||||
stack(width: 128, max_height: 256, h_align: :center, margin_top: 16, fill: true) do
|
||||
flow(width: 256, height: 64, h_align: :center, margin_top: 16) do
|
||||
button "Save", width: 1.0 do
|
||||
save_settings!
|
||||
end
|
||||
|
||||
flow(fill: true)
|
||||
|
||||
end
|
||||
button("Clear package cache: #{W3DHub.format_size(Dir.glob("#{Store.settings[:package_cache_dir]}/**/**").map { |f| File.file?(f) ? File.size(f) : 0}.sum)}", **DANGEROUS_BUTTON) do
|
||||
# TODO.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ class W3DHub
|
||||
class Settings
|
||||
def self.defaults
|
||||
{
|
||||
language: Gosu.user_languages.first.split("_").first,
|
||||
language: Gosu.user_languages.first&.split("_")&.first || "en",
|
||||
app_install_dir: default_app_install_dir,
|
||||
package_cache_dir: default_package_cache_dir,
|
||||
parallel_downloads: 4,
|
||||
|
||||
@@ -11,7 +11,7 @@ class W3DHub
|
||||
@fraction = 0.0
|
||||
@w3dhub_logo = get_image("#{GAME_ROOT_PATH}/media/icons/app.png")
|
||||
@tasks = {
|
||||
# connectivity_check: { started: false, complete: false }, # HEAD connectivity-check.ubuntu.com or HEAD secure.w3dhub.com?
|
||||
connectivity_check: { started: false, complete: false }, # HEAD connectivity-check.ubuntu.com or HEAD secure.w3dhub.com?
|
||||
# launcher_updater: { started: false, complete: false },
|
||||
server_list: { started: false, complete: false },
|
||||
refresh_user_token: { started: false, complete: false },
|
||||
@@ -139,6 +139,41 @@ class W3DHub
|
||||
@tasks[:refresh_user_token][:complete] = true
|
||||
end
|
||||
|
||||
def connectivity_check
|
||||
domains = {
|
||||
"w3dhub-api.w3d.cyberarm.dev": false,
|
||||
"s3.w3d.cyberarm.dev": false,
|
||||
"secure.w3dhub.com": false
|
||||
}
|
||||
|
||||
@status_label.value = "Checking uplink..."
|
||||
domains.each do |key, value|
|
||||
begin
|
||||
Resolv.getaddress(key.to_s)
|
||||
rescue => e
|
||||
logger.error(LOG_TAG) {"Failed to resolve hostname: #{key.to_s}"}
|
||||
logger.error(LOG_TAG) {e}
|
||||
|
||||
push_state(
|
||||
ConfirmDialog,
|
||||
title: "DNS Resolution Failure",
|
||||
message: "Failed to resolve: #{key.to_s}\n\nTry disabling VPN or proxy if in use.\n\n\nContinue offline?",
|
||||
cancel_callback: ->() { window.close },
|
||||
accept_callback: ->() {
|
||||
@offline_mode = true
|
||||
Store.offline_mode = true
|
||||
@tasks[:connectivity_check][:complete] = true
|
||||
}
|
||||
)
|
||||
|
||||
# Prevent task from being marked as completed
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
@tasks[:connectivity_check][:complete] = true
|
||||
end
|
||||
|
||||
def service_status
|
||||
Api.on_thread(:service_status) do |service_status|
|
||||
@service_status = service_status
|
||||
|
||||
@@ -24,6 +24,7 @@ class W3DHub
|
||||
stack(width: 1.0, height: 46, padding: 8) do
|
||||
button "Okay", width: 1.0 do
|
||||
pop_state
|
||||
@options[:accept_callback]&.call
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class W3DHub
|
||||
DIR_NAME = "W3DHubAlt"
|
||||
VERSION = "0.7.0"
|
||||
end
|
||||
DIR_NAME = "W3DHubAlt".freeze
|
||||
VERSION = "0.8.1".freeze
|
||||
end
|
||||
|
||||
@@ -84,11 +84,11 @@ class W3DHub
|
||||
BLACK_IMAGE = Gosu::Image.from_blob(1, 1, "\x00\x00\x00\xff")
|
||||
end
|
||||
|
||||
require "i18n"
|
||||
require "websocket-client-simple"
|
||||
require "English"
|
||||
require "sdl2"
|
||||
|
||||
require_relative "lib/i18n"
|
||||
I18n.load_path << Dir["#{W3DHub::GAME_ROOT_PATH}/locales/*.yml"]
|
||||
I18n.default_locale = :en
|
||||
|
||||
|
||||
Reference in New Issue
Block a user