Application updating and repair is now possible, save news and applications listing to cache directory instead of current working directory, added support for application unlinking, misc. other changes

This commit is contained in:
2026-10-07 22:54:42 -05:00
parent bdf678342e
commit a7e1378f55
17 changed files with 285 additions and 64 deletions

View File

@@ -9,7 +9,7 @@ gem "json"
# "game" library gem # "game" library gem
gem "cyberarm_engine" gem "cyberarm_engine"
gem "sdl3-bindings" gem "sdl3-bindings"
gem "gosu", '1.5.0' # temporary, manually built, stand-in for SDL3 supporting gosu gem "gosu", '1.5.0', path: "../gosu" # temporary, manually built, stand-in for SDL3 supporting gosu
# networking libs # networking libs
gem "async" gem "async"

View File

@@ -1,3 +1,8 @@
PATH
remote: ../gosu
specs:
gosu (1.5.0)
GEM GEM
remote: https://gem.coop/ remote: https://gem.coop/
specs: specs:
@@ -27,20 +32,28 @@ GEM
fiber-annotation fiber-annotation
fiber-local (~> 1.1) fiber-local (~> 1.1)
json json
cyberarm_engine (0.25.1) cyberarm_engine (0.30.0)
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)
ffi (1.17.4)
ffi (1.17.4-aarch64-linux-gnu)
ffi (1.17.4-aarch64-linux-musl)
ffi (1.17.4-arm-linux-gnu)
ffi (1.17.4-arm-linux-musl)
ffi (1.17.4-arm64-darwin)
ffi (1.17.4-x86-linux-gnu)
ffi (1.17.4-x86-linux-musl)
ffi (1.17.4-x86_64-darwin)
ffi (1.17.4-x86_64-linux-gnu) ffi (1.17.4-x86_64-linux-gnu)
ffi (1.17.4-x86_64-linux-musl)
fiber-annotation (0.2.0) fiber-annotation (0.2.0)
fiber-local (1.1.0) fiber-local (1.1.0)
fiber-storage fiber-storage
fiber-storage (1.0.1) fiber-storage (1.0.1)
gosu (1.5.0-x86_64-linux) io-endpoint (0.19.0)
ffi (~> 1.12)
io-endpoint (0.18.0)
openssl (>= 3.3.0) openssl (>= 3.3.0)
io-event (1.22.0) io-event (1.22.1)
io-stream (0.14.0) io-stream (0.14.0)
openssl (>= 3.3) openssl (>= 3.3)
ircparser (1.0.0) ircparser (1.0.0)
@@ -51,7 +64,7 @@ GEM
protocol-http (0.72.0) protocol-http (0.72.0)
protocol-http1 (0.41.0) protocol-http1 (0.41.0)
protocol-http (~> 0.68) protocol-http (~> 0.68)
protocol-http2 (0.28.0) protocol-http2 (0.29.1)
protocol-hpack (~> 1.4) protocol-hpack (~> 1.4)
protocol-http (~> 0.62) protocol-http (~> 0.62)
protocol-rack (0.23.0) protocol-rack (0.23.0)
@@ -65,12 +78,22 @@ GEM
rack (3.2.7) rack (3.2.7)
rake (13.4.2) rake (13.4.2)
rexml (3.4.4) rexml (3.4.4)
rubyzip (3.6.0) rubyzip (3.7.0)
sdl3-bindings (1.0.4) sdl3-bindings (1.0.4)
ffi (~> 1.16) ffi (~> 1.16)
PLATFORMS PLATFORMS
x86_64-linux aarch64-linux-gnu
aarch64-linux-musl
arm-linux-gnu
arm-linux-musl
arm64-darwin
ruby
x86-linux-gnu
x86-linux-musl
x86_64-darwin
x86_64-linux-gnu
x86_64-linux-musl
DEPENDENCIES DEPENDENCIES
async async
@@ -80,7 +103,7 @@ DEPENDENCIES
chunky_png chunky_png
cyberarm_engine cyberarm_engine
digest-crc digest-crc
gosu (= 1.5.0) gosu (= 1.5.0)!
ircparser ircparser
json json
logger logger
@@ -96,15 +119,25 @@ CHECKSUMS
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
chunky_png (1.4.0) sha256=89d5b31b55c0cf4da3cf89a2b4ebc3178d8abe8cbaf116a1dba95668502fdcfe chunky_png (1.4.0) sha256=89d5b31b55c0cf4da3cf89a2b4ebc3178d8abe8cbaf116a1dba95668502fdcfe
console (1.38.0) sha256=35b82f59130e95e9be6b832d9e539c8b3988fc192e4a8e9a86ab4922f44ad5de console (1.38.0) sha256=35b82f59130e95e9be6b832d9e539c8b3988fc192e4a8e9a86ab4922f44ad5de
cyberarm_engine (0.25.1) sha256=6f851a2ed0a32bdd25fc619172b546a8e26eefeb13a62c509f226337170d0efe cyberarm_engine (0.30.0) sha256=f67d7410c78ad69c865400f9ea23abae74fdf5f9dadae19006cc4fd706064b0f
digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07 digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07
ffi (1.17.4) sha256=bcd1642e06f0d16fc9e09ac6d49c3a7298b9789bcb58127302f934e437d60acf
ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df
ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39
ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564
ffi (1.17.4-arm-linux-musl) sha256=9d4838ded0465bef6e2426935f6bcc93134b6616785a84ffd2a3d82bc3cf6f95
ffi (1.17.4-arm64-darwin) sha256=19071aaf1419251b0a46852abf960e77330a3b334d13a4ab51d58b31a937001b
ffi (1.17.4-x86-linux-gnu) sha256=38e150df5f4ca555e25beca4090823ae09657bceded154e3c52f8631c1ed72cf
ffi (1.17.4-x86-linux-musl) sha256=fbeec0fc7c795bcf86f623bb18d31ea1820f7bd580e1703a3d3740d527437809
ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e
fiber-annotation (0.2.0) sha256=7abfadf1d119f508867d4103bf231c0354d019cc39a5738945dec2edadaf6c03 fiber-annotation (0.2.0) sha256=7abfadf1d119f508867d4103bf231c0354d019cc39a5738945dec2edadaf6c03
fiber-local (1.1.0) sha256=c885f94f210fb9b05737de65d511136ea602e00c5105953748aa0f8793489f06 fiber-local (1.1.0) sha256=c885f94f210fb9b05737de65d511136ea602e00c5105953748aa0f8793489f06
fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1 fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1
gosu (1.5.0-x86_64-linux) gosu (1.5.0)
io-endpoint (0.18.0) sha256=720d186bcd584e8ba490bfc91dab332cc3c5ffee848b5b03894555cb275151aa io-endpoint (0.19.0) sha256=547aaefe9a78bc57fe25f9f7661b460395ebdfda506f70fdf4cc71d2ae5ca578
io-event (1.22.0) sha256=4936d38dfcf2291d897707fd94690f1a63fdb1ea3ab4fa63c6739fc6c61e1534 io-event (1.22.1) sha256=0dd054cefe4a29f1b8286604f332c14f3208d75f54902961e91e94d45e5e1600
io-stream (0.14.0) sha256=07d9b13ea2066ccbbc761fff8d69ad5a31e47561d31e02cc271f3ed9c1a8f1cd io-stream (0.14.0) sha256=07d9b13ea2066ccbbc761fff8d69ad5a31e47561d31e02cc271f3ed9c1a8f1cd
ircparser (1.0.0) sha256=0d4598f691172e2ad078207c50ba62c0d3428ae841836b0a682e6cd01d62878d ircparser (1.0.0) sha256=0d4598f691172e2ad078207c50ba62c0d3428ae841836b0a682e6cd01d62878d
json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
@@ -113,15 +146,15 @@ CHECKSUMS
protocol-hpack (1.5.1) sha256=6feca238b8078da1cd295677d6f306c6001af92d75fe0643d33e6956cbc3ad91 protocol-hpack (1.5.1) sha256=6feca238b8078da1cd295677d6f306c6001af92d75fe0643d33e6956cbc3ad91
protocol-http (0.72.0) sha256=b153474785979495b9f2df78f0fdc2e02edc7e92c1f7ea00425eed73bd02194c protocol-http (0.72.0) sha256=b153474785979495b9f2df78f0fdc2e02edc7e92c1f7ea00425eed73bd02194c
protocol-http1 (0.41.0) sha256=9863874a1228948a896c3e7ea16d13a680c8f3ed50706007c78b7033018b0ea3 protocol-http1 (0.41.0) sha256=9863874a1228948a896c3e7ea16d13a680c8f3ed50706007c78b7033018b0ea3
protocol-http2 (0.28.0) sha256=b7c6195dcfa80ec2484670e9fd8fc21024cc654a391aa0792433aff1188c1691 protocol-http2 (0.29.1) sha256=3f4ee667b5730b7d05c70d3771bd5d5cdbb31beedaa9d05f2bdac68c9c984d19
protocol-rack (0.23.0) sha256=63eec3341b20565b3dd155345fd1426ecbbed322cdc8a6accc993905388c03d7 protocol-rack (0.23.0) sha256=63eec3341b20565b3dd155345fd1426ecbbed322cdc8a6accc993905388c03d7
protocol-url (0.19.0) sha256=3f6b9f6aa742a259a0b3610fbbd9685dcd9506adf6734f428ff96259faabfb21 protocol-url (0.19.0) sha256=3f6b9f6aa742a259a0b3610fbbd9685dcd9506adf6734f428ff96259faabfb21
protocol-websocket (0.21.2) sha256=cbe35be8e045597b221874fba5928d7cd2483884126e3e36faece4741dc88eb7 protocol-websocket (0.21.2) sha256=cbe35be8e045597b221874fba5928d7cd2483884126e3e36faece4741dc88eb7
rack (3.2.7) sha256=93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7 rack (3.2.7) sha256=93e13e1c24f93556671d85d2d79fa228c3485815c50d7e2f265b5330c6528fb7
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
rubyzip (3.6.0) sha256=268994d44d62282d1cfd99bf10eae48d7267199158ad7ea3e1fee2da9458b695 rubyzip (3.7.0) sha256=65c19294da75297a939006f3516deacc33185fbd721ff1954f7a231db6d3e121
sdl3-bindings (1.0.4) sha256=326f497c30e6a5cab402e6178403b30c9df4a11a4e109e08c5da6e920c7bb55b sdl3-bindings (1.0.4) sha256=326f497c30e6a5cab402e6178403b30c9df4a11a4e109e08c5da6e920c7bb55b
BUNDLED WITH BUNDLED WITH
4.0.8 4.1.0.beta1

View File

@@ -71,5 +71,9 @@ module W3DHubLauncher
found_server found_server
end end
def self.announce_application_changed(application, state)
CyberarmEngine::EventBus.publish("application_changed", { application: application, state: state })
end
end end
end end

View File

@@ -67,7 +67,7 @@ module W3DHubLauncher
Worker::Api.update_settings(MemCache[:settings]) Worker::Api.update_settings(MemCache[:settings])
# FIXME: broadcast app installed on CyberarmEngine::EventBus ApplicationHelper.announce_application_changed(app, :installed)
end end
end end
end end

View File

@@ -187,7 +187,7 @@ module W3DHubLauncher
Worker::Api.applications do |result| Worker::Api.applications do |result|
if result.okay? if result.okay?
File.write("applications.json", result.data) File.write("#{CACHE_PATH}/applications.json", result.data)
hash = JSON.parse(result.data) hash = JSON.parse(result.data)
applications = hash["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || [] applications = hash["applications"]&.map { |app| W3DHubLauncher::Worker::Api::Application.new(app) } || []

View File

@@ -35,11 +35,19 @@ module W3DHubLauncher
end end
def focus def focus
@parent.show_battleview_panel # @parent.show_battleview_panel
CyberarmEngine::EventBus.subscribe("application_changed") do |hash|
if @current_app&.id == hash[:application].id && @current_channel&.id == hash[:application].channel
populate_game_info
end
end
end end
def blur def blur
@parent.hide_battleview_panel @parent.hide_battleview_panel
CyberarmEngine::EventBus.unsubscribe("application_changed")
end end
def populate_game_content_container def populate_game_content_container
@@ -115,7 +123,7 @@ module W3DHubLauncher
unless MemCache[:"events_#{app_id}"] unless MemCache[:"events_#{app_id}"]
Worker::Api.events(app_id) do |result| Worker::Api.events(app_id) do |result|
if result.okay? if result.okay?
File.write("events_#{app_id}.json", result.data) File.write("#{CACHE_PATH}/events_#{app_id}.json", result.data)
MemCache[:"events_#{app_id}"] = JSON.parse(result.data)&.map { |item| Worker::Api::ServerEvent.new(item) } || [] MemCache[:"events_#{app_id}"] = JSON.parse(result.data)&.map { |item| Worker::Api::ServerEvent.new(item) } || []
populate_game_event if app_id == @current_app.id populate_game_event if app_id == @current_app.id
@@ -126,7 +134,7 @@ module W3DHubLauncher
unless MemCache[:"news_#{app_id}"] unless MemCache[:"news_#{app_id}"]
Worker::Api.news(app_id) do |result| Worker::Api.news(app_id) do |result|
if result.okay? if result.okay?
File.write("news_#{app_id}.json", result.data) File.write("#{CACHE_PATH}/news_#{app_id}.json", result.data)
MemCache[:"news_#{app_id}"] = JSON.parse(result.data)["news"]&.map { |item| Worker::Api::NewsItem.new(item) } || [] MemCache[:"news_#{app_id}"] = JSON.parse(result.data)["news"]&.map { |item| Worker::Api::NewsItem.new(item) } || []
populate_game_news if app_id == @current_app.id populate_game_news if app_id == @current_app.id
@@ -186,15 +194,25 @@ module W3DHubLauncher
flow(width: 1.0, height: 60) do flow(width: 1.0, height: 60) do
if application if application
button "JOIN", tip: "Join most populated or lowest ping server", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn| if @current_channel.update_available?(application.version)
server = ApplicationHelper.play_now_server(application) button "Update", tip: "Update application from v#{application.version} to v#{@current_channel.version}", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **UPDATE_BUTTON_THEME do |btn|
btn.enabled = false
if server Worker::Api.update_application(@current_app.id, @current_channel.id) do |result, status|
ApplicationHelper.join_server(application, server) handle_task_update(result, status)
end
end
else else
puts "No server available." button "JOIN", tip: "Join most populated or lowest ping server", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn|
server = ApplicationHelper.play_now_server(application)
if server
ApplicationHelper.join_server(application, server)
else
puts "No server available."
end
end
end end
end
button safe_get_image("#{ROOT_PATH}/media/icons/singleplayer.png"), tip: "Single player", image_height: 1.0, background_nine_slice: NINE_SLICE_SQUARE, **CTA_BUTTON_THEME do button safe_get_image("#{ROOT_PATH}/media/icons/singleplayer.png"), tip: "Single player", image_height: 1.0, background_nine_slice: NINE_SLICE_SQUARE, **CTA_BUTTON_THEME do
ApplicationHelper.run(application) ApplicationHelper.run(application)
end end
@@ -206,26 +224,48 @@ module W3DHubLauncher
menu_item("Screenshots Folder", tip: "Open application installation folder:\n#{Dir.home}/Documents/W3D Hub/#{application.id}-#{application.channel}/Screenshots") menu_item("Screenshots Folder", tip: "Open application installation folder:\n#{Dir.home}/Documents/W3D Hub/#{application.id}-#{application.channel}/Screenshots")
stack(width: 1.0, height: 2, background: 0xff_bbbbbb) stack(width: 1.0, height: 2, background: 0xff_bbbbbb)
menu_item("Application Settings", tip: "Edit application launch options") menu_item("Application Settings", tip: "Edit application launch options")
menu_item("Repair Installation", tip: "Attempt to fix and repair installation") if @current_app.servicable?
menu_item("Check for Updates", tip: "Manually check for application updates") menu_item("Repair Installation", tip: "Attempt to fix and repair installation") do
Worker::Api.repair_application(@current_app.id, @current_channel.id) do |result, status|
handle_task_update(result, status)
end
end
menu_item("Check for Updates", tip: "Manually check for application updates")
end
stack(width: 1.0, height: 2, background: 0xff_bbbbbb) stack(width: 1.0, height: 2, background: 0xff_bbbbbb)
menu_item("Move Installation", tip: "Move application installation to a different directory or disk") if @current_app.servicable?
menu_item("Unlink Installation", tip: "The application will be removed from the launcher\nbut the application's files will not be touched") menu_item("Move Installation", tip: "Move application installation to a different directory or disk") do
menu_item("Uninstall", tip: "Uninstall application and delete files") # FIXME: Show a prompt dialog for choosing new location
# Worker::Api.move_application(@current_app.id, @current_channel.id) do |result, status|
# handle_task_update(result, status)
# end
end
end
menu_item("Unlink Installation", tip: "The application will be removed from the launcher\nbut the application's files will not be touched") do
Worker::Api.unlink_application(@current_app.id, @current_channel.id) do |result, status|
if result.okay?
MemCache[:settings] = Worker::Api::Settings.new(JSON.parse(result.data))
populate_game_info
end
end
end
if @current_app.servicable?
menu_item("Uninstall", tip: "Uninstall application and delete files")
end
end.show end.show
end end
elsif @current_app.servicable? elsif @current_app.servicable?
# pp @current_app # pp @current_app
button "Import", tip: "Import existing application installation", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn| button "Download", tip: "Download and install application", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_LEFT, **CTA_BUTTON_THEME do |btn|
dialog(Dialog::ImportApplication, application: @current_app, channel: @current_channel)
end
button "Download", tip: "Download and install application", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_RIGHT, **CTA_BUTTON_THEME do |btn|
btn.enabled = false btn.enabled = false
Worker::Api.install_application(@current_app.id, @current_channel.id) do |status, result| Worker::Api.install_application(@current_app.id, @current_channel.id) do |result, status|
handle_task_update(status, result) handle_task_update(result, status)
end end
end end
button "Import", tip: "Import existing application installation", fill: true, height: 1.0, background_nine_slice: NINE_SLICE_ROUNDED_RIGHT, **CTA_BUTTON_THEME do |btn|
dialog(Dialog::ImportApplication, application: @current_app, channel: @current_channel)
end
else else
button "Import", tip: "Import existing application installation", fill: true, height: 1.0, **CTA_BUTTON_THEME do |btn| button "Import", tip: "Import existing application installation", fill: true, height: 1.0, **CTA_BUTTON_THEME do |btn|
dialog(Dialog::ImportApplication, application: @current_app, channel: @current_channel) dialog(Dialog::ImportApplication, application: @current_app, channel: @current_channel)
@@ -297,10 +337,12 @@ module W3DHubLauncher
when Worker::Request::STATUS_COMPLETE when Worker::Request::STATUS_COMPLETE
status_bar.hide status_bar.hide
MemCache[:settings].applications << Worker::Api::Settings::Application.new(data["application"]) # FIXME: Application might NOT be installed
application = MemCache[:settings].add_or_update_application(Worker::Api::Settings::Application.new(data["application"]))
Worker::Api.update_settings(MemCache[:settings]) Worker::Api.update_settings(MemCache[:settings])
populate_game_info # FIXME: Application might NOT be installed
ApplicationHelper.announce_application_changed(application, :installed)
when Worker::Request::STATUS_IN_PROGRESS when Worker::Request::STATUS_IN_PROGRESS
status_bar.show status_bar.show

View File

@@ -93,7 +93,7 @@ module W3DHubLauncher
end end
# battleview/friends container # battleview/friends container
@battleview_container = stack(width: 400, height: 1.0, margin_left: LARGE_PADDING) do @battleview_container = stack(width: 400, height: 1.0, margin_left: LARGE_PADDING, visible: false) do
# friend management container # friend management container
flow(width: 1.0, height: 60) do flow(width: 1.0, height: 60) do
flow(width: 1.0, v_align: :center) do flow(width: 1.0, v_align: :center) do
@@ -358,7 +358,7 @@ module W3DHubLauncher
def button_up(id) def button_up(id)
super super
@battleview_container.toggle if id == Gosu::KB_F8 # @battleview_container.toggle if id == Gosu::KB_F8
end end
end end
end end

View File

@@ -39,6 +39,19 @@ module W3DHubLauncher
} }
} }
UPDATE_BUTTON_THEME = {
color: 0xff_ffffff,
background_nine_slice_color: 0xff_e66100,
hover: {
color: 0xff_ffffff,
background_nine_slice_color: 0xff_ff7800
},
active: {
color: 0xff_ffffff,
background_nine_slice_color: 0xff_c64600
}
}
THEME = { THEME = {
TextBlock: { TextBlock: {
text_static: true, text_static: true,

View File

@@ -49,24 +49,38 @@ module W3DHubLauncher
@w3dhub_api = W3DHubLauncher::W3DHubApi.new @w3dhub_api = W3DHubLauncher::W3DHubApi.new
Async do |task| Async do |task|
UNIXServer.open(IPC_PATH) do |server| task.async do
while(socket = server.accept) while true
@client = socket @tasks.delete_if { |t| [Request::STATUS_ERROR, Request::STATUS_COMPLETE].include?(t.status) }
task.async do if @tasks.first && @tasks.all? { |t| t.status == Request::STATUS_PENDING }
while(data = socket.gets) @tasks.first.start(self)
json = JSON.parse(data) end
query = Request::Query.new(type: json["type"].to_sym, request_id: json["request_id"], data: json["data"])
# pp [:server_incoming, data, query] sleep 0.25
end
end
if respond_to?(query.type) task.async do |subtask|
response = send(query.type, query) UNIXServer.open(IPC_PATH) do |server|
# pp [:server_to_client, response] while(socket = server.accept)
payload = { status: response.status, request_id: response.request_id, data: response.result.data, error: response.result.error }.to_json @client = socket
socket.write(payload)
socket.write(RESPONSE_SEPARATOR) task.async do
socket.flush while(data = socket.gets)
json = JSON.parse(data)
query = Request::Query.new(type: json["type"].to_sym, request_id: json["request_id"], data: json["data"])
# pp [:server_incoming, data, query]
if respond_to?(query.type)
response = send(query.type, query)
# pp [:server_to_client, response]
payload = { status: response.status, request_id: response.request_id, data: response.result.data, error: response.result.error }.to_json
socket.write(payload)
socket.write(RESPONSE_SEPARATOR)
socket.flush
end
end end
end end
end end
@@ -269,8 +283,8 @@ module W3DHubLauncher
total_downloaded_bytes: total_downloaded_bytes, total_downloaded_bytes: total_downloaded_bytes,
content_length: content_length content_length: content_length
}) })
# FIXME: Send intermediate response to requester
# send(query, Response.new(Request::STATUS_IN_PROGRESS, query.request_id progress_result)) message_requester(query.request_id, Request::STATUS_IN_PROGRESS, progress_result.data, progress_result.error)
end end
end end
@@ -453,15 +467,44 @@ module W3DHubLauncher
installed_version: nil, installed_version: nil,
target_version: channel.version target_version: channel.version
) )
@tasks.last.start(self)
Response.new(Request::STATUS_PENDING, query.request_id, CyberarmEngine::Result.new(data: true)) Response.new(Request::STATUS_PENDING, query.request_id, CyberarmEngine::Result.new(data: true))
end end
def task_update_application(query) def task_update_application(query)
application = @w3dhub_api.applications.find { |app| app.id == query.data["app_id"] }
channel = application.channels.find { |chan| chan.id == query.data["channel_id"] }
settings_application = @settings.application_installed?(application.id, channel.id)
@tasks << Task::UpdateApplication.new(
request_id: query.request_id,
application: application,
channel: channel,
installed_version: settings_application.version,
target_version: channel.version
)
Response.new(Request::STATUS_PENDING, query.request_id, CyberarmEngine::Result.new(data: true))
end end
def task_repair_application(query) def task_repair_application(query)
application = @w3dhub_api.applications.find { |app| app.id == query.data["app_id"] }
channel = application.channels.find { |chan| chan.id == query.data["channel_id"] }
settings_application = @settings.application_installed?(application.id, channel.id)
@tasks << Task::RepairApplication.new(
request_id: query.request_id,
application: application,
channel: channel,
installed_version: settings_application.version,
target_version: settings_application.version
)
pp @tasks.last
Response.new(Request::STATUS_PENDING, query.request_id, CyberarmEngine::Result.new(data: true))
end end
def task_move_application(query) def task_move_application(query)
@@ -469,5 +512,20 @@ module W3DHubLauncher
def task_uninstall_application(query) def task_uninstall_application(query)
end end
def unlink_application(query)
result = CyberarmEngine::Result.new
application = @settings.application_installed?(query.data["app_id"], query.data["channel_id"])
if application
@settings.remove_application(application)
query = Request::Query.new(query.type, query.request_id, @settings.to_json)
return update_settings(query)
end
deliver_response(result, query)
end
end end
end end

View File

@@ -100,6 +100,11 @@ module W3DHubLauncher
def self.uninstall_application(app_id, channel_id, &block) def self.uninstall_application(app_id, channel_id, &block)
Worker::Request.new(:task_uninstall_application, { app_id: app_id, channel_id: channel_id }, &block) Worker::Request.new(:task_uninstall_application, { app_id: app_id, channel_id: channel_id }, &block)
end end
# request unlinking of application
def self.unlink_application(app_id, channel_id, &block)
Worker::Request.new(:unlink_application, { app_id: app_id, channel_id: channel_id }, &block)
end
end end
end end
end end

View File

@@ -80,6 +80,10 @@ module W3DHubLauncher
extended_data("serverchannel", @id) extended_data("serverchannel", @id)
end end
def update_available?(current_version)
current_version < @version
end
def to_json(context = nil) def to_json(context = nil)
{ {
"id": @id, "id": @id,

View File

@@ -41,6 +41,22 @@ module W3DHubLauncher
applications.find { |app| app.id == app_id && app.channel == channel_id } applications.find { |app| app.id == app_id && app.channel == channel_id }
end end
def add_or_update_application(application)
index = applications.index { |app| app.id == application.id && app.channel == application.channel }
if index
applications[index] = application
else
applications << application
end
application
end
def remove_application(application)
applications.delete(application)
end
# User explictly set options # User explictly set options
class Preferences class Preferences
SCHEMA = 0 SCHEMA = 0

View File

@@ -6,6 +6,7 @@ module W3DHubLauncher
IndexedFile = Data.define(:type, :path) IndexedFile = Data.define(:type, :path)
attr_reader :request_id, :application, :channel, :installed_version, :target_version attr_reader :request_id, :application, :channel, :installed_version, :target_version
attr_reader :status
def initialize(request_id:, application:, channel:, installed_version:, target_version:) def initialize(request_id:, application:, channel:, installed_version:, target_version:)
@request_id = request_id @request_id = request_id
@@ -16,6 +17,8 @@ module W3DHubLauncher
pp self pp self
@status = Worker::Request::STATUS_PENDING
# caching mechinism for file path normalization # caching mechinism for file path normalization
@file_index = {} @file_index = {}
# manifests! # manifests!
@@ -36,19 +39,28 @@ module W3DHubLauncher
end end
def start(worker) def start(worker)
return unless status == Worker::Request::STATUS_PENDING
@worker = worker @worker = worker
@installation_directory ||= application_target_installation_directory @installation_directory ||= application_target_installation_directory
update_status(label: "Starting...", fraction: 0.0) update_status(label: "Starting...", fraction: 0.0)
execute
@status = Worker::Request::STATUS_BUSY
v = execute
@status = Worker::Request::STATUS_COMPLETE
v
end end
def abort_task!(reason = "") def abort_task!(reason = "")
puts reason puts reason
message_requester(Worker::Request::STATUS_ERROR, data: nil, error: reason) message_requester(Worker::Request::STATUS_ERROR, data: nil, error: reason)
@status = Worker::Request::STATUS_ERROR
raise reason raise reason
end end
@@ -120,6 +132,8 @@ module W3DHubLauncher
@required_manifest_files = @manifest_files.clone @required_manifest_files = @manifest_files.clone
cached_checksums = {}
# Process manifest game files in NEWEST to OLDEST order so that we don't erroneously flag # Process manifest game files in NEWEST to OLDEST order so that we don't erroneously flag
# valid files as invalid due to an OLDER version of the file being checked FIRST. # valid files as invalid due to an OLDER version of the file being checked FIRST.
@manifest_files.reverse.each do |file| @manifest_files.reverse.each do |file|
@@ -133,13 +147,17 @@ module W3DHubLauncher
next unless File.exist?(file_path) # && !File.directory?(file_path) next unless File.exist?(file_path) # && !File.directory?(file_path)
puts "verifying #{file_path}..." puts "verifying #{file_path}..."
checksum = Digest::SHA256.file(file_path).hexdigest.upcase checksum = cached_checksums[file_path] || Digest::SHA256.file(file_path).hexdigest.upcase
cached_checksums[file_path] ||= checksum
if checksum == file.checksum if checksum == file.checksum
@required_manifest_files.delete(file) @required_manifest_files.delete(file)
# remove irrelevant older versions of the file from consideration # remove irrelevant older versions of the file from consideration
@required_manifest_files.delete_if { |f| f.name.casecmp?(file.name) && f.version < file.version } @required_manifest_files.delete_if { |f| f.name.casecmp?(file.name) && f.version < file.version }
puts " verified:#{file.version}"
else
puts " failed verification:#{file.version}"
end end
end end
end end
@@ -279,12 +297,14 @@ module W3DHubLauncher
timestamp: Time.now.to_i timestamp: Time.now.to_i
) )
@status = Worker::Request::STATUS_COMPLETE
message_requester(Worker::Request::STATUS_COMPLETE, data: { application: app }) message_requester(Worker::Request::STATUS_COMPLETE, data: { application: app })
puts "APPLICATION: #{@application.name} #{@application.id}:#{@channel.id}:#{@target_version} installed." puts "APPLICATION: #{@application.name} #{@application.id}:#{@channel.id}:#{@target_version} installed."
end end
def mark_application_uninstalled def mark_application_uninstalled
@status = Worker::Request::STATUS_COMPLETE
message_requester(Worker::Request::STATUS_COMPLETE, data: {}) message_requester(Worker::Request::STATUS_COMPLETE, data: {})
end end
@@ -295,7 +315,7 @@ module W3DHubLauncher
end end
def update_status(label:, fraction:) def update_status(label:, fraction:)
message_requester(data: { status: { title: "TASK #{@application.name} (#{@channel.name})", label: label, fraction: fraction } } ) message_requester(data: { status: { title: "#{@task_verb} #{@application.name} (#{@channel.name})", label: label, fraction: fraction } } )
end end
def application_target_installation_directory def application_target_installation_directory

View File

@@ -2,6 +2,7 @@ module W3DHubLauncher
class Task class Task
class InstallApplication < Task class InstallApplication < Task
def setup def setup
@task_verb = "Installing".freeze
end end
def execute def execute

View File

@@ -0,0 +1,11 @@
module W3DHubLauncher
class Task
class RepairApplication < InstallApplication
def setup
super
@task_verb = "Repairing".freeze
end
end
end
end

View File

@@ -0,0 +1,11 @@
module W3DHubLauncher
class Task
class UpdateApplication < InstallApplication
def setup
super
@task_verb = "Updating".freeze
end
end
end
end

View File

@@ -77,6 +77,9 @@ module W3DHubLauncher
W3DHubLauncher::Worker.new.listen W3DHubLauncher::Worker.new.listen
end end
# Prevent race condition where server IPC isn't ready before the client attempts to connect
sleep 0.1
# UNIXSocket / client # UNIXSocket / client
WORKER = W3DHubLauncher::Worker.new WORKER = W3DHubLauncher::Worker.new
WORKER.connect WORKER.connect