From b3665af5c4e61db38fb18bb78f4de451ae84010c Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Mon, 22 Nov 2021 11:39:42 -0600 Subject: [PATCH] Disable async-websocket for now, added i18n gem, added support for translations, added password prompt for passworded servers --- Gemfile | 3 +- Gemfile.lock | 35 ------------ lib/api/server_list_server.rb | 3 +- lib/application_manager.rb | 8 ++- lib/pages/games.rb | 28 +++++----- lib/pages/login.rb | 10 ++-- lib/pages/server_browser.rb | 102 ++++++++++++++++++++++------------ lib/states/interface.rb | 24 ++++---- lib/states/prompt_dialog.rb | 2 +- locales/en.yml | 46 +++++++++++++++ w3dhub.rb | 4 ++ 11 files changed, 157 insertions(+), 108 deletions(-) create mode 100644 locales/en.yml diff --git a/Gemfile b/Gemfile index aa91633..89963ac 100644 --- a/Gemfile +++ b/Gemfile @@ -2,4 +2,5 @@ source "https://rubygems.org" gem "cyberarm_engine" gem "launchy" -gem "async-websocket" \ No newline at end of file +gem "i18n" +# gem "async-websocket" diff --git a/Gemfile.lock b/Gemfile.lock index f18f9ce..6be255f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,59 +3,24 @@ GEM specs: addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) - async (1.30.1) - console (~> 1.10) - nio4r (~> 2.3) - timers (~> 4.1) - async-http (0.56.5) - 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) - async-io (1.32.2) - async - async-pool (0.3.9) - 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) - console (1.14.0) - fiber-local cyberarm_engine (0.19.1) clipboard (~> 1.3.5) excon (~> 0.78.0) gosu (~> 1.1) gosu_more_drawables (~> 0.3) excon (0.78.1) - fiber-local (1.0.0) gosu (1.2.0) gosu_more_drawables (0.3.1) launchy (2.5.0) addressable (~> 2.7) - nio4r (2.5.8) - protocol-hpack (1.4.2) - protocol-http (0.22.5) - protocol-http1 (0.14.2) - 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.6) - timers (4.3.3) PLATFORMS x64-mingw32 x86_64-linux DEPENDENCIES - async-websocket cyberarm_engine launchy diff --git a/lib/api/server_list_server.rb b/lib/api/server_list_server.rb index 796dffe..460644b 100644 --- a/lib/api/server_list_server.rb +++ b/lib/api/server_list_server.rb @@ -16,7 +16,7 @@ class W3DHub end class Status - attr_reader :name, :map, :max_players, :player_count, :started, :remaining, :teams, :players + attr_reader :name, :password, :map, :max_players, :player_count, :started, :remaining, :teams, :players def initialize(hash) @data = hash @@ -25,6 +25,7 @@ class W3DHub @players = @data[:players]&.map { |t| Player.new(t) } @name = @data[:name] + @password = @data[:password] || false @map = @data[:map] @max_players = @data[:maxplayers] @player_count = @players.size || @data[:numplayers].to_i diff --git a/lib/application_manager.rb b/lib/application_manager.rb index 22b6c1f..7befa18 100644 --- a/lib/application_manager.rb +++ b/lib/application_manager.rb @@ -46,7 +46,8 @@ class W3DHub exe = "#{app_data[:install_directory]}/#{config_exe}" if File.exist?(exe) - Process.spawn("#{wine_command(app_id, channel)}\"#{exe}\"") + pid = Process.spawn("#{wine_command(app_id, channel)}\"#{exe}\"") + Process.detach(pid) end end end @@ -114,7 +115,9 @@ class W3DHub def run(app_id, channel, *args) if (app_data = installed?(app_id, channel)) - Process.spawn("#{wine_command(app_id, channel)}\"#{app_data[:install_path]}\"", *args) + pp "#{wine_command(app_id, channel)}#{app_data[:install_path]}", *args + pid = Process.spawn("#{wine_command(app_id, channel)}#{app_data[:install_path]}", *args) + Process.detach(pid) end end @@ -122,6 +125,7 @@ class W3DHub if installed?(app_id, channel) && window.settings[:server_list_username].to_s.length.positive? run( app_id, channel, + "-launcher", "+connect #{server.address}:#{server.port}", "+netplayername \"#{window.settings[:server_list_username]}\"", password ? "+password \"#{password}\"" : "" diff --git a/lib/pages/games.rb b/lib/pages/games.rb index af4c860..aba35d1 100644 --- a/lib/pages/games.rb +++ b/lib/pages/games.rb @@ -61,7 +61,7 @@ class W3DHub flow(width: 1.0, height: 0.03) do # background 0xff_444411 - inscription "Channel" + inscription I18n.t(:"games.channel") list_box(items: game.channels.map(&:name), choose: channel.name, enabled: game.channels.count > 1, margin_top: 0, margin_bottom: 0, width: 128, padding_left: 1, padding_top: 1, padding_right: 1, padding_bottom: 1, text_size: 14) do |value| @@ -87,15 +87,15 @@ class W3DHub if window.application_manager.installed?(game.id, channel.id) Hash.new.tap { |hash| - hash["Game Settings"] = { icon: "gear", block: proc { window.application_manager.settings(game.id, channel.id) } } - hash["Wine Configuration"] = { icon: "gear", block: proc { window.application_manager.wine_configuration(game.id, channel.id) } } if W3DHub.unix? + hash[I18n.t(:"games.game_settings")] = { icon: "gear", block: proc { window.application_manager.settings(game.id, channel.id) } } + hash[I18n.t(:"games.wine_configuration")] = { icon: "gear", block: proc { window.application_manager.wine_configuration(game.id, channel.id) } } if W3DHub.unix? if game.id != "ren" - hash["Repair Installation"] = { icon: "wrench", block: proc { window.application_manager.repair(game.id, channel.id) } } - hash["Uninstall"] = { icon: "trashCan", block: proc { window.application_manager.uninstall(game.id, channel.id) } } + hash[I18n.t(:"games.repair_installation")] = { icon: "wrench", block: proc { window.application_manager.repair(game.id, channel.id) } } + hash[I18n.t(:"games.uninstall_game")] = { icon: "trashCan", block: proc { window.application_manager.uninstall(game.id, channel.id) } } end - hash["Install Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :installation) } } - hash["User Data Folder"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :user_data) } } - hash["View Screenshots"] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :screenshots) } } + hash[I18n.t(:"games.install_folder")] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :installation) } } + hash[I18n.t(:"games.user_data_folder")] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :user_data) } } + hash[I18n.t(:"games.view_screenshots")] = { icon: nil, block: proc { window.application_manager.show_folder(game.id, channel.id, :screenshots) } } }.each do |key, hash| flow(width: 1.0, height: 22, margin_bottom: 8) do image "#{GAME_ROOT_PATH}/media/ui_icons/#{hash[:icon]}.png", width: 0.11 if hash[:icon] @@ -128,22 +128,22 @@ class W3DHub # background 0xff_551100 if window.application_manager.installed?(game.id, channel.id) - button "Play Now", margin_left: 24 do + button "#{I18n.t(:"interface.play_now")}", margin_left: 24 do window.application_manager.run(game.id, channel.id) end - button "Single Player", margin_left: 24 do + button "#{I18n.t(:"interface.single_player")}", margin_left: 24 do window.application_manager.run(game.id, channel.id) end else unless game.id == "ren" - button "Install", margin_left: 24, enabled: !window.application_manager.task?(:installer, game.id, channel.id) do |button| + button "#{I18n.t(:"interface.install")}", margin_left: 24, enabled: !window.application_manager.task?(:installer, game.id, channel.id) do |button| button.enabled = false window.application_manager.install(game.id, channel.id) end end - button "Import", margin_left: 24, enabled: false do + button "#{I18n.t(:"interface.import")}", margin_left: 24, enabled: false do window.application_manager.import(game.id, channel.id, "?") end end @@ -159,7 +159,7 @@ class W3DHub end @game_news_container.clear do - title "Fetching News...", padding: 8 + title I18n.t(:"games.fetching_news"), padding: 8 end end end @@ -203,7 +203,7 @@ class W3DHub flow(width: 1.0) do inscription item.timestamp.strftime("%Y-%m-%d"), width: 0.5 - link "Read More", width: 0.5, text_align: :right, text_size: 14 do + link I18n.t(:"games.read_more"), width: 0.5, text_align: :right, text_size: 14 do Launchy.open(item.uri) end end diff --git a/lib/pages/login.rb b/lib/pages/login.rb index b5af8d6..587dbfd 100644 --- a/lib/pages/login.rb +++ b/lib/pages/login.rb @@ -80,8 +80,8 @@ class W3DHub tagline "#{window.account.username}" flow(width: 1.0) do - link("Logout", text_size: 16) { depopulate_account_info } - link "Profile", text_size: 16 do + link(I18n.t(:"interface.log_out"), text_size: 16, width: 0.5) { depopulate_account_info } + link I18n.t(:"interface.profile"), text_size: 16, width: 0.49 do Launchy.open("https://secure.w3dhub.com/forum/index.php?showuser=#{window.account.id}") end end @@ -98,11 +98,11 @@ class W3DHub @host.instance_variable_get(:"@account_container").clear do stack(width: 0.7, height: 1.0) do # background 0xff_222222 - tagline "Not Logged In", text_wrap: :none + tagline "#{I18n.t(:"interface.not_logged_in")}", text_wrap: :none flow(width: 1.0) do - link("Log in", text_size: 16) { page(W3DHub::Pages::Login) } - link "Register", text_size: 16 do + link(I18n.t(:"interface.log_in"), text_size: 16, width: 0.5) { page(W3DHub::Pages::Login) } + link I18n.t(:"interface.register"), text_size: 16, width: 0.49 do Launchy.open("https://secure.w3dhub.com/forum/index.php?app=core&module=global§ion=register") end end diff --git a/lib/pages/server_browser.rb b/lib/pages/server_browser.rb index 16e327d..1d31989 100644 --- a/lib/pages/server_browser.rb +++ b/lib/pages/server_browser.rb @@ -13,7 +13,7 @@ class W3DHub body.clear do stack(width: 1.0, height: 1.0, padding: 8) do stack(width: 1.0, height: 0.04) do - inscription "Filters" + inscription "#{I18n.t(:"server_browser.filters")}" end flow(width: 1.0, height: 0.06) do @@ -38,7 +38,7 @@ class W3DHub end end - para "Region" + para I18n.t(:"server_browser.region") list_box items: ["Any", "North America", "Europe"], width: 0.25 do |value| @filter_region = value @@ -47,9 +47,9 @@ class W3DHub end flow(width: 0.249, height: 1.0) do - inscription "Nickname:", width: 0.32 + inscription "#{I18n.t(:"server_browser.nickname")}:", width: 0.32 @nickname_label = inscription "#{window.settings[:server_list_username]}", width: 0.6 - image "#{GAME_ROOT_PATH}/media/ui_icons/wrench.png", height: 16, hover: { color: 0xaa_ffffff }, tip: "Set nickname" do + image "#{GAME_ROOT_PATH}/media/ui_icons/wrench.png", height: 16, hover: { color: 0xaa_ffffff }, tip: I18n.t(:"server_browser.set_nickname") do # Prompt for player name prompt_for_nickname( accept_callback: proc do |entry| @@ -74,29 +74,29 @@ class W3DHub end stack(width: 0.50, height: 1.0) do - para "Hostname", text_wrap: :none, width: 1.0 + para "#{I18n.t(:"server_browser.hostname")}", text_wrap: :none, width: 1.0 end flow(width: 0.24, height: 1.0) do - para "Current Map", text_wrap: :none, width: 1.0 + para "#{I18n.t(:"server_browser.current_map")}", text_wrap: :none, width: 1.0 end flow(width: 0.11, height: 1.0) do - para "Players", text_wrap: :none, width: 1.0 + para "#{I18n.t(:"server_browser.players")}", text_wrap: :none, width: 1.0 end stack(width: 0.06) do - para "Ping", text_wrap: :none, width: 1.0 + para "#{I18n.t(:"server_browser.ping")}", text_wrap: :none, width: 1.0 end end @server_list_container = stack(width: 1.0, height: 0.95, scroll: true) do - para "Fetching server list..." + para I18n.t(:"server_browser.fetching_server_list") end end @game_server_info_container = stack(width: 0.38, height: 1.0) do - para "No server selected", width: 1.0, text_align: :center + para I18n.t(:"server_browser.no_server_selected"), width: 1.0, text_align: :center end end end @@ -191,10 +191,12 @@ class W3DHub stack(width: 1.0, height: 0.25) do game_installed = window.application_manager.installed?(server.game, window.applications.games.find { |g| g.id == server.game }.channels.first.id) - button "Join Server", enabled: !game_installed.nil? do + button "#{I18n.t(:"server_browser.join_server")}", enabled: !game_installed.nil? do # Check for nickname # prompt for nickname # !abort unless nickname set + # Check if password needed + # prompt for password # Launch game if window.settings[:server_list_username].to_s.length.zero? prompt_for_nickname( @@ -203,34 +205,44 @@ class W3DHub window.settings[:server_list_username] = entry window.settings.save_settings - window.application_manager.join_server( - server.game, - window.applications.games.find { |g| g.id == server.game }.channels.first.id, server - ) + if server.status.password + prompt_for_password( + accept_callback: proc do |password| + join_server(game, server, password) + end + ) + else + join_server(game, server, nil) + end + end + ) + end + + if server.status.password + prompt_for_password( + accept_callback: proc do |password| + join_server(game, server, password) end ) else - window.application_manager.join_server( - server.game, - window.applications.games.find { |g| g.id == server.game }.channels.first.id, server - ) + join_server(game, server, nil) end end end stack(width: 1.0, height: 0.55, margin_top: 16) do flow(width: 1.0, height: 0.33) do - inscription "Game", width: 0.28, text_wrap: :none + inscription "#{I18n.t(:"server_browser.game")}", width: 0.28, text_wrap: :none inscription "#{game_name(server.game)} (branch)", width: 0.71, text_wrap: :none end flow(width: 1.0, height: 0.33) do - inscription "Map", width: 0.28, text_wrap: :none + inscription "#{I18n.t(:"server_browser.map")}", width: 0.28, text_wrap: :none inscription server.status.map, width: 0.71, text_wrap: :none end flow(width: 1.0, height: 0.33) do - inscription "Max Players", width: 0.28, text_wrap: :none + inscription "#{I18n.t(:"server_browser.max_players")}", width: 0.28, text_wrap: :none inscription "#{server.status.max_players}", width: 0.71, text_wrap: :none end end @@ -285,7 +297,7 @@ class W3DHub list = Api.server_list(2) if list - @server_list = list.sort_by! { |s| s&.status&.players.size }.reverse + @server_list = list.sort_by! { |s| s&.status&.players&.size }.reverse main_thread_queue << proc { populate_server_list } @@ -303,31 +315,47 @@ class W3DHub end def game_name(game) - case game - when "ia" - "Interim Apex" - when "apb" - "Red Alert: A Path Beyond" - when "tsr" - "Tiberian Sun: Reborn" - when "ecw" - "Expansive Civilian Warfare" - else - "C&C Renegade" - end + window.applications.games.detect { |g| g.id == game }&.name end def prompt_for_nickname(accept_callback: nil, cancel_callback: nil) push_state( W3DHub::States::PromptDialog, - title: "Set Nickname", - message: "Set a nickname that will be used when joining a server:", + title: I18n.t(:"server_browser.set_nickname"), + message: I18n.t(:"server_browser.set_nickname_message"), prefill: window.settings[:server_list_username], accept_callback: accept_callback, cancel_callback: cancel_callback, valid_callback: proc { |entry| entry.length.positive? } ) end + + def prompt_for_password(accept_callback: nil, cancel_callback: nil) + push_state( + W3DHub::States::PromptDialog, + title: I18n.t(:"server_browser.enter_password"), + message: I18n.t(:"server_browser.enter_password_message"), + input_type: :password, + accept_callback: accept_callback, + cancel_callback: cancel_callback, + valid_callback: proc { |entry| entry.length.positive? } + ) + end + + def join_server(game, server, password) + if ( + (server.status.password && password.length.positive?) || + !server.status.password) && + window.settings[:server_list_username].to_s.length.zero? + + window.application_manager.join_server( + server.game, + window.applications.games.find { |g| g.id == server.game }.channels.first.id, server, password + ) + else + window.push_state(W3DHub::States::MessageDialog, type: "?", title: "?", message: "?") + end + end end end end diff --git a/lib/states/interface.rb b/lib/states/interface.rb index 2a65afc..d788e00 100644 --- a/lib/states/interface.rb +++ b/lib/states/interface.rb @@ -32,12 +32,12 @@ class W3DHub # background 0xff_8855ff stack(width: 0.75, height: 1.0) do - title "W3D Hub Launcher", height: 0.5 + title "#{I18n.t(:"app_name")}", height: 0.5 flow(width: 1.0, height: 0.5) do flow(width: 0.18, height: 1.0) do button( get_image("#{GAME_ROOT_PATH}/media/ui_icons/gear.png"), - tip: "W3D Hub Launcher Settings", + tip: I18n.t(:"interface.app_settings_tip"), image_height: 1.0, padding_left: 4, padding_top: 4, @@ -50,7 +50,7 @@ class W3DHub button( get_image("#{GAME_ROOT_PATH}/media/ui_icons/import.png"), - tip: "Download Manager", + tip: I18n.t(:"interface.download_manager"), image_height: 1.0, padding_left: 4, padding_top: 4, @@ -64,11 +64,11 @@ class W3DHub @application_taskbar_container = stack(width: 0.77, height: 1.0, margin_left: 16) do flow(width: 1.0, height: 0.65) do - @application_taskbar_label = inscription "Downloading Expansive Civilian Warfare...", width: 0.65, text_wrap: :none - @application_taskbar_status_label = inscription "460.2 MB / 254.5 GB", width: 0.35, text_align: :right, text_wrap: :none + @application_taskbar_label = inscription "", width: 0.65, text_wrap: :none + @application_taskbar_status_label = inscription "", width: 0.35, text_align: :right, text_wrap: :none end - @application_taskbar_progressbar = progress fraction: 0.4, height: 2, width: 1.0 + @application_taskbar_progressbar = progress fraction: 0.0, height: 2, width: 1.0 end end end @@ -76,11 +76,11 @@ class W3DHub @account_container = flow(width: 0.25, height: 1.0) do stack(width: 0.7, height: 1.0) do # background 0xff_222222 - tagline "Not Logged In", text_wrap: :none + tagline "#{I18n.t(:"interface.not_logged_in")}", text_wrap: :none flow(width: 1.0) do - link("Log in", text_size: 16) { page(W3DHub::Pages::Login) } - link "Register", text_size: 16 do + link(I18n.t(:"interface.log_in"), text_size: 16, width: 0.5) { page(W3DHub::Pages::Login) } + link I18n.t(:"interface.register"), text_size: 16, width: 0.49 do Launchy.open("https://secure.w3dhub.com/forum/index.php?app=core&module=global§ion=register") end end @@ -95,15 +95,15 @@ class W3DHub end flow(width: 0.55, height: 1.0) do - link "Games" do + link I18n.t(:"interface.games") do page(W3DHub::Pages::Games) end - link "Server Browser", margin_left: 18 do + link I18n.t(:"interface.server_browser"), margin_left: 18 do page(W3DHub::Pages::ServerBrowser) end - link "Community", margin_left: 18 do + link I18n.t(:"interface.community"), margin_left: 18 do page(W3DHub::Pages::Community) end end diff --git a/lib/states/prompt_dialog.rb b/lib/states/prompt_dialog.rb index 41cf118..1252850 100644 --- a/lib/states/prompt_dialog.rb +++ b/lib/states/prompt_dialog.rb @@ -19,7 +19,7 @@ class W3DHub stack(width: 1.0, height: 0.78, padding: 16) do para @options[:message], width: 1.0 - @prompt_entry = edit_line @options[:prefill].to_s, margin_top: 24, width: 1.0, focus: true + @prompt_entry = edit_line @options[:prefill].to_s, margin_top: 24, width: 1.0, focus: true, type: @options[:input_type] == :password ? :password : :text end flow(width: 1.0, height: 0.1, padding: 8) do diff --git a/locales/en.yml b/locales/en.yml new file mode 100644 index 0000000..0ae11c0 --- /dev/null +++ b/locales/en.yml @@ -0,0 +1,46 @@ +en: + app_name: W3D Hub Launcher + interface: + log_in: Log in + register: Register + log_out: Log out + not_logged_in: Not Logged In + profile: Profile + games: Games + server_browser: Server Browser + community: Community + download_manager: Download Manager + play_now: Play Now + single_player: Single Player + import: Import + install: Install + app_settings_tip: W3D Hub Launcher Settings + games: + game_settings: Game Settings + wine_configuration: Wine Configuration + repair_installation: Repair Installation + uninstall_game: Uninstall Game + install_folder: Install Folder + user_data_folder: User Data Folder + view_screenshots: View Screenshots + read_more: Read More + fetching_news: Fetching news... + channel: Channel + server_browser: + join_server: Join Server + game: Game + map: Map + max_players: Max Players + filters: Filters + region: Region + fetching_server_list: Fetching server list... + no_server_selected: No server selected + hostname: Hostname + current_map: Current Map + players: Players + ping: Ping + nickname: Nickname + set_nickname: Set Nickname + set_nickname_message: Set a nickname that will be used when joining a server + enter_password: Enter Password + enter_password_message: This server requires a password diff --git a/w3dhub.rb b/w3dhub.rb index 2d52101..9c3c65b 100644 --- a/w3dhub.rb +++ b/w3dhub.rb @@ -10,9 +10,13 @@ require "fileutils" require "digest" require "rexml" +require "i18n" require "launchy" require "zip" +I18n.load_path << Dir[File.expand_path("locales") + "/*.yml"] +I18n.default_locale = :en + class W3DHub GAME_ROOT_PATH = File.expand_path(".", __dir__) CACHE_PATH = "#{GAME_ROOT_PATH}/data/cache"