Module: Lich::Main::ArgvOptions::SideEffects

Defined in:
documented/main/argv_options.rb

Overview

Apply side effects: dark mode, hosts-dir, bind-address, detachable-client

Class Method Summary collapse

Class Method Details

.announce(level, message) ⇒ Object

Surface a message on both channels every bind handler uses.



189
190
191
192
# File 'documented/main/argv_options.rb', line 189

def self.announce(level, message)
  $stdout.puts "#{level}: #{message}"
  Lich.log "#{level}: #{message}"
end

.die(message) ⇒ Object

Fatal argv problem: tell the user everywhere, then stop.



195
196
197
198
# File 'documented/main/argv_options.rb', line 195

def self.die(message)
  announce('error', message)
  exit 1
end

.execute(argv_options) ⇒ Hash

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

Called after argument parsing; modifies global state ($frontend, Win32 ShellExecute) and may exit the process

Applies side effects implied by parsed argv_options: theme, host directories, bind addresses, detachable client configuration, and SAL file launching.

Resolves hostnames to concrete IP addresses (via BindAddressOption and BindHostResolver), handles --hosts-dir path validation, configures detachable client binding, and launches .sal files with the appropriate system integration (Win32 ShellExecute, Wine, or system). Fatal errors cause immediate exit(1).

Parameters:

Returns:

  • (Hash) —

    the same argv_options hash, modified in-place with resolved addresses, host directories, and client configuration



179
180
181
182
183
184
185
186
# File 'documented/main/argv_options.rb', line 179

def self.execute(argv_options)
  StartupTheme.apply(argv_options)
  handle_hosts_dir(argv_options)
  handle_bind_address(argv_options)
  handle_detachable_client(argv_options)
  handle_sal_launch(argv_options)
  argv_options
end

.handle_bind_address(argv_options) ⇒ Object

--bind-address shares the keyword vocabulary of --detachable-client hosts (tailscale/lan/any). Resolve it once, up front, so the frontend listener, the --game proxy, and a detachable client that inherits it all bind the same concrete address -- and so the exposure warning appears exactly once.



205
206
207
208
209
210
211
212
# File 'documented/main/argv_options.rb', line 205

def self.handle_bind_address(argv_options)
  result = BindAddressOption.apply(argv_options[:bind_address])
  die(result.error) if result.error
  return unless result.host

  argv_options[:bind_address] = result.host
  announce('warning', result.warning) if result.warning
end

.handle_detachable_client(argv_options) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Resolves --detachable-client address/port and applies --bind-address as default.

Parses --detachable-client= format (host and/or port), resolves the host keyword (tailscale/lan/any) to a concrete IP via BindHostResolver, and defaults to --bind-address or 127.0.0.1. Port is optional. Raises DetachableClientTarget::ParseError or BindHostResolver::Error on invalid input, which Lich::Main::ArgvOptions::SideEffects catches and exits with an error message.

Parameters:

  • argv_options (Hash) —

    the options hash to update with :detachable_client_host and :detachable_client_port keys

Raises:



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'documented/main/argv_options.rb', line 252

def self.handle_detachable_client(argv_options)
  argv_options[:detachable_client_host] = argv_options[:bind_address] || '127.0.0.1'
  argv_options[:detachable_client_port] = nil
  arg = ARGV.find { |a| a.start_with?('--detachable-client=') }
  return unless arg

  begin
    target = DetachableClientTarget.parse(arg.split('=', 2).last)
    if target.host
      resolution = Lich::Common::BindHostResolver.resolve(target.host)
      argv_options[:detachable_client_host] = resolution.host
      announce('warning', resolution.warning) if resolution.warning
    end
    # (The port-only form inherits --bind-address, which
    # handle_bind_address already resolved and warned about; the
    # loopback default warrants no warning.)
    argv_options[:detachable_client_port] = target.port
  rescue DetachableClientTarget::ParseError, Lich::Common::BindHostResolver::Error => e
    die(e.message)
  end
end

.handle_hosts_dir(argv_options) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Extracts and validates the --hosts-dir option, storing the normalized path in argv_options.

Removes the argument from ARGV, converts backslashes to forward slashes, and ensures the directory ends with '/'. Logs a warning to stdout if the directory does not exist, but does not exit or raise.

Parameters:

  • argv_options (Hash) —

    the options hash to update with :hosts_dir key



224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'documented/main/argv_options.rb', line 224

def self.handle_hosts_dir(argv_options)
  if (arg = ARGV.find { |a| a =~ /^--hosts-dir=(.+)$/i })
    hosts_dir = arg[/^--hosts-dir=(.+)$/i, 1]
    ARGV.delete(arg)
    if hosts_dir && File.exist?(hosts_dir)
      hosts_dir = hosts_dir.tr('\\', '/')
      hosts_dir += '/' unless hosts_dir[-1..-1] == '/'
      argv_options[:hosts_dir] = hosts_dir
    else
      $stdout.puts "warning: given hosts directory does not exist: #{hosts_dir}"
    end
  end
end

.handle_sal_launch(argv_options) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

Exits the entire process; used only for SAL-file launch mode

This method returns an undefined value.

Validates and launches a .sal launch file using system integration (Win32, Wine, or direct system call), then exits.

Checks file existence, logs the path, and for SGE.sal specifically, retrieves the Simutronics launcher command and executes it with elevated privileges (runas on Win32 non-XP, open otherwise), substituting %1 with the file path. On non-SGE files or platforms without Win32, delegates to Wine or system(). Always exits when invoked (does not return).

Parameters:

  • argv_options (Hash) —

    the options hash (read only; checks :sal key)



287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# File 'documented/main/argv_options.rb', line 287

def self.handle_sal_launch(argv_options)
  return unless argv_options[:sal]

  unless File.exist?(argv_options[:sal])
    Lich.log "error: launch file does not exist: #{argv_options[:sal]}"
    Lich.msgbox "error: launch file does not exist: #{argv_options[:sal]}"
    exit
  end
  Lich.log "info: launch file: #{argv_options[:sal]}"

  if argv_options[:sal] =~ /SGE\.sal/i
    unless (launcher_cmd = Lich.get_simu_launcher)
      $stdout.puts 'error: failed to find the Simutronics launcher'
      Lich.log 'error: failed to find the Simutronics launcher'
      exit
    end
    launcher_cmd.sub!('%1', argv_options[:sal])
    Lich.log "info: launcher_cmd: #{launcher_cmd}"
    if defined?(Win32) && launcher_cmd =~ /^"(.*?)"\s*(.*)$/
      dir_file = $1
      param = $2
      dir = dir_file.slice(/^.*[\\\/]/)
      file = dir_file.sub(/^.*[\\\/]/, '')
      operation = (Win32.isXP? ? 'open' : 'runas')
      r = Win32.ShellExecute(lpOperation: operation, lpFile: file, lpDirectory: dir, lpParameters: param)
      Lich.log "error: Win32.ShellExecute returned #{r}; Win32.GetLastError: #{Win32.GetLastError}" if r < 33
    elsif defined?(Wine)
      system("#{Wine::BIN} #{launcher_cmd}")
    else
      system(launcher_cmd)
    end
    exit
  end
end