Windows Task Scheduler
Back: Documentation home / README
ok-ef provides daily task external commands and built-in scheduled-task support.
For more freedom, you can use the Windows Task Scheduler.
1. Create pre-hook and post-hook files from the templates
The following 终末地.pre.ps1 cleans up old logs before execution and optionally starts the game early to wait for a hot update. Modify the paths and save; the examples are compatible with the built-in Windows PowerShell 5.1:
$ErrorActionPreference = "Stop"
$efDir = "C:\Program Files\Hypergryph Launcher\games\Endfield Game"
$okefDir = "C:\ok-ef"
$workingDir = Join-Path $okefDir "data\apps\ok-ef\working"
$hotUpdateMinutes = 5 # set to 0 to skip early startup
if (-not (Test-Path -LiteralPath $workingDir -PathType Container)) {
throw "ok-ef working directory does not exist: $workingDir"
}
$logDir = Join-Path $workingDir "logs"
if (Test-Path -LiteralPath $logDir -PathType Container) {
$logs = @(Get-ChildItem -LiteralPath $logDir -File)
if ($logs.Count -gt 15) {
$cutoff = (Get-Date).AddDays(-30)
$logs | Where-Object { $_.CreationTime -lt $cutoff } | Remove-Item -Force
}
$currentLog = Join-Path $logDir "ok-script.log"
if (Test-Path -LiteralPath $currentLog -PathType Leaf) {
$timestamp = (Get-Item -LiteralPath $currentLog).CreationTime.ToString("yyyy-MM-dd.HH-mm-ss")
Move-Item -LiteralPath $currentLog -Destination "$currentLog.$timestamp.log" -Force
}
}
if ($hotUpdateMinutes -gt 0) {
$gameExe = Join-Path $efDir "Endfield.exe"
if (-not (Test-Path -LiteralPath $gameExe -PathType Leaf)) {
throw "Game executable does not exist: $gameExe"
}
Start-Process -FilePath $gameExe -WorkingDirectory $efDir
Start-Sleep -Seconds ($hotUpdateMinutes * 60)
Get-Process -Name "Endfield" -ErrorAction SilentlyContinue | Stop-Process -Force
}
# Add your custom code here (e.g. message notification)
The following 终末地.post.ps1 waits for the task to exit, ends the relevant processes on timeout, and archives logs and screenshots. $maxMinutes includes the pre-script's hot-update time:
$okefDir = "C:\ok-ef"
$workingDir = Join-Path $okefDir "data\apps\ok-ef\working"
$maxMinutes = 44
$hotUpdateMinutes = 5
$deadline = (Get-Date).AddMinutes([Math]::Max(1, $maxMinutes - $hotUpdateMinutes))
# `cmd /c start` is non-blocking; leave startup time for ok-ef and the game process.
Start-Sleep -Seconds 5
do {
$gameProcesses = @(Get-Process -Name "Endfield" -ErrorAction SilentlyContinue)
$okefProcesses = @(Get-Process -Name "ok-ef" -ErrorAction SilentlyContinue)
if ($gameProcesses.Count -eq 0 -and $okefProcesses.Count -eq 0) { break }
Start-Sleep -Seconds 1
} while ((Get-Date) -lt $deadline)
$timedOut = $gameProcesses.Count -gt 0 -or $okefProcesses.Count -gt 0
if ($timedOut) {
$gameProcesses | Stop-Process -Force
$okefProcesses | Stop-Process -Force
$message = "Execution timed out; terminated the still-running game or ok-ef processes."
} else {
$message = "Execution finished."
}
$logPath = Join-Path $workingDir "logs\ok-script.log"
$version = ""
if (Test-Path -LiteralPath $logPath -PathType Leaf) {
$logLines = Get-Content -LiteralPath $logPath -Encoding UTF8
$summaryPatterns = @("已完成的任务列表", "已失败的任务列表", "已跳过的任务列表", "未处理的任务列表", "当前失败的任务")
foreach ($pattern in $summaryPatterns) {
$line = $logLines | Select-String -Pattern $pattern | Select-Object -Last 1
if ($null -ne $line) { $message += "`n`n$line" }
}
$versionLine = $logLines | Select-String -Pattern "app_version:([^,]+)" | Select-Object -Last 1
if ($null -ne $versionLine -and $versionLine -match "app_version:([^,]+)") {
$version = " " + $matches[1]
}
$timestamp = (Get-Item -LiteralPath $logPath).CreationTime.ToString("yyyy-MM-dd.HH-mm-ss")
Move-Item -LiteralPath $logPath -Destination "$logPath.$timestamp.log" -Force
}
$screenshotDir = Join-Path $workingDir "screenshots"
$backupDir = Join-Path $workingDir "screenshots.backup"
if (Test-Path -LiteralPath $screenshotDir -PathType Container) {
$screenshots = @(Get-ChildItem -LiteralPath $screenshotDir -File -ErrorAction SilentlyContinue)
if ($screenshots.Count -gt 0) {
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
$screenshots | Move-Item -Destination $backupDir -Force
}
}
# Add your custom code here (e.g. send a notification using $message and $version)
2. Create and import the scheduled-task file
The scheduled-task template is as follows:
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<SecurityDescriptor></SecurityDescriptor>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<StartBoundary>2026-01-01T04:00:00</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "终末地.pre.ps1"</Arguments>
<WorkingDirectory>PLACEHOLDER_PATH_TO_PRE_SCRIPT_DIRECTORY</WorkingDirectory>
</Exec>
<Exec>
<Command>cmd</Command>
<Arguments>/c start "" "ok-ef.exe" -t 1 -e</Arguments>
<WorkingDirectory>PLACEHOLDER_PATH_TO_OK_EF_APP_DIRECTORY</WorkingDirectory>
</Exec>
<Exec>
<Command>powershell.exe</Command>
<Arguments>-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "终末地.post.ps1"</Arguments>
<WorkingDirectory>PLACEHOLDER_PATH_TO_POST_SCRIPT_DIRECTORY</WorkingDirectory>
</Exec>
</Actions>
</Task>
Copy the content into a new xml file, make the following changes, and save:
- Change
PLACEHOLDER_PATH_TO_OK_EF_APP_DIRECTORYto the absolute path of the ok-ef app. - Change
PLACEHOLDER_PATH_TO_PRE_SCRIPT_DIRECTORYto the location of the终末地.pre.ps1file. - Change
PLACEHOLDER_PATH_TO_POST_SCRIPT_DIRECTORYto the location of the终末地.post.ps1file.
-t 1 runs the 1st item of onetime_tasks in src/config.py, i.e. Daily Tasks. The number changes when the list order changes; verify the current registration order before changing to another number. -e exits ok-ef after the task finishes.
Index auto-correction at startup: tasks created through ok-ef's built-in 「Scheduled Tasks」 feature (the 「Scheduled Tasks」 tab in the GUI) store their
-targument inconfigs/schedule_tasks_cache.jsonand in the Windows scheduled task. On every app startup, it reads this app's tasks (\ok-ef\) from the cache, compares each name against the currentonetime_tasksorder, automatically rewrites-tto the correct current index, and syncs the Windows scheduled task. So after reorderingonetime_tasksyou don't need to fix the number manually — the next startup corrects it automatically; if this run was triggered by the scheduled task, it also corrects before running, ensuring the right task runs. Other ok-* apps' tasks (e.g.\ok-gf2\) are not modified.
Use Win + R and run taskschd.msc to open the Task Scheduler. Click Action > Import Task to add the xml file above. Change the Name and click OK.
(After creation a scheduled task cannot be renamed or moved; to modify it, right-click the task, delete it, and re-import. The xml file above is no longer needed after import and can be deleted.)
The scheduled task is now created. At 4:00 AM every day, the computer automatically runs ok-ef.
3. Modify the scheduled task
Basic usage
Use Win + R and run taskschd.msc to open the Task Scheduler, then right-click the scheduled task.
- Click
Runto run the scheduled task manually. - Click
Stopto stop a running scheduled task. Note that ok-ef runs non-blocking and must be closed manually.