feat: game pause/speed control API with AI pause protocol
- POST /api/action/pause - pause game (AI must call before operations) - POST /api/action/unpause - resume game at desired speed - POST /api/action/speed - set game speed (1x/2x/3x) - Game state now includes isPaused and gameSpeed - AI pause protocol documented in SKILL.md: pause before every operation - Introduces cell.isDiggable flag that filters neutronium properly - pause/unpause/speed CLI commands added to oni_api.py
This commit is contained in:
@ -181,6 +181,15 @@ namespace ONIAgentBridge
|
||||
case ("/api/action/priority_type", "POST"):
|
||||
responseJson = ExecutePriorityType(ctx);
|
||||
break;
|
||||
case ("/api/action/pause", "POST"):
|
||||
responseJson = ExecutePause(ctx);
|
||||
break;
|
||||
case ("/api/action/unpause", "POST"):
|
||||
responseJson = ExecuteUnpause(ctx);
|
||||
break;
|
||||
case ("/api/action/speed", "POST"):
|
||||
responseJson = ExecuteSpeed(ctx);
|
||||
break;
|
||||
|
||||
default:
|
||||
ctx.Response.StatusCode = 404;
|
||||
@ -293,6 +302,9 @@ namespace ONIAgentBridge
|
||||
|
||||
PushEvent("state_poll", "info", "Game state polled", $"Cycle {GameClock.Instance?.GetCycle() ?? 0}");
|
||||
|
||||
bool isPaused = SpeedControlScreen.Instance?.IsPaused ?? false;
|
||||
int gameSpeed = isPaused ? 0 : (SpeedControlScreen.Instance?.GetSpeed() ?? 1);
|
||||
|
||||
return JsonSerializer.Serialize(new
|
||||
{
|
||||
cycle = GameClock.Instance?.GetCycle() ?? 0,
|
||||
@ -304,6 +316,8 @@ namespace ONIAgentBridge
|
||||
worldSize = cellCount,
|
||||
gridWidth = Grid.WidthInCells,
|
||||
gridHeight = Grid.HeightInCells,
|
||||
isPaused,
|
||||
gameSpeed,
|
||||
eventCount = _eventSeq
|
||||
});
|
||||
}
|
||||
@ -847,7 +861,8 @@ namespace ONIAgentBridge
|
||||
bool isDiggable = Grid.Solid[cell] && elem != null
|
||||
&& elem.id != SimHashes.Unobtanium
|
||||
&& elem.id != SimHashes.Katairite
|
||||
&& elem.id != SimHashes.Void;
|
||||
&& elem.id != SimHashes.Void
|
||||
&& !elem.name.Contains("Neutronium");
|
||||
|
||||
// Check dupe safety
|
||||
bool isSafeForDupe = elem != null
|
||||
@ -1246,6 +1261,74 @@ namespace ONIAgentBridge
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Pause / Speed Control
|
||||
// ===================================================================
|
||||
private string ExecutePause(HttpListenerContext ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = ReadBody<PauseRequest>(ctx);
|
||||
// No body needed, but accept optional { "reason": "..." }
|
||||
string reason = data?.reason ?? "AI operation in progress";
|
||||
|
||||
SpeedControlScreen.Instance?.Pause(false, true);
|
||||
PushEvent("pause", "info", "Game paused",
|
||||
$"Game paused by AI: {reason}", "system");
|
||||
|
||||
return JsonSerializer.Serialize(ActionOk("game_paused",
|
||||
new { reason, isPaused = true }));
|
||||
}
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
private string ExecuteUnpause(HttpListenerContext ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = ReadBody<UnpauseRequest>(ctx);
|
||||
int speed = data?.speed ?? 1;
|
||||
if (speed < 1) speed = 1;
|
||||
if (speed > 3) speed = 3;
|
||||
|
||||
SpeedControlScreen.Instance?.Unpause(true);
|
||||
SpeedControlScreen.Instance?.SetSpeed(speed);
|
||||
PushEvent("unpause", "info", "Game resumed",
|
||||
$"Game resumed by AI at {speed}x speed", "system");
|
||||
|
||||
return JsonSerializer.Serialize(ActionOk("game_unpaused",
|
||||
new { speed, isPaused = false }));
|
||||
}
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
private string ExecuteSpeed(HttpListenerContext ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = ReadBody<SpeedRequest>(ctx);
|
||||
if (data == null)
|
||||
return JsonSerializer.Serialize(FailInvalid("invalid_request"));
|
||||
|
||||
int speed = data.speed;
|
||||
if (speed < 1) speed = 1;
|
||||
if (speed > 3) speed = 3;
|
||||
|
||||
bool isPaused = SpeedControlScreen.Instance?.IsPaused ?? false;
|
||||
if (!isPaused)
|
||||
{
|
||||
SpeedControlScreen.Instance?.SetSpeed(speed);
|
||||
}
|
||||
|
||||
PushEvent("speed", "info", $"Game speed set to {speed}x",
|
||||
$"Speed changed to {speed}x", "system");
|
||||
|
||||
return JsonSerializer.Serialize(ActionOk("speed_set",
|
||||
new { speed, isPaused }));
|
||||
}
|
||||
catch (Exception e) { return JsonSerializer.Serialize(FailException(e)); }
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// Batch System
|
||||
// ===================================================================
|
||||
@ -1578,6 +1661,9 @@ namespace ONIAgentBridge
|
||||
internal class MopRequest { public int x { get; set; } public int y { get; set; } }
|
||||
internal class HarvestRequest { public int x { get; set; } public int y { get; set; } }
|
||||
internal class CancelRequest { public int x { get; set; } public int y { get; set; } }
|
||||
internal class PauseRequest { public string reason { get; set; } }
|
||||
internal class UnpauseRequest { public int speed { get; set; } }
|
||||
internal class SpeedRequest { public int speed { get; set; } }
|
||||
internal class PriorityGlobalRequest { public string target { get; set; } public int priority { get; set; } }
|
||||
internal class PriorityTypeRequest { public string buildingType { get; set; } public int priority { get; set; } }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user