Module: Lich::Common::OrderlyShutdown

Defined in:
documented/common/orderly_shutdown.rb

Overview

Runs the explicit user-requested shutdown sequence while game I/O is alive.

This runner is separate from ShutdownCoordinator: the coordinator records intent and progress, while this module performs teardown steps and updates the stored result. Connection-loss cleanup can add its own runner without overloading the state object.

Defined Under Namespace

Classes: Result, ServerExitTimeout

Constant Summary collapse

SERVER_EXIT_TIMEOUT_SECONDS =
10

Class Method Summary collapse

Class Method Details

.request_user_exit(source:, current_script: nil, coordinator: ShutdownCoordinator, scripts_provider: nil, script_drain: ShutdownScriptDrain, vars: Vars, game: Game, active_sessions_lifecycle: nil, slow_threshold: 1.5, server_exit_command: nil, server_exit_timeout: SERVER_EXIT_TIMEOUT_SECONDS) ⇒ Result

Requests the same orderly shutdown path used by explicit frontend exits.

Script callers can pass themselves as current_script so the drain stops other scripts without killing the script that is initiating shutdown.

Parameters:

  • source (#to_s)

    source recorded in shutdown logs

  • current_script (Object, nil) (defaults to: nil)

    script to exclude from shutdown drain

  • server_exit_command (String, nil) (defaults to: nil)

    validated command sent before close

  • server_exit_timeout (Numeric) (defaults to: SERVER_EXIT_TIMEOUT_SECONDS)

    seconds to wait for remote EOF

Returns:

  • (Result)

    stored orderly-shutdown result



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'documented/common/orderly_shutdown.rb', line 52

def request_user_exit(source:, current_script: nil, coordinator: ShutdownCoordinator, scripts_provider: nil, script_drain: ShutdownScriptDrain, vars: Vars, game: Game, active_sessions_lifecycle: nil, slow_threshold: 1.5, server_exit_command: nil, server_exit_timeout: SERVER_EXIT_TIMEOUT_SECONDS)
  validate_server_exit!(server_exit_command, server_exit_timeout)
  ShutdownLog.begin_user_exit_summary!
  coordinator.request(reason: :user_exit, source: source)

  initial_scripts =
    if scripts_provider
      scripts_provider.call
    else
      Script.begin_shutdown
    end
  scripts_provider ||= proc { Script.progress_shutdown }
  remaining_scripts = proc { scripts_provider.call.reject { |script| script.equal?(current_script) } }

  result = run(
    coordinator: coordinator,
    initial_scripts: initial_scripts.reject { |script| script.equal?(current_script) },
    remaining_scripts: remaining_scripts,
    script_drain: script_drain,
    vars: vars,
    game: game,
    active_sessions_lifecycle: active_sessions_lifecycle,
    slow_threshold: slow_threshold,
    server_exit_command: server_exit_command,
    server_exit_timeout: server_exit_timeout
  )
  if current_script && initial_scripts.include?(current_script)
    result.scripts_drained = false
    result.completed = false
  end
  result
end

.run(coordinator:, initial_scripts:, remaining_scripts:, script_drain:, vars:, game:, active_sessions_lifecycle: nil, slow_threshold: 1.5, server_exit_command: nil, server_exit_timeout: SERVER_EXIT_TIMEOUT_SECONDS) ⇒ Result

Executes orderly user shutdown once.

The sequence intentionally runs script hooks and local state save before closing the game connection. If another frontend thread already started the same sequence, this method returns that existing result.

Parameters:

  • coordinator (Lich::Common::ShutdownCoordinator)

    state/intent coordinator

  • initial_scripts (Array<#kill,#name>)

    scripts present at shutdown start

  • remaining_scripts (#call)

    returns scripts still registered

  • script_drain (#run)

    script drain service

  • vars (#save)

    local script settings persistence

  • game (#close)

    game connection facade

  • active_sessions_lifecycle (#update_connected, nil) (defaults to: nil)

    optional session registry

  • slow_threshold (Float) (defaults to: 1.5)

    seconds before script drain reports slow scripts

  • server_exit_command (String, nil) (defaults to: nil)

    validated command sent before close

  • server_exit_timeout (Numeric) (defaults to: SERVER_EXIT_TIMEOUT_SECONDS)

    seconds to wait for remote EOF

Returns:

  • (Result)

    stored orderly-shutdown result

Raises:

  • (ArgumentError)

    when caller input is invalid or reason is not :user_exit



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'documented/common/orderly_shutdown.rb', line 103

def run(coordinator:, initial_scripts:, remaining_scripts:, script_drain:, vars:, game:, active_sessions_lifecycle: nil, slow_threshold: 1.5, server_exit_command: nil, server_exit_timeout: SERVER_EXIT_TIMEOUT_SECONDS)
  validate!(coordinator: coordinator, remaining_scripts: remaining_scripts, script_drain: script_drain, vars: vars, game: game, server_exit_command: server_exit_command)
  validate_server_exit!(server_exit_command, server_exit_timeout)
  raise ArgumentError, "orderly user exit requires reason=:user_exit" unless coordinator.orderly_user_exit?

  result = Result.new(
    completed: false,
    failures: [],
    scripts_drained: false,
    vars_saved: false,
    game_closed: false
  )

  stored_result = coordinator.begin_orderly_shutdown(result)
  return stored_result unless stored_result.equal?(result)

  log_info("orderly user shutdown starting")
  update_active_sessions(result, active_sessions_lifecycle)
  drain_scripts(result, initial_scripts, remaining_scripts, script_drain, slow_threshold)
  save_vars(result, vars)
  request_server_exit(result, game, server_exit_command, server_exit_timeout) if server_exit_command
  close_game(result, game)
  finish(result)
end