artificer/lib/root.zig

137 lines
4.1 KiB
Zig

const std = @import("std");
const Api = @import("artifacts-api");
const Allocator = std.mem.Allocator;
pub const Brain = @import("./brain.zig");
pub const TaskGraph = @import("./task_graph.zig");
const Artificer = @This();
const expiration_margin: u64 = 100 * std.time.ns_per_ms; // 100ms
const server_down_retry_interval = 5; // minutes
server: Api.Server,
characters: std.ArrayList(Brain),
task_graph: TaskGraph,
paused_until: ?i64 = null, // ms
pub fn init(allocator: Allocator, token: []const u8) !Artificer {
var server = try Api.Server.init(allocator);
errdefer server.deinit();
try server.setToken(token);
var characters = std.ArrayList(Brain).init(allocator);
errdefer characters.deinit(); // TODO: Add character deinit
const chars = try server.listMyCharacters();
defer chars.deinit();
for (chars.items) |char| {
try characters.append(try Brain.init(allocator, char.name));
}
return Artificer{
.server = server,
.characters = characters,
.task_graph = TaskGraph.init(allocator)
};
}
pub fn deinit(self: *Artificer) void {
for (self.characters.items) |brain| {
brain.deinit();
}
self.characters.deinit();
self.server.deinit();
}
pub fn step(self: *Artificer) !void {
if (self.paused_until) |paused_until| {
if (std.time.milliTimestamp() < paused_until) {
return;
}
self.paused_until = null;
}
runNextActions(self.characters.items, &self.server) catch |err| switch (err) {
Api.FetchError.ServerUnavailable => {
self.paused_until = std.time.milliTimestamp() + std.time.ms_per_min * server_down_retry_interval;
std.log.warn("Server is down, retrying in {}min", .{ server_down_retry_interval });
return;
},
else => return err
};
for (self.characters.items) |*brain| {
if (brain.task != null) {
try brain.step(&self.server);
continue;
}
const character = self.server.store.getCharacter(brain.name).?;
if (character.task) |taskmaster_task| {
if (taskmaster_task.total > taskmaster_task.progress) {
switch (taskmaster_task.type) {
.monsters => {
const monster_code = self.server.store.getCode(taskmaster_task.target_id).?;
const maps = try self.server.getMaps(.{ .code = monster_code });
defer maps.deinit();
if (maps.items.len > 0) {
const resource_map: Api.Map = maps.items[0];
std.debug.print("fight at {}\n", .{resource_map.position});
brain.task = .{
.fight = .{
.at = resource_map.position,
.until = .{ .quantity = taskmaster_task.total - taskmaster_task.progress },
}
};
}
},
.crafts => {},
.resources => {},
}
}
} else {
brain.task = .{ .accept_task = .{} };
}
}
}
pub fn nextStepAt(self: *Artificer) i64 {
if (self.paused_until) |paused_until| {
return paused_until;
}
return earliestCooldown(self.characters.items, &self.server) orelse 0;
}
fn earliestCooldown(characters: []Brain, api: *Api.Server) ?i64 {
var earliest_cooldown: ?i64 = null;
for (characters) |*brain| {
if (brain.action_queue.items.len == 0) continue;
const cooldown = brain.cooldown(api);
if (earliest_cooldown == null or earliest_cooldown.? > cooldown) {
earliest_cooldown = cooldown;
}
}
return earliest_cooldown;
}
fn runNextActions(characters: []Brain, api: *Api.Server) !void {
for (characters) |*brain| {
if (brain.action_queue.items.len == 0) continue;
const cooldown = brain.cooldown(api);
if (std.time.milliTimestamp() >= cooldown) {
try brain.performNextAction(api);
}
}
}