ok-oldking / ok-oldking/ok-script
Fix one-time task re-run delay before run when the target window is unfocused
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 739
- Forks
- 208
- Avg merge
- 7h 47m
- Merged PRs (30d)
- 1
Description
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
- On Windows, configure a game with a foreground interaction backend such as
PynputorPyDirect. - Register a one-time task whose
run()starts withensure_in_front()and then performs input. TheFishingOnceTaskinok-how-to-fishis an example. - Start the task once while the game window is active. The task runs successfully.
- Let the client UI or another application take focus.
- 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
Pynputinteraction backend- Reproduced while developing
ok-how-to-fish
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the TaskExecutor boundary and trace the one-time execution path from StartController.start(task), including _mark_task_enabled(), BaseTask.enable(), and the first next_frame() call. Check the follow-up regression test for the expected ordering, and consider the issue complete when one-time tasks prepare the interaction before their first frame while trigger tasks retain their existing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100