Removed Async[websocket/http] due to excessive require times and reliablity issues on Windows

This commit is contained in:
2022-10-30 18:10:47 -05:00
parent 7359d73027
commit 340c083a43
12 changed files with 176 additions and 210 deletions

View File

@@ -6,12 +6,9 @@ gem "i18n"
gem "rexml" gem "rexml"
gem "digest-crc" gem "digest-crc"
gem "ffi" gem "ffi"
gem "async", "~>1.30.1" gem "websocket-client-simple"
gem "async-http"
gem "async-websocket"
gem "thread-local" gem "thread-local"
gem "ircparser" gem "ircparser"
gem "net-ping"
gem "win32-security", platforms: [:x64_mingw, :mingw] gem "win32-security", platforms: [:x64_mingw, :mingw]
# group :windows_packaging do # group :windows_packaging do

View File

@@ -1,46 +1,22 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
addressable (2.8.0) addressable (2.8.1)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 6.0)
async (1.30.3)
console (~> 1.10)
nio4r (~> 2.3)
timers (~> 4.1)
async-http (0.56.6)
async (>= 1.25)
async-io (>= 1.28)
async-pool (>= 0.2)
protocol-http (~> 0.22.0)
protocol-http1 (~> 0.14.0)
protocol-http2 (~> 0.14.0)
traces (~> 0.4.0)
async-io (1.33.0)
async
async-pool (0.3.10)
async (>= 1.25)
async-websocket (0.19.0)
async-http (~> 0.54)
async-io (~> 1.23)
protocol-websocket (~> 0.7.0)
clipboard (1.3.6)
concurrent-ruby (1.1.10) concurrent-ruby (1.1.10)
console (1.15.3) cyberarm_engine (0.22.0)
fiber-local
cyberarm_engine (0.21.0)
clipboard (~> 1.3)
excon (~> 0.88) excon (~> 0.88)
gosu (~> 1.1) gosu (~> 1.1)
gosu_more_drawables (~> 0.3) gosu_more_drawables (~> 0.3)
digest-crc (0.6.4) digest-crc (0.6.4)
rake (>= 12.0.0, < 14.0.0) rake (>= 12.0.0, < 14.0.0)
excon (0.92.4) event_emitter (0.2.6)
excon (0.93.1)
ffi (1.15.5) ffi (1.15.5)
ffi (1.15.5-x64-mingw-ucrt) ffi (1.15.5-x64-mingw-ucrt)
ffi (1.15.5-x64-mingw32) ffi (1.15.5-x64-mingw32)
ffi-win32-extensions (1.0.4) ffi-win32-extensions (1.0.4)
ffi ffi
fiber-local (1.0.0)
gosu (1.4.3) gosu (1.4.3)
gosu_more_drawables (0.3.1) gosu_more_drawables (0.3.1)
i18n (1.12.0) i18n (1.12.0)
@@ -48,24 +24,14 @@ GEM
ircparser (1.0.0) ircparser (1.0.0)
launchy (2.5.0) launchy (2.5.0)
addressable (~> 2.7) addressable (~> 2.7)
net-ping (2.0.8) public_suffix (5.0.0)
nio4r (2.5.8)
protocol-hpack (1.4.2)
protocol-http (0.22.6)
protocol-http1 (0.14.4)
protocol-http (~> 0.22)
protocol-http2 (0.14.2)
protocol-hpack (~> 1.4)
protocol-http (~> 0.18)
protocol-websocket (0.7.5)
protocol-http (~> 0.2)
protocol-http1 (~> 0.2)
public_suffix (4.0.7)
rake (13.0.6) rake (13.0.6)
rexml (3.2.5) rexml (3.2.5)
thread-local (1.1.0) thread-local (1.1.0)
timers (4.3.3) websocket (1.2.9)
traces (0.4.1) websocket-client-simple (0.6.0)
event_emitter
websocket
win32-security (0.5.0) win32-security (0.5.0)
ffi ffi
ffi-win32-extensions ffi-win32-extensions
@@ -76,18 +42,15 @@ PLATFORMS
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
async (~> 1.30.1)
async-http
async-websocket
cyberarm_engine cyberarm_engine
digest-crc digest-crc
ffi ffi
i18n i18n
ircparser ircparser
launchy launchy
net-ping
rexml rexml
thread-local thread-local
websocket-client-simple
win32-security win32-security
BUNDLED WITH BUNDLED WITH

View File

@@ -4,15 +4,17 @@ class W3DHub
API_TIMEOUT = 10 # seconds API_TIMEOUT = 10 # seconds
USER_AGENT = "Cyberarm's Linux Friendly W3D Hub Launcher v#{W3DHub::VERSION}".freeze USER_AGENT = "Cyberarm's Linux Friendly W3D Hub Launcher v#{W3DHub::VERSION}".freeze
DEFAULT_HEADERS = [ DEFAULT_HEADERS = {
["User-Agent", USER_AGENT], "User-Agent": USER_AGENT,
["Accept", "application/json"] "Accept": "application/json"
].freeze }.freeze
FORM_ENCODED_HEADERS = ( FORM_ENCODED_HEADERS = {
DEFAULT_HEADERS + [["Content-Type", "application/x-www-form-urlencoded"]] "User-Agent": USER_AGENT,
).freeze "Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded"
}.freeze
def self.on_fiber(method, *args, &callback) def self.on_thread(method, *args, &callback)
BackgroundWorker.job(-> { Api.send(method, *args) }, callback) BackgroundWorker.job(-> { Api.send(method, *args) }, callback)
end end
@@ -27,8 +29,6 @@ class W3DHub
ENDPOINT = "https://secure.w3dhub.com".freeze ENDPOINT = "https://secure.w3dhub.com".freeze
def self.post(url, headers = DEFAULT_HEADERS, body = nil) def self.post(url, headers = DEFAULT_HEADERS, body = nil)
@client ||= Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(ENDPOINT, protocol: Async::HTTP::Protocol::HTTP10))
# TODO: Check if session has expired and attempt to refresh session before submitting request # TODO: Check if session has expired and attempt to refresh session before submitting request
logger.debug(LOG_TAG) { "Fetching POST \"#{url}\"..." } logger.debug(LOG_TAG) { "Fetching POST \"#{url}\"..." }
@@ -37,17 +37,15 @@ class W3DHub
if Store.account if Store.account
logger.debug(LOG_TAG) { " Injecting Authorization header..." } logger.debug(LOG_TAG) { " Injecting Authorization header..." }
headers = headers.dup headers = headers.dup
headers << ["Authorization", "Bearer #{Store.account.access_token}"] headers["Authorization"] = "Bearer #{Store.account.access_token}"
end end
begin begin
Async::Task.current.with_timeout(API_TIMEOUT) do Excon.post(url, headers: headers, body: body, tcp_nodelay: true, write_timeout: API_TIMEOUT, read_timeout: API_TIMEOUT, connection_timeout: API_TIMEOUT)
@client.post(url, headers, body) rescue Excon::Errors::Timeout
end
rescue Async::TimeoutError
logger.error(LOG_TAG) { "Connection to \"#{url}\" timed out after: #{API_TIMEOUT} seconds" } logger.error(LOG_TAG) { "Connection to \"#{url}\" timed out after: #{API_TIMEOUT} seconds" }
DummyResponse.new DummyResponse.new
rescue EOFError => e rescue Excon::Socket::Error => e
logger.error(LOG_TAG) { "Connection to \"#{url}\" errored:" } logger.error(LOG_TAG) { "Connection to \"#{url}\" errored:" }
logger.error(LOG_TAG) { e } logger.error(LOG_TAG) { e }
DummyResponse.new DummyResponse.new
@@ -73,16 +71,16 @@ class W3DHub
body = "data=#{JSON.dump({refreshToken: refresh_token})}" body = "data=#{JSON.dump({refreshToken: refresh_token})}"
response = post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body) response = post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body)
if response.success? if response.status == 200
user_data = JSON.parse(response.read, symbolize_names: true) user_data = JSON.parse(response.body, symbolize_names: true)
return false if user_data[:error] return false if user_data[:error]
body = "data=#{JSON.dump({ id: user_data[:userid] })}" body = "data=#{JSON.dump({ id: user_data[:userid] })}"
user_details = post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body) user_details = post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body)
if user_details.success? if user_details.status == 200
user_details_data = JSON.parse(user_details.read, symbolize_names: true) user_details_data = JSON.parse(user_details.body, symbolize_names: true)
else else
logger.error(LOG_TAG) { "Failed to fetch refresh user details:" } logger.error(LOG_TAG) { "Failed to fetch refresh user details:" }
logger.error(LOG_TAG) { user_details } logger.error(LOG_TAG) { user_details }
@@ -101,16 +99,16 @@ class W3DHub
body = "data=#{JSON.dump({username: username, password: password})}" body = "data=#{JSON.dump({username: username, password: password})}"
response = post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body) response = post("#{ENDPOINT}/apis/launcher/1/user-login", FORM_ENCODED_HEADERS, body)
if response.success? if response.status == 200
user_data = JSON.parse(response.read, symbolize_names: true) user_data = JSON.parse(response.body, symbolize_names: true)
return false if user_data[:error] return false if user_data[:error]
body = "data=#{JSON.dump({ id: user_data[:userid] })}" body = "data=#{JSON.dump({ id: user_data[:userid] })}"
user_details = post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body) user_details = post("#{ENDPOINT}/apis/w3dhub/1/get-user-details", FORM_ENCODED_HEADERS, body)
if user_details.success? if user_details.status == 200
user_details_data = JSON.parse(user_details.read, symbolize_names: true) user_details_data = JSON.parse(user_details.body, symbolize_names: true)
else else
logger.error(LOG_TAG) { "Failed to fetch user details:" } logger.error(LOG_TAG) { "Failed to fetch user details:" }
logger.error(LOG_TAG) { user_details } logger.error(LOG_TAG) { user_details }
@@ -137,8 +135,8 @@ class W3DHub
def self.service_status def self.service_status
response = post("#{ENDPOINT}/apis/w3dhub/1/get-service-status", DEFAULT_HEADERS) response = post("#{ENDPOINT}/apis/w3dhub/1/get-service-status", DEFAULT_HEADERS)
if response.success? if response.status == 200
ServiceStatus.new(response.read) ServiceStatus.new(response.body)
else else
logger.error(LOG_TAG) { "Failed to fetch service status:" } logger.error(LOG_TAG) { "Failed to fetch service status:" }
logger.error(LOG_TAG) { response } logger.error(LOG_TAG) { response }
@@ -153,8 +151,8 @@ class W3DHub
def self.applications def self.applications
response = post("#{ENDPOINT}/apis/launcher/1/get-applications") response = post("#{ENDPOINT}/apis/launcher/1/get-applications")
if response.success? if response.status == 200
Applications.new(response.read) Applications.new(response.body)
else else
logger.error(LOG_TAG) { "Failed to fetch applications list:" } logger.error(LOG_TAG) { "Failed to fetch applications list:" }
logger.error(LOG_TAG) { response } logger.error(LOG_TAG) { response }
@@ -170,8 +168,8 @@ class W3DHub
body = "data=#{JSON.dump({category: category})}" body = "data=#{JSON.dump({category: category})}"
response = post("#{ENDPOINT}/apis/w3dhub/1/get-news", FORM_ENCODED_HEADERS, body) response = post("#{ENDPOINT}/apis/w3dhub/1/get-news", FORM_ENCODED_HEADERS, body)
if response.success? if response.status == 200
News.new(response.read) News.new(response.body)
else else
logger.error(LOG_TAG) { "Failed to fetch news for:" } logger.error(LOG_TAG) { "Failed to fetch news for:" }
logger.error(LOG_TAG) { category } logger.error(LOG_TAG) { category }
@@ -188,8 +186,8 @@ class W3DHub
body = URI.encode_www_form("data": JSON.dump({ packages: packages })) body = URI.encode_www_form("data": JSON.dump({ packages: packages }))
response = post("#{ENDPOINT}/apis/launcher/1/get-package-details", FORM_ENCODED_HEADERS, body) response = post("#{ENDPOINT}/apis/launcher/1/get-package-details", FORM_ENCODED_HEADERS, body)
if response.success? if response.status == 200
hash = JSON.parse(response.read, symbolize_names: true) hash = JSON.parse(response.body, symbolize_names: true)
hash[:packages].map { |pkg| Package.new(pkg) } hash[:packages].map { |pkg| Package.new(pkg) }
else else
logger.error(LOG_TAG) { "Failed to fetch package details for:" } logger.error(LOG_TAG) { "Failed to fetch package details for:" }
@@ -214,8 +212,8 @@ class W3DHub
body = URI.encode_www_form("data": JSON.dump({ serverPath: app_id })) body = URI.encode_www_form("data": JSON.dump({ serverPath: app_id }))
response = post("#{ENDPOINT}/apis/w3dhub/1/get-server-events", FORM_ENCODED_HEADERS, body) response = post("#{ENDPOINT}/apis/w3dhub/1/get-server-events", FORM_ENCODED_HEADERS, body)
if response.success? if response.status == 200
array = JSON.parse(response.read, symbolize_names: true) array = JSON.parse(response.body, symbolize_names: true)
array.map { |e| Event.new(e) } array.map { |e| Event.new(e) }
else else
false false
@@ -227,11 +225,11 @@ class W3DHub
SERVER_LIST_ENDPOINT = "https://gsh.w3dhub.com".freeze SERVER_LIST_ENDPOINT = "https://gsh.w3dhub.com".freeze
def self.get(url, headers = DEFAULT_HEADERS, body = nil) def self.get(url, headers = DEFAULT_HEADERS, body = nil)
@client ||= Async::HTTP::Client.new(Async::HTTP::Endpoint.parse(SERVER_LIST_ENDPOINT, protocol: Async::HTTP::Protocol::HTTP10)) @client ||= Excon.new(SERVER_LIST_ENDPOINT, persistent: true)
logger.debug(LOG_TAG) { "Fetching GET \"#{url}\"..." } logger.debug(LOG_TAG) { "Fetching GET \"#{url}\"..." }
@client.get(url, headers, body) Excon.get(url, headers: headers, body: body, persistent: true)
end end
# Method: GET # Method: GET
@@ -254,8 +252,8 @@ class W3DHub
def self.server_list(level = 1) def self.server_list(level = 1)
response = get("#{SERVER_LIST_ENDPOINT}/listings/getAll/v2?statusLevel=#{level}") response = get("#{SERVER_LIST_ENDPOINT}/listings/getAll/v2?statusLevel=#{level}")
if response.success? if response.status == 200
data = JSON.parse(response.read, symbolize_names: true) data = JSON.parse(response.body, symbolize_names: true)
return data.map { |hash| ServerListServer.new(hash) } return data.map { |hash| ServerListServer.new(hash) }
end end
@@ -276,8 +274,8 @@ class W3DHub
def self.server_details(id, level) def self.server_details(id, level)
response = get("#{SERVER_LIST_ENDPOINT}/listings/getStatus/v2/#{id}?statusLevel=#{level}") response = get("#{SERVER_LIST_ENDPOINT}/listings/getStatus/v2/#{id}?statusLevel=#{level}")
if response.success? if response.status == 200
hash = JSON.parse(response.read, symbolize_names: true) hash = JSON.parse(response.body, symbolize_names: true)
return hash return hash
end end

View File

@@ -42,6 +42,8 @@ class W3DHub
end end
def send_ping(force_ping = false) def send_ping(force_ping = false)
return
if force_ping || Gosu.milliseconds - @last_pinged >= @ping_interval if force_ping || Gosu.milliseconds - @last_pinged >= @ping_interval
@last_pinged = Gosu.milliseconds @last_pinged = Gosu.milliseconds

View File

@@ -3,65 +3,6 @@ class W3DHub
class ServerListUpdater class ServerListUpdater
LOG_TAG = "W3DHub::Api::ServerListUpdater".freeze LOG_TAG = "W3DHub::Api::ServerListUpdater".freeze
include CyberarmEngine::Common include CyberarmEngine::Common
##!!! When this breaks update from: https://github.com/socketry/async-websocket/blob/master/lib/async/websocket/connection.rb
# refinements preserves super... 😢
class PatchedConnection < ::Protocol::WebSocket::Connection
include ::Protocol::WebSocket::Headers
def self.call(framer, protocol = [], **options)
instance = self.new(framer, Array(protocol).first, **options)
return instance unless block_given?
begin
yield instance
ensure
instance.close
end
end
def initialize(framer, protocol = nil, response: nil, **options)
super(framer, **options)
@protocol = protocol
@response = response
end
def close
super
if @response
@response.finish
@response = nil
end
end
attr :protocol
def read
if (buffer = super)
buffer.split("\x1e").map { |json| parse(json) }
end
end
def write(object)
super("#{dump(object)}\x1e")
end
def parse(buffer)
JSON.parse(buffer, symbolize_names: true)
end
def dump(object)
JSON.dump(object)
end
def call
self.close
end
end
@@instance = nil @@instance = nil
def self.instance def self.instance
@@ -87,45 +28,36 @@ class W3DHub
retry retry
end end
end end
logger.debug(LOG_TAG) { "Cleaning up..." }
@@instance = nil
end end
def connect def connect
Async do |task| auto_reconnect = false
internet = Async::HTTP::Internet.instance
logger.debug(LOG_TAG) { "Requesting connection token..." } logger.debug(LOG_TAG) { "Requesting connection token..." }
response = internet.post("https://gsh.w3dhub.com/listings/push/v2/negotiate?negotiateVersion=1", Api::DEFAULT_HEADERS, [""]) response = Excon.post("https://gsh.w3dhub.com/listings/push/v2/negotiate?negotiateVersion=1", headers: Api::DEFAULT_HEADERS, body: "")
data = JSON.parse(response.read, symbolize_names: true) data = JSON.parse(response.body, symbolize_names: true)
id = data[:connectionToken] id = data[:connectionToken]
endpoint = Async::HTTP::Endpoint.parse("https://gsh.w3dhub.com/listings/push/v2?id=#{id}", alpn_protocols: Async::HTTP::Protocol::HTTP11.names) endpoint = "https://gsh.w3dhub.com/listings/push/v2?id=#{id}"
logger.debug(LOG_TAG) { "Connecting to websocket..." } logger.debug(LOG_TAG) { "Connecting to websocket..." }
Async::WebSocket::Client.connect(endpoint, headers: Api::DEFAULT_HEADERS, handler: PatchedConnection) do |connection| WebSocket::Client::Simple.connect(endpoint, headers: Api::DEFAULT_HEADERS) do |ws|
logger.debug(LOG_TAG) { "Requesting json protocol, v1..." } ws.on(:message) do |msg|
connection.write({ protocol: "json", version: 1 }) msg = msg.data.split("\x1e").first
connection.flush
logger.debug(LOG_TAG) { "Received: #{connection.read}" }
logger.debug(LOG_TAG) { "Sending \"PING\"(?)" }
connection.write({ "type": 6 })
logger.debug(LOG_TAG) { "Subscribing to server changes..." } hash = JSON.parse(msg, symbolize_names: true)
Store.server_list.each_with_index do |server, i|
i += 1
mode = 1 # 2 full details, 1 basic details
out = { "type": 1, "invocationId": "#{i}", "target": "SubscribeToServerStatusUpdates", "arguments": [server.id, mode] }
connection.write(out)
end
logger.debug(LOG_TAG) { "Waiting for data..." } # Send PING(?)
while (message = connection.read) if hash.empty? || hash[:type] == 6
connection.write({ type: 6 }) if message.first[:type] == 6 ws.send({ type: 6 }.to_json + "\x1e")
else
if message&.first&.fetch(:type) == 1 case hash[:type]
message.each do |rpc| when 1
next unless rpc[:target] == "ServerStatusChanged" if hash[:target] == "ServerStatusChanged"
id, data = hash[:arguments]
id, data = rpc[:arguments]
server = Store.server_list.find { |s| s.id == id } server = Store.server_list.find { |s| s.id == id }
server_updated = server&.update(data) server_updated = server&.update(data)
States::Interface.instance&.update_server_browser(server) if server_updated States::Interface.instance&.update_server_browser(server) if server_updated
@@ -133,10 +65,32 @@ class W3DHub
end end
end end
end end
ensure
logger.debug(LOG_TAG) { "Cleaning up..." } ws.on(:open) do
@@instance = nil logger.debug(LOG_TAG) { "Requesting json protocol, v1..." }
ws.send({ protocol: "json", version: 1 }.to_json + "\x1e")
logger.debug(LOG_TAG) { "Subscribing to server changes..." }
Store.server_list.each_with_index do |server, i|
i += 1
mode = 1 # 2 full details, 1 basic details
out = { "type": 1, "invocationId": "#{i}", "target": "SubscribeToServerStatusUpdates", "arguments": [server.id, mode] }
ws.send(out.to_json + "\x1e")
end
end
ws.on(:close) do |e|
p e
auto_reconnect = true
end
ws.on(:error) do |e|
p e
auto_reconnect = true
end
end end
connect if auto_reconnect
end end
end end
end end

View File

@@ -56,7 +56,7 @@ class W3DHub
@task_state = :running @task_state = :running
Thread.new do Thread.new do
Sync do # Sync do
begin begin
status = execute_task status = execute_task
rescue FailFast rescue FailFast
@@ -78,7 +78,7 @@ class W3DHub
hide_application_taskbar if @task_state == :failed hide_application_taskbar if @task_state == :failed
send_message_dialog(:failure, "Task #{type.inspect} failed for #{@application.name}", @task_failure_reason) if @task_state == :failed && !@fail_silently send_message_dialog(:failure, "Task #{type.inspect} failed for #{@application.name}", @task_failure_reason) if @task_state == :failed && !@fail_silently
end # end
end end
end end

View File

@@ -9,13 +9,12 @@ class W3DHub
logger.info(LOG_TAG) { "Starting background job worker..." } logger.info(LOG_TAG) { "Starting background job worker..." }
@@thread = Thread.current
@@alive = true @@alive = true
@@run = true @@run = true
@@instance = self.new @@instance = self.new
Async do @@instance.handle_jobs
@@instance.handle_jobs
end
end end
def self.instance def self.instance
@@ -30,10 +29,18 @@ class W3DHub
@@alive @@alive
end end
def self.busy?
instance&.busy?
end
def self.shutdown! def self.shutdown!
@@run = false @@run = false
end end
def self.kill!
@@thread.kill
end
def self.job(job, callback, error_handler = nil) def self.job(job, callback, error_handler = nil)
@@instance.add_job(Job.new(job: job, callback: callback, error_handler: error_handler)) @@instance.add_job(Job.new(job: job, callback: callback, error_handler: error_handler))
end end
@@ -43,6 +50,7 @@ class W3DHub
end end
def initialize def initialize
@busy = false
@jobs = [] @jobs = []
end end
@@ -50,12 +58,16 @@ class W3DHub
while BackgroundWorker.run? while BackgroundWorker.run?
job = @jobs.shift job = @jobs.shift
@busy = true
begin begin
job&.do job&.do
rescue => error rescue => error
job&.raise_error(error) job&.raise_error(error)
end end
@busy = !@jobs.empty?
sleep 0.1 sleep 0.1
end end
@@ -67,6 +79,10 @@ class W3DHub
@jobs << job @jobs << job
end end
def busy?
@busy
end
class Job class Job
def initialize(job:, callback:, error_handler: nil, deliver_to_queue: false) def initialize(job:, callback:, error_handler: nil, deliver_to_queue: false)
@job = job @job = job

View File

@@ -16,12 +16,12 @@ class W3DHub
path path
elsif async elsif async
BackgroundWorker.job( BackgroundWorker.job(
-> { Async::HTTP::Internet.instance.get(uri, W3DHub::Api::DEFAULT_HEADERS) }, -> { Api.get(uri, W3DHub::Api::DEFAULT_HEADERS) },
->(response) { response.save(path, "wb") if response.success? } ->(response) { File.open(path, "wb") { |f| f.write response.body } if response.status == 200 }
) )
else else
response = Async::HTTP::Internet.instance.get(uri, W3DHub::Api::DEFAULT_HEADERS) response = Api.get(uri, W3DHub::Api::DEFAULT_HEADERS)
response.save(path, "wb") if response.success? File.open(path, "wb") { |f| f.write response.body } if response.status == 200
end end
end end
@@ -82,7 +82,7 @@ class W3DHub
block.call(chunk, remaining_bytes, total_bytes) block.call(chunk, remaining_bytes, total_bytes)
end end
response.success? response.status == 200
ensure ensure
file&.close file&.close
end end

View File

@@ -40,6 +40,14 @@ class W3DHub
end end
end end
def self.commmand(command)
if windows?
else
IO.popen(command)
end
end
def self.home_directory def self.home_directory
File.expand_path("~") File.expand_path("~")
end end

View File

@@ -78,7 +78,7 @@ class W3DHub
logger.info(LOG_TAG) { "Refreshing user login..." } logger.info(LOG_TAG) { "Refreshing user login..." }
# TODO: Check without network # TODO: Check without network
Api.on_fiber(:refresh_user_login, account.refresh_token) do |refreshed_account| Api.on_thread(:refresh_user_login, account.refresh_token) do |refreshed_account|
update_account_data(refreshed_account) update_account_data(refreshed_account)
end end
@@ -108,7 +108,7 @@ class W3DHub
end end
def service_status def service_status
Api.on_fiber(:service_status) do |service_status| Api.on_thread(:service_status) do |service_status|
@service_status = service_status @service_status = service_status
if @service_status if @service_status
@@ -132,7 +132,7 @@ class W3DHub
def applications def applications
@status_label.value = I18n.t(:"boot.checking_for_updates") @status_label.value = I18n.t(:"boot.checking_for_updates")
Api.on_fiber(:applications) do |applications| Api.on_thread(:applications) do |applications|
if applications if applications
Store.applications = applications Store.applications = applications
@@ -152,7 +152,7 @@ class W3DHub
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
Api.on_fiber(:package_details, packages) do |package_details| Api.on_thread(:package_details, packages) do |package_details|
package_details&.each do |package| package_details&.each do |package|
path = Cache.package_path(package.category, package.subcategory, package.name, package.version) path = Cache.package_path(package.category, package.subcategory, package.name, package.version)
generated_icon_path = "#{GAME_ROOT_PATH}/media/icons/#{package.subcategory}.png" generated_icon_path = "#{GAME_ROOT_PATH}/media/icons/#{package.subcategory}.png"
@@ -180,7 +180,7 @@ class W3DHub
def server_list def server_list
@status_label.value = I18n.t(:"server_browser.fetching_server_list") @status_label.value = I18n.t(:"server_browser.fetching_server_list")
Api.on_fiber(:server_list, 2) do |list| Api.on_thread(:server_list, 2) do |list|
Store.server_list = list.sort_by! { |s| s&.status&.players&.size }.reverse if list Store.server_list = list.sort_by! { |s| s&.status&.players&.size }.reverse if list
Store.server_list_last_fetch = Gosu.milliseconds Store.server_list_last_fetch = Gosu.milliseconds

View File

@@ -35,6 +35,9 @@ class W3DHub
while (block = Store.main_thread_queue.shift) while (block = Store.main_thread_queue.shift)
block&.call block&.call
end end
# Manually sleep main thread so that the BackgroundWorker thread can be scheduled
sleep(update_interval / 1000.0) if W3DHub::BackgroundWorker.busy?
end end
def gain_focus def gain_focus

View File

@@ -5,6 +5,7 @@ require "fileutils"
require "digest" require "digest"
require "rexml" require "rexml"
require "logger" require "logger"
require "time"
class W3DHub class W3DHub
W3DHUB_DEBUG = ARGV.join.include?("--debug") W3DHUB_DEBUG = ARGV.join.include?("--debug")
@@ -22,7 +23,31 @@ end
module Kernel module Kernel
def logger def logger
W3DHub::LOGGER @logger = W3DHub::LOGGER
end
class W3DHubLogger
def initialize
end
def level=(options)
end
def info(tag, &block)
pp [tag, block&.call]
end
def debug(tag, &block)
pp [tag, block&.call]
end
def warn(tag, &block)
pp [tag, block&.call]
end
def error(tag, &block)
pp [tag, block&.call]
end
end end
end end
@@ -42,15 +67,7 @@ end
require "i18n" require "i18n"
require "launchy" require "launchy"
require "websocket-client-simple"
require "async"
require "async/barrier"
require "async/semaphore"
require "async/http/internet/instance"
require "async/http/endpoint"
require "async/websocket/client"
require "protocol/websocket/connection"
require "net/ping"
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
@@ -124,14 +141,22 @@ Thread.new do
W3DHub::BackgroundWorker.create W3DHub::BackgroundWorker.create
end end
until W3DHub::BackgroundWorker.alive?
sleep 0.1
end
logger.info(W3DHub::LOG_TAG) { "Launching window..." } logger.info(W3DHub::LOG_TAG) { "Launching window..." }
# W3DHub::Window.new(width: 980, height: 720, borderless: false, resizable: true).show unless defined?(Ocra) # W3DHub::Window.new(width: 980, height: 720, borderless: false, resizable: true).show unless defined?(Ocra)
W3DHub::Window.new(width: 1280, height: 800, borderless: false, resizable: true).show unless defined?(Ocra) W3DHub::Window.new(width: 1280, height: 800, borderless: false, resizable: true).show unless defined?(Ocra)
# W3DHub::Window.new(width: 1920, height: 1080, borderless: false, resizable: true).show unless defined?(Ocra) # W3DHub::Window.new(width: 1920, height: 1080, borderless: false, resizable: true).show unless defined?(Ocra)
W3DHub::BackgroundWorker.shutdown! W3DHub::BackgroundWorker.shutdown!
worker_soft_halt = Gosu.milliseconds
# Wait for BackgroundWorker to return # Wait for BackgroundWorker to return
while W3DHub::BackgroundWorker.alive? while W3DHub::BackgroundWorker.alive?
W3DHub::BackgroundWorker.kill! if Gosu.milliseconds - worker_soft_halt >= 1_000
sleep 0.1 sleep 0.1
end end