Module: Lich::Common::FrontendLauncher
- Defined in:
- documented/common/frontend_launcher.rb
Overview
Converts catalog launcher metadata into either a launch-file command template or a shell-free environment/argv process plan.
Defined Under Namespace
Classes: Error, SpawnPlan, UnavailableError, UnsupportedError
Constant Summary collapse
- PROCESS_LOCAL_LAUNCH_FIELDS =
Launch data field names that are local to the Lich process and must be removed before the data is serialized for a native frontend via Simutronics' .sal contract.
%w[FRONTEND CUSTOMLAUNCHARGV].freeze
- CONNECTION_PLACEHOLDER_PATTERN =
Regular expression matching Lich's connection-configuration placeholder tokens. These tokens are replaced with the actual connection values after the local proxy listener is opened.
/(%host%|%port%|%key%)/.freeze
Class Method Summary collapse
-
.command(frontend_id, platform_key: Frontend.platform_key, locator: FrontendLocator, simu_launcher: -> { Lich.get_simu_launcher }) ⇒ String+
Builds the platform command template for a registered frontend.
-
.launchable?(frontend_id, locator: FrontendLocator, frontend: Frontend, refresh: false) ⇒ Boolean
Returns whether a frontend has enough machine-local configuration to launch.
-
.native_session_data(launch_data) ⇒ Array<String>
Removes Lich-only control fields before launch data is serialized for a native frontend.
-
.render_connection(command, host:, port:, key:) ⇒ String+
Resolves every connection placeholder after Lich opens the local listener used by a configured frontend.
-
.saga_managed_login_plan(account:, character:, game_code:, platform_key: Frontend.platform_key, locator: FrontendLocator, refresh: true) ⇒ SpawnPlan
Builds a shell-free Saga launch plan that asks Saga to authenticate the named character and start its own Via-Lich session.
-
.spawn_plan(frontend_id, host:, port:, key:, platform_key: Frontend.platform_key, locator: FrontendLocator, refresh: true) ⇒ SpawnPlan
Builds a shell-free process environment and argv for an environment launcher adapter.
-
.with_additional_arguments(base_command, frontend, platform_key: Frontend.platform_key) ⇒ String+
Appends user-configured arguments to a command-template launcher.
Class Method Details
.command(frontend_id, platform_key: Frontend.platform_key, locator: FrontendLocator, simu_launcher: -> { Lich.get_simu_launcher }) ⇒ String+
Builds the platform command template for a registered frontend.
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'documented/common/frontend_launcher.rb', line 83 def command( frontend_id, platform_key: Frontend.platform_key, locator: FrontendLocator, simu_launcher: -> { Lich.get_simu_launcher } ) definition = Frontend.definition_for(frontend_id) base_command = case definition.dig(:metadata, :launcher_adapter) when :environment raise UnsupportedError, "#{definition[:id]} requires a structured spawn plan" when :avalon avalon_command(definition, platform_key, locator) when :simutronics simu_launcher.call || raise(UnavailableError, 'Simutronics launcher was not found') when :custom definition.dig(:metadata, :launch_command) else raise UnsupportedError, "no launcher adapter for #{definition[:id]}" end if base_command.to_s.strip.empty? raise UnavailableError, "#{Frontend.display_name(definition[:id])} has no launch command" end with_additional_arguments(base_command, definition, platform_key: platform_key) end |
.launchable?(frontend_id, locator: FrontendLocator, frontend: Frontend, refresh: false) ⇒ Boolean
Returns whether a frontend has enough machine-local configuration to launch. Custom adapters are backed by their persisted command; native adapters continue to use executable discovery.
62 63 64 65 66 67 68 69 70 71 |
# File 'documented/common/frontend_launcher.rb', line 62 def launchable?(frontend_id, locator: FrontendLocator, frontend: Frontend, refresh: false) definition = frontend.definition_for(frontend_id) if definition.dig(:metadata, :launcher_adapter) == :custom return !definition.dig(:metadata, :launch_command).to_s.strip.empty? end locator.launchable?(definition[:id], refresh: refresh) rescue ArgumentError false end |
.native_session_data(launch_data) ⇒ Array<String>
Removes Lich-only control fields before launch data is serialized for a native frontend. The selected frontend identity remains available to the current Lich process but is not part of Simutronics' .sal contract.
138 139 140 141 142 143 |
# File 'documented/common/frontend_launcher.rb', line 138 def native_session_data(launch_data) Array(launch_data).reject do |line| key = line.to_s.split('=', 2).first.to_s.upcase PROCESS_LOCAL_LAUNCH_FIELDS.include?(key) end end |
.render_connection(command, host:, port:, key:) ⇒ String+
Resolves every connection placeholder after Lich opens the local listener used by a configured frontend.
153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'documented/common/frontend_launcher.rb', line 153 def render_connection(command, host:, port:, key:) if command.is_a?(Array) return command.map { |argument| render_connection(argument, host: host, port: port, key: key) } end { '%host%' => host, '%port%' => port, '%key%' => key }.reduce(command.to_s) do |rendered, (placeholder, value)| rendered.gsub(placeholder, value.to_s) end end |
.saga_managed_login_plan(account:, character:, game_code:, platform_key: Frontend.platform_key, locator: FrontendLocator, refresh: true) ⇒ SpawnPlan
Builds a shell-free Saga launch plan that asks Saga to authenticate
the named character and start its own Via-Lich session. Unlike
spawn_plan, this contract does not create a Lich proxy listener or
pass a game key; Saga owns authentication and Lich process startup.
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
# File 'documented/common/frontend_launcher.rb', line 240 def saga_managed_login_plan( account:, character:, game_code:, platform_key: Frontend.platform_key, locator: FrontendLocator, refresh: true ) identifiers = { account: account, character: character, game_code: game_code } identifiers.each do |name, value| raise ArgumentError, "#{name} must not be empty" if value.to_s.strip.empty? end definition = Frontend.definition_for('saga') unless definition.dig(:metadata, :launcher_adapter) == :environment raise UnsupportedError, 'no environment launcher for saga' end platform_key = Frontend.validate_platform_key!(platform_key) plan = definition.dig(:metadata, :launch_plans, platform_key) raise UnsupportedError, "no #{platform_key} launcher for saga" unless plan command, arguments = resolve_plan_launch( plan, definition, platform_key, locator, refresh: refresh ) SpawnPlan.new( environment: { # Saga 0.8.5 implements this currently undocumented startup contract. 'SAGA_AUTO_LOGIN' => "#{character.to_s.strip}@#{game_code.to_s.strip.upcase}", 'SAGA_AUTO_LOGIN_ACCOUNT' => account.to_s.strip, 'SAGA_AUTO_LOGIN_MODE' => 'lich' }, argv: [command, *arguments] ) end |
.spawn_plan(frontend_id, host:, port:, key:, platform_key: Frontend.platform_key, locator: FrontendLocator, refresh: true) ⇒ SpawnPlan
Builds a shell-free process environment and argv for an environment launcher adapter. Catalog launch_plans are keyed by platform and contain :command, :arguments, and :environment. A command may be a literal executable or :resolved_executable for locator discovery; %host%, %port%, and %key% tokens are replaced in environment values. Connection secrets remain out of process arguments.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'documented/common/frontend_launcher.rb', line 184 def spawn_plan( frontend_id, host:, port:, key:, platform_key: Frontend.platform_key, locator: FrontendLocator, refresh: true ) definition = Frontend.definition_for(frontend_id) unless definition.dig(:metadata, :launcher_adapter) == :environment raise UnsupportedError, "no environment launcher for #{definition[:id]}" end replacements = { '%host%' => host, '%port%' => port, '%key%' => key } replacements.each do |token, value| raise ArgumentError, "#{token.delete('%')} must not be empty" if value.to_s.empty? end platform_key = Frontend.validate_platform_key!(platform_key) plan = definition.dig(:metadata, :launch_plans, platform_key) unless plan raise UnsupportedError, "no #{platform_key} launcher for #{definition[:id]}" end environment = plan.fetch(:environment).transform_values do |value| replacements.reduce(value.to_s) { |resolved, (token, replacement)| resolved.gsub(token, replacement.to_s) } end command, arguments = resolve_plan_launch( plan, definition, platform_key, locator, refresh: refresh ) SpawnPlan.new( environment: environment, argv: [command, *arguments] ) end |
.with_additional_arguments(base_command, frontend, platform_key: Frontend.platform_key) ⇒ String+
Appends user-configured arguments to a command-template launcher. Arguments are escaped independently while Lich's connection placeholders remain available for the launch handoff to replace.
119 120 121 122 123 124 125 126 127 128 129 |
# File 'documented/common/frontend_launcher.rb', line 119 def with_additional_arguments(base_command, frontend, platform_key: Frontend.platform_key) definition = frontend.is_a?(Hash) ? frontend : Frontend.definition_for(frontend) arguments = Array(definition.dig(:metadata, :additional_arguments)) return [*base_command, *arguments] if base_command.is_a?(Array) return base_command if arguments.empty? return [*WindowsCommandLine.split(base_command), *arguments] if platform_key == :windows escaped_arguments = arguments.map { |argument| escape_argument(argument) } argument_separator = definition.dig(:metadata, :launcher_adapter) == :avalon ? ' --args ' : ' ' "#{base_command}#{argument_separator}#{escaped_arguments.join(' ')}" end |