Skip to content

Fix one-time task re-run delay before run when the target window is unfocused #94

Description

@sinxy-sai

Context

This issue was found while developing and repeatedly debugging the FishingOnceTask in ok-how-to-fish. ok-how-to-fish was created from the ok-script application template and uses ok-script as its underlying task execution framework.

The task uses the documented BaseTask API. Its run() method starts by calling ensure_in_front() before sending keyboard and mouse input. The expected debugging workflow is to run the task, adjust the task code or configuration, then run the same task again.

This is not caused by the application template using the task API incorrectly or by the GUI debug action calling the task incorrectly. The GUI run action calls StartController.start(task), which is the framework's normal entry point for starting a one-time task.

Reproduction

  1. On Windows, configure a game with a foreground interaction backend such as Pynput or PyDirect.
  2. Register a one-time task whose run() starts with ensure_in_front() and then performs input. The FishingOnceTask in ok-how-to-fish is an example.
  3. Start the task once while the game window is active. The task runs successfully.
  4. Let the client UI or another application take focus.
  5. Start the same one-time task again, for example from the GUI task card or the task editor's Run action.

Expected behavior

The target game window is brought to the foreground before the executor requests the first frame, and the task enters task.run() normally on every run.

Actual behavior

The task is queued and selected, but it does not reach its first task-level action immediately. For example, the log does not show the FishingOnceTask state Selecting rod until after the frame wait/timeout.

The current execution order is effectively:

select one-time task
-> request next_frame()
-> task.run()

For foreground interaction backends, should_capture() returns False while the target window is not active. Therefore next_frame() waits before task.run() is reached. The task-level ensure_in_front() cannot prepare the window because it is after the blocked operation. In the current executor, the one-time path passes time_out=4, so this appears as an avoidable delay of up to about four seconds rather than a true infinite wait.

There is another relevant framework path: StartController._mark_task_enabled() directly marks the task enabled and queues it. It does not go through BaseTask.enable(), where interaction.on_run() is otherwise called. This path is used by the GUI task card/editor flow and also by command-line/headless and Web/API task starts.

Root cause

The framework prepares the foreground interaction too late for one-time tasks. A one-time task must be prepared after it is selected but before the first next_frame() call.

The task API usage is valid: ensure_in_front() is a reasonable task-level action, but the executor's precondition that it must obtain a frame first makes that action unable to help in this case.

Trigger tasks should keep their existing behavior because they are expected to observe the current foreground/capture state rather than forcibly foregrounding the target for every trigger check.

Proposed fix

Prepare the interaction backend before the first frame for one-time tasks. The follow-up PR implements this at the TaskExecutor boundary and adds a regression test for the execution order. The preparation is a no-op for interaction backends that do not need foreground activation.

Environment

  • Windows 11
  • ok-script 2.0.0b7
  • Python 3.12
  • Pynput interaction backend
  • Reproduced while developing ok-how-to-fish

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions