mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2026-03-22 12:16:15 +00:00
Compare commits
5 Commits
v0.8.1
...
f608f45f02
| Author | SHA1 | Date | |
|---|---|---|---|
| f608f45f02 | |||
| 603328a51f | |||
| 48297ad9cd | |||
| 39fbb9df38 | |||
| bc9a524a55 |
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:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ master, test ]
|
branches: [master]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
13
Gemfile
13
Gemfile
@@ -6,7 +6,6 @@ gem "cyberarm_engine"
|
|||||||
gem "sdl2-bindings"
|
gem "sdl2-bindings"
|
||||||
gem "libui", platforms: [:windows]
|
gem "libui", platforms: [:windows]
|
||||||
gem "digest-crc"
|
gem "digest-crc"
|
||||||
gem "i18n"
|
|
||||||
gem "ircparser"
|
gem "ircparser"
|
||||||
gem "rexml"
|
gem "rexml"
|
||||||
gem "rubyzip"
|
gem "rubyzip"
|
||||||
@@ -19,9 +18,9 @@ gem "win32-security", platforms: [:windows]
|
|||||||
# use `bundle _x.y.z_ COMMAND` to use this one...
|
# 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: 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
|
# 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
|
group :windows_packaging do
|
||||||
# gem "bundler", "~>2.4.3"
|
gem "bundler", "~>2.4.3"
|
||||||
# gem "rake"
|
gem "rake"
|
||||||
# gem "ocran"
|
gem "ocran"
|
||||||
# gem "releasy"#, path: "../releasy"
|
gem "releasy"#, path: "../releasy"
|
||||||
# end
|
end
|
||||||
|
|||||||
28
Gemfile.lock
28
Gemfile.lock
@@ -3,17 +3,17 @@ GEM
|
|||||||
specs:
|
specs:
|
||||||
base64 (0.3.0)
|
base64 (0.3.0)
|
||||||
concurrent-ruby (1.3.5)
|
concurrent-ruby (1.3.5)
|
||||||
|
cri (2.15.12)
|
||||||
cyberarm_engine (0.24.5)
|
cyberarm_engine (0.24.5)
|
||||||
gosu (~> 1.1)
|
gosu (~> 1.1)
|
||||||
digest-crc (0.7.0)
|
digest-crc (0.7.0)
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
rake (>= 12.0.0, < 14.0.0)
|
||||||
event_emitter (0.2.6)
|
event_emitter (0.2.6)
|
||||||
excon (1.3.0)
|
excon (1.3.2)
|
||||||
logger
|
logger
|
||||||
ffi (1.17.2-x64-mingw-ucrt)
|
ffi (1.17.0)
|
||||||
ffi (1.17.2-x86_64-linux-gnu)
|
ffi-win32-extensions (1.1.0)
|
||||||
ffi-win32-extensions (1.0.4)
|
ffi (>= 1.15.5, <= 1.17.0)
|
||||||
ffi
|
|
||||||
fiddle (1.1.8)
|
fiddle (1.1.8)
|
||||||
gosu (1.4.6)
|
gosu (1.4.6)
|
||||||
i18n (1.14.7)
|
i18n (1.14.7)
|
||||||
@@ -23,9 +23,16 @@ GEM
|
|||||||
fiddle
|
fiddle
|
||||||
logger (1.7.0)
|
logger (1.7.0)
|
||||||
mutex_m (0.3.0)
|
mutex_m (0.3.0)
|
||||||
rake (13.3.0)
|
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)
|
rexml (3.4.4)
|
||||||
rubyzip (3.1.1)
|
rubyzip (3.2.2)
|
||||||
sdl2-bindings (0.2.3)
|
sdl2-bindings (0.2.3)
|
||||||
ffi (~> 1.15)
|
ffi (~> 1.15)
|
||||||
websocket (1.2.11)
|
websocket (1.2.11)
|
||||||
@@ -42,16 +49,19 @@ GEM
|
|||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
x64-mingw-ucrt
|
x64-mingw-ucrt
|
||||||
x86_64-linux
|
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
base64
|
base64
|
||||||
|
bundler (~> 2.4.3)
|
||||||
cyberarm_engine
|
cyberarm_engine
|
||||||
digest-crc
|
digest-crc
|
||||||
excon
|
excon
|
||||||
i18n
|
i18n
|
||||||
ircparser
|
ircparser
|
||||||
libui
|
libui
|
||||||
|
ocran
|
||||||
|
rake
|
||||||
|
releasy
|
||||||
rexml
|
rexml
|
||||||
rubyzip
|
rubyzip
|
||||||
sdl2-bindings
|
sdl2-bindings
|
||||||
@@ -60,4 +70,4 @@ DEPENDENCIES
|
|||||||
win32-security
|
win32-security
|
||||||
|
|
||||||
BUNDLED WITH
|
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.
|
Only requires OpenGL, Ruby, and a few gems.
|
||||||
|
|
||||||
## Installing
|
## 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 Gosu's [dependencies](https://github.com/gosu/gosu/wiki/Getting-Started-on-Linux).
|
||||||
* Install required gems: `bundle install`
|
* Install required gems: `bundle install`
|
||||||
|
|
||||||
|
|||||||
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
|
||||||
@@ -51,10 +51,16 @@ class W3DHub
|
|||||||
end
|
end
|
||||||
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
|
button "Save", width: 1.0 do
|
||||||
save_settings!
|
save_settings!
|
||||||
end
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ class W3DHub
|
|||||||
class Settings
|
class Settings
|
||||||
def self.defaults
|
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,
|
app_install_dir: default_app_install_dir,
|
||||||
package_cache_dir: default_package_cache_dir,
|
package_cache_dir: default_package_cache_dir,
|
||||||
parallel_downloads: 4,
|
parallel_downloads: 4,
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class W3DHub
|
|||||||
|
|
||||||
@task_index = 0
|
@task_index = 0
|
||||||
|
|
||||||
stack(width: 1.0, height: 1.0, border_thickness: 1, border_color: W3DHub::BORDER_COLOR, background_image: "#{GAME_ROOT_PATH}/media/banners/background.png", background_image_color: 0xff_525252, background_image_mode: :fill) do
|
stack(width: 1.0, height: 1.0, border_thickness: 1, border_color: W3DHub::BORDER_COLOR,
|
||||||
|
background_image: "#{GAME_ROOT_PATH}/media/banners/background.png", background_image_color: 0xff_525252, background_image_mode: :fill) do
|
||||||
stack(width: 1.0, fill: true) do
|
stack(width: 1.0, fill: true) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -41,7 +42,8 @@ class W3DHub
|
|||||||
end
|
end
|
||||||
|
|
||||||
def draw
|
def draw
|
||||||
Gosu.draw_circle(window.width / 2, window.height / 2, @w3dhub_logo.width * (0.6 + Math.cos(Gosu.milliseconds / 1000.0 * Math::PI).abs * 0.05), 128, 0xaa_353535, 32)
|
Gosu.draw_circle(window.width / 2, window.height / 2,
|
||||||
|
@w3dhub_logo.width * (0.6 + Math.cos(Gosu.milliseconds / 1000.0 * Math::PI).abs * 0.05), 128, 0xaa_353535, 32)
|
||||||
@w3dhub_logo.draw_rot(window.width / 2, window.height / 2, 32)
|
@w3dhub_logo.draw_rot(window.width / 2, window.height / 2, 32)
|
||||||
|
|
||||||
super
|
super
|
||||||
@@ -147,19 +149,18 @@ class W3DHub
|
|||||||
}
|
}
|
||||||
|
|
||||||
@status_label.value = "Checking uplink..."
|
@status_label.value = "Checking uplink..."
|
||||||
domains.each do |key, value|
|
domains.each do |key, _value|
|
||||||
begin
|
|
||||||
Resolv.getaddress(key.to_s)
|
Resolv.getaddress(key.to_s)
|
||||||
rescue => e
|
rescue StandardError => e
|
||||||
logger.error(LOG_TAG) {"Failed to resolve hostname: #{key.to_s}"}
|
logger.error(LOG_TAG) { "Failed to resolve hostname: #{key}" }
|
||||||
logger.error(LOG_TAG) { e }
|
logger.error(LOG_TAG) { e }
|
||||||
|
|
||||||
push_state(
|
push_state(
|
||||||
ConfirmDialog,
|
ConfirmDialog,
|
||||||
title: "DNS Resolution Failure",
|
title: "DNS Resolution Failure",
|
||||||
message: "Failed to resolve: #{key.to_s}\n\nTry disabling VPN or proxy if in use.\n\n\nContinue offline?",
|
message: "Failed to resolve: #{key}\n\nTry disabling VPN or proxy if in use.\n\n\nContinue offline?",
|
||||||
cancel_callback: ->() { window.close },
|
cancel_callback: -> { window.close },
|
||||||
accept_callback: ->() {
|
accept_callback: lambda {
|
||||||
@offline_mode = true
|
@offline_mode = true
|
||||||
Store.offline_mode = true
|
Store.offline_mode = true
|
||||||
@tasks[:connectivity_check][:complete] = true
|
@tasks[:connectivity_check][:complete] = true
|
||||||
@@ -169,7 +170,6 @@ class W3DHub
|
|||||||
# Prevent task from being marked as completed
|
# Prevent task from being marked as completed
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
@tasks[:connectivity_check][:complete] = true
|
@tasks[:connectivity_check][:complete] = true
|
||||||
end
|
end
|
||||||
@@ -187,7 +187,9 @@ class W3DHub
|
|||||||
|
|
||||||
@tasks[:service_status][:complete] = true
|
@tasks[:service_status][:complete] = true
|
||||||
else
|
else
|
||||||
BackgroundWorker.foreground_job(-> {}, ->(_) { @status_label.value = I18n.t(:"boot.w3dhub_service_is_down") })
|
BackgroundWorker.foreground_job(-> {}, lambda { |_|
|
||||||
|
@status_label.value = I18n.t(:"boot.w3dhub_service_is_down")
|
||||||
|
})
|
||||||
@tasks[:service_status][:complete] = true
|
@tasks[:service_status][:complete] = true
|
||||||
|
|
||||||
@offline_mode = true
|
@offline_mode = true
|
||||||
@@ -246,6 +248,7 @@ class W3DHub
|
|||||||
@status_label.value = "Retrieving application icons, this might take a moment..." # I18n.t(:"boot.checking_for_updates")
|
@status_label.value = "Retrieving application icons, this might take a moment..." # I18n.t(:"boot.checking_for_updates")
|
||||||
|
|
||||||
packages = []
|
packages = []
|
||||||
|
failure = false
|
||||||
Store.applications.games.each do |app|
|
Store.applications.games.each do |app|
|
||||||
packages << { category: app.category, subcategory: app.id, name: "#{app.id}.ico", version: "" }
|
packages << { category: app.category, subcategory: app.id, name: "#{app.id}.ico", version: "" }
|
||||||
end
|
end
|
||||||
@@ -261,21 +264,32 @@ class W3DHub
|
|||||||
|
|
||||||
regenerate = false
|
regenerate = false
|
||||||
|
|
||||||
broken_or_out_dated_icon = Digest::SHA256.new.hexdigest(File.binread(path)).upcase != package.checksum.upcase if File.exist?(path)
|
if File.exist?(path)
|
||||||
|
broken_or_out_dated_icon = Digest::SHA256.new.hexdigest(File.binread(path)).upcase != package.checksum.upcase
|
||||||
|
end
|
||||||
|
|
||||||
if File.exist?(path) && !broken_or_out_dated_icon
|
if File.exist?(path) && !broken_or_out_dated_icon
|
||||||
regenerate = !File.exist?(generated_icon_path)
|
regenerate = !File.exist?(generated_icon_path)
|
||||||
else
|
else
|
||||||
|
begin
|
||||||
Cache.fetch_package(package, proc {})
|
Cache.fetch_package(package, proc {})
|
||||||
regenerate = true
|
regenerate = true
|
||||||
end
|
rescue Errno::EACCES => e
|
||||||
|
failure = true
|
||||||
if regenerate
|
push_state(MessageDialog, title: "Fatal Error",
|
||||||
BackgroundWorker.foreground_job(-> { ICO.new(file: path) }, ->(result) { result.save(result.images.max_by(&:width), generated_icon_path) })
|
message: "Directory Permission Error (#{e.class}):\n#{e}.\n\nIs the required drive mounted?",
|
||||||
|
accept_callback: -> { window.close })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@tasks[:app_icons][:complete] = true
|
next unless regenerate
|
||||||
|
|
||||||
|
BackgroundWorker.foreground_job(-> { ICO.new(file: path) }, lambda { |result|
|
||||||
|
result.save(result.images.max_by(&:width), generated_icon_path)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
@tasks[:app_icons][:complete] = true unless failure
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -296,7 +310,8 @@ class W3DHub
|
|||||||
package_details&.each do |package|
|
package_details&.each do |package|
|
||||||
next if package.error?
|
next if package.error?
|
||||||
|
|
||||||
package_cache_path = Cache.package_path(package.category, package.subcategory, package.name, package.version)
|
package_cache_path = Cache.package_path(package.category, package.subcategory, package.name,
|
||||||
|
package.version)
|
||||||
|
|
||||||
missing_or_broken_image = File.exist?(package_cache_path) ? Digest::SHA256.new.hexdigest(File.binread(package_cache_path)).upcase != package.checksum.upcase : true
|
missing_or_broken_image = File.exist?(package_cache_path) ? Digest::SHA256.new.hexdigest(File.binread(package_cache_path)).upcase != package.checksum.upcase : true
|
||||||
|
|
||||||
@@ -356,7 +371,7 @@ class W3DHub
|
|||||||
"web-links": [],
|
"web-links": [],
|
||||||
"extended-data": [
|
"extended-data": [
|
||||||
{ name: "colour", value: game[:colour] },
|
{ name: "colour", value: game[:colour] },
|
||||||
{ name: "usesEngineCfg", value: game[:uses_engine_cfg] },
|
{ name: "usesEngineCfg", value: game[:uses_engine_cfg] }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,11 +84,11 @@ class W3DHub
|
|||||||
BLACK_IMAGE = Gosu::Image.from_blob(1, 1, "\x00\x00\x00\xff")
|
BLACK_IMAGE = Gosu::Image.from_blob(1, 1, "\x00\x00\x00\xff")
|
||||||
end
|
end
|
||||||
|
|
||||||
require "i18n"
|
|
||||||
require "websocket-client-simple"
|
require "websocket-client-simple"
|
||||||
require "English"
|
require "English"
|
||||||
require "sdl2"
|
require "sdl2"
|
||||||
|
|
||||||
|
require_relative "lib/i18n"
|
||||||
I18n.load_path << Dir["#{W3DHub::GAME_ROOT_PATH}/locales/*.yml"]
|
I18n.load_path << Dir["#{W3DHub::GAME_ROOT_PATH}/locales/*.yml"]
|
||||||
I18n.default_locale = :en
|
I18n.default_locale = :en
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user