add prefetching of static data
This commit is contained in:
parent
ee563e6ba6
commit
7d572e7064
25
src/api/enum_string_utils.zig
Normal file
25
src/api/enum_string_utils.zig
Normal file
@ -0,0 +1,25 @@
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
|
||||
pub fn EnumStringUtils(TargetEnum: anytype, str_to_tag_mapping: anytype) type {
|
||||
if (str_to_tag_mapping.len != @typeInfo(TargetEnum).Enum.fields.len) {
|
||||
@compileLog("Mapping is not exhaustive");
|
||||
}
|
||||
|
||||
const EnumMapping = std.ComptimeStringMap(TargetEnum, str_to_tag_mapping);
|
||||
|
||||
return struct {
|
||||
pub fn fromString(str: []const u8) ?TargetEnum {
|
||||
return EnumMapping.get(str);
|
||||
}
|
||||
|
||||
pub fn toString(value: TargetEnum) []const u8 {
|
||||
inline for (str_to_tag_mapping) |mapping| {
|
||||
if (mapping[1] == value) {
|
||||
return mapping[0];
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -98,10 +98,7 @@ comptime {
|
||||
assert(@typeInfo(QueuedAction).Union.fields.len == @typeInfo(QueuedActionResult).Union.fields.len);
|
||||
}
|
||||
|
||||
name: []const u8,
|
||||
routine: union (enum) {
|
||||
idle,
|
||||
|
||||
pub const CharacterTask = union(enum) {
|
||||
fight: struct {
|
||||
at: Position,
|
||||
target: Server.ItemIdQuantity,
|
||||
@ -113,16 +110,33 @@ routine: union (enum) {
|
||||
progress: u64 = 0,
|
||||
},
|
||||
craft: struct {
|
||||
at: Position,
|
||||
target: Server.ItemIdQuantity,
|
||||
progress: u64 = 0,
|
||||
},
|
||||
},
|
||||
|
||||
pub fn isComplete(self: CharacterTask) bool {
|
||||
return switch (self) {
|
||||
.fight => |args| {
|
||||
return args.progress >= args.target.quantity;
|
||||
},
|
||||
.gather => |args| {
|
||||
return args.progress >= args.target.quantity;
|
||||
},
|
||||
.craft => |args| {
|
||||
return args.progress >= args.target.quantity;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
name: []const u8,
|
||||
action_queue: std.ArrayList(QueuedAction),
|
||||
task: ?CharacterTask = null,
|
||||
|
||||
pub fn init(allocator: Allocator, name: []const u8) !CharacterBrain {
|
||||
return CharacterBrain{
|
||||
.name = try allocator.dupe(u8, name),
|
||||
.routine = .idle,
|
||||
.action_queue = std.ArrayList(QueuedAction).init(allocator),
|
||||
};
|
||||
}
|
||||
@ -375,9 +389,9 @@ pub fn performNextAction(self: *CharacterBrain, api: *Server) !void {
|
||||
}
|
||||
|
||||
fn onActionCompleted(self: *CharacterBrain, result: QueuedActionResult) void {
|
||||
switch (self.routine) {
|
||||
.idle => {},
|
||||
if (self.task == null) return;
|
||||
|
||||
switch (self.task.?) {
|
||||
.fight => |*args| {
|
||||
if (result.get(.fight)) |r| {
|
||||
const fight_result: Server.FightResult = r;
|
||||
@ -405,27 +419,21 @@ fn onActionCompleted(self: *CharacterBrain, result: QueuedActionResult) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isRoutineFinished(self: *CharacterBrain) bool {
|
||||
return switch (self.routine) {
|
||||
.idle => false,
|
||||
|
||||
.fight => |args| {
|
||||
return args.progress >= args.target.quantity;
|
||||
},
|
||||
.gather => |args| {
|
||||
return args.progress >= args.target.quantity;
|
||||
},
|
||||
.craft => |args| {
|
||||
return args.progress >= args.target.quantity;
|
||||
pub fn isTaskFinished(self: *CharacterBrain) bool {
|
||||
if (self.task == null) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return self.task.?.isComplete();
|
||||
}
|
||||
|
||||
pub fn performRoutine(self: *CharacterBrain, api: *Server) !void {
|
||||
switch (self.routine) {
|
||||
.idle => {
|
||||
pub fn performTask(self: *CharacterBrain, api: *Server) !void {
|
||||
if (self.task == null) {
|
||||
std.log.debug("[{s}] idle", .{self.name});
|
||||
},
|
||||
return;
|
||||
}
|
||||
|
||||
switch (self.task.?) {
|
||||
.fight => |args| {
|
||||
try self.fightRoutine(api, args.at);
|
||||
},
|
||||
@ -433,7 +441,7 @@ pub fn performRoutine(self: *CharacterBrain, api: *Server) !void {
|
||||
try self.gatherRoutine(api, args.at);
|
||||
},
|
||||
.craft => |args| {
|
||||
try self.craftRoutine(api, args.target.id, args.target.quantity);
|
||||
try self.craftRoutine(api, args.at, args.target.id, args.target.quantity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -541,7 +549,7 @@ fn withdrawFromBank(self: *CharacterBrain, api: *Server, items: []const Server.S
|
||||
return true;
|
||||
}
|
||||
|
||||
fn craftItem(self: *CharacterBrain, api: *Server, id: Server.ItemId, quantity: u64) !bool {
|
||||
fn craftItem(self: *CharacterBrain, api: *Server, workstation: Position, id: Server.ItemId, quantity: u64) !bool {
|
||||
var character = api.findCharacter(self.name).?;
|
||||
|
||||
const inventory_quantity = character.inventory.getQuantity(id);
|
||||
@ -549,24 +557,6 @@ fn craftItem(self: *CharacterBrain, api: *Server, id: Server.ItemId, quantity: u
|
||||
return false;
|
||||
}
|
||||
|
||||
const code = api.getItemCode(id) orelse return error.InvalidItemId;
|
||||
const item = try api.getItem(code) orelse return error.ItemNotFound;
|
||||
if (item.craft == null) {
|
||||
return error.NotCraftable;
|
||||
}
|
||||
|
||||
const recipe = item.craft.?;
|
||||
|
||||
// TODO: Figure this out dynamically
|
||||
const workstation = switch (recipe.skill) {
|
||||
.weaponcrafting => Position{ .x = 2, .y = 1 },
|
||||
.gearcrafting => Position{ .x = 3, .y = 1 },
|
||||
.jewelrycrafting => Position{ .x = 1, .y = 3 },
|
||||
.cooking => Position{ .x = 1, .y = 1 },
|
||||
.woodcutting => Position{ .x = -2, .y = -3 },
|
||||
.mining => Position{ .x = 1, .y = 5 },
|
||||
};
|
||||
|
||||
if (try self.moveIfNeeded(api, workstation)) {
|
||||
return true;
|
||||
}
|
||||
@ -579,7 +569,7 @@ fn craftItem(self: *CharacterBrain, api: *Server, id: Server.ItemId, quantity: u
|
||||
return true;
|
||||
}
|
||||
|
||||
fn craftRoutine(self: *CharacterBrain, api: *Server, id: Server.ItemId, quantity: u64) !void {
|
||||
fn craftRoutine(self: *CharacterBrain, api: *Server, workstation: Position, id: Server.ItemId, quantity: u64) !void {
|
||||
var character = api.findCharacter(self.name).?;
|
||||
const inventory_quantity = character.inventory.getQuantity(id);
|
||||
if (inventory_quantity >= quantity) {
|
||||
@ -595,7 +585,6 @@ fn craftRoutine(self: *CharacterBrain, api: *Server, id: Server.ItemId, quantity
|
||||
}
|
||||
|
||||
const recipe = target_item.craft.?;
|
||||
assert(recipe.quantity == 1); // TODO: Add support for recipe which produce multiple items
|
||||
|
||||
var needed_items = recipe.items;
|
||||
for (needed_items.slice()) |*needed_item| {
|
||||
@ -606,7 +595,7 @@ fn craftRoutine(self: *CharacterBrain, api: *Server, id: Server.ItemId, quantity
|
||||
return;
|
||||
}
|
||||
|
||||
if (try self.craftItem(api, id, quantity)) {
|
||||
if (try self.craftItem(api, workstation, id, quantity)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
205
src/main.zig
205
src/main.zig
@ -8,6 +8,10 @@ const CharacterBrain = @import("./character_brain.zig");
|
||||
|
||||
// pub const std_options = .{ .log_level = .debug };
|
||||
|
||||
fn todo() void {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn currentTime() f64 {
|
||||
const timestamp: f64 = @floatFromInt(std.time.milliTimestamp());
|
||||
return timestamp / std.time.ms_per_s;
|
||||
@ -82,6 +86,176 @@ fn getAPITokenFromArgs(allocator: Allocator) !?[]u8 {
|
||||
return try allocator.dupe(u8, std.mem.trim(u8,token,"\n\t "));
|
||||
}
|
||||
|
||||
const TaskTree = struct {
|
||||
const TaskTreeNode = struct {
|
||||
parent: ?usize,
|
||||
character_task: CharacterBrain.CharacterTask,
|
||||
};
|
||||
|
||||
const Nodes = std.ArrayList(TaskTreeNode);
|
||||
|
||||
allocator: Allocator,
|
||||
nodes: Nodes,
|
||||
api: *Server,
|
||||
|
||||
pub fn init(allocator: Allocator, api: *Server) TaskTree {
|
||||
return TaskTree{
|
||||
.allocator = allocator,
|
||||
.nodes = Nodes.init(allocator),
|
||||
.api = api
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: TaskTree) void {
|
||||
self.nodes.deinit();
|
||||
}
|
||||
|
||||
fn appendNode(self: *TaskTree, node: TaskTreeNode) !usize {
|
||||
try self.nodes.append(node);
|
||||
return self.nodes.items.len-1;
|
||||
}
|
||||
|
||||
fn appendSubTree(self: *TaskTree, code: []const u8, quantity: u64, parent: ?usize) !void {
|
||||
if (quantity == 0) return;
|
||||
|
||||
const item = (try self.api.getItem(code)).?;
|
||||
const item_id = try self.api.getItemId(code);
|
||||
|
||||
const eql = std.mem.eql;
|
||||
if (item.craft) |recipe| {
|
||||
const craft_count = std.math.divCeil(u64, quantity, recipe.quantity) catch unreachable;
|
||||
|
||||
const skill_str = Server.SkillUtils.toString(recipe.skill);
|
||||
const workshop_maps = try self.api.getMaps(.{ .code = skill_str, .type = .workshop });
|
||||
defer workshop_maps.deinit();
|
||||
|
||||
if (workshop_maps.items.len == 0) return error.WorkshopNotFound;
|
||||
if (workshop_maps.items.len > 1) std.log.warn("Multiple workshop locations exist", .{});
|
||||
|
||||
const node_id = try self.appendNode(TaskTreeNode{
|
||||
.parent = parent,
|
||||
.character_task = .{
|
||||
.craft = .{
|
||||
.at = workshop_maps.items[0].position,
|
||||
.target = .{ .id = item_id, .quantity = craft_count }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for (recipe.items.slots.constSlice()) |recipe_item| {
|
||||
const recipe_item_code = self.api.getItemCode(recipe_item.id).?;
|
||||
try self.appendSubTree(recipe_item_code, recipe_item.quantity * craft_count, node_id);
|
||||
}
|
||||
} else {
|
||||
if (item.type == .resource) {
|
||||
if (eql(u8, item.subtype, "mining")) {
|
||||
const resources = try self.api.getResources(.{ .drop = code });
|
||||
defer resources.deinit();
|
||||
|
||||
if (resources.items.len == 0) return error.ResourceNotFound;
|
||||
if (resources.items.len > 1) std.log.warn("Multiple resources exist for target item", .{});
|
||||
const resource_code = resources.items[0].code;
|
||||
|
||||
const resource_maps = try self.api.getMaps(.{ .code = resource_code });
|
||||
defer resource_maps.deinit();
|
||||
|
||||
if (resource_maps.items.len == 0) return error.MapNotFound;
|
||||
if (resource_maps.items.len > 1) std.log.warn("Multiple map locations exist for resource", .{});
|
||||
const resource_map = resource_maps.items[0];
|
||||
|
||||
_ = try self.appendNode(TaskTreeNode{
|
||||
.parent = parent,
|
||||
.character_task = .{
|
||||
.gather = .{
|
||||
.at = resource_map.position,
|
||||
.target = .{ .id = item_id, .quantity = quantity }
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (eql(u8, item.subtype, "mob")) {
|
||||
const monsters = try self.api.getMonsters(.{ .drop = code });
|
||||
defer monsters.deinit();
|
||||
|
||||
if (monsters.items.len == 0) return error.ResourceNotFound;
|
||||
if (monsters.items.len > 1) std.log.warn("Multiple monsters exist for target item", .{});
|
||||
const monster_code = monsters.items[0].code;
|
||||
|
||||
const resource_maps = try self.api.getMaps(.{ .code = monster_code });
|
||||
defer resource_maps.deinit();
|
||||
|
||||
if (resource_maps.items.len == 0) return error.MapNotFound;
|
||||
if (resource_maps.items.len > 1) std.log.warn("Multiple map locations exist for monster", .{});
|
||||
const resource_map = resource_maps.items[0];
|
||||
|
||||
_ = try self.appendNode(TaskTreeNode{
|
||||
.parent = parent,
|
||||
.character_task = .{
|
||||
.fight = .{
|
||||
.at = resource_map.position,
|
||||
.target = .{ .id = item_id, .quantity = quantity }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn listByParent(self: *const TaskTree, parent: ?usize) !std.ArrayList(usize) {
|
||||
var found_nodes = std.ArrayList(usize).init(self.allocator);
|
||||
for (0.., self.nodes.items) |i, node| {
|
||||
if (node.parent == parent) {
|
||||
try found_nodes.append(i);
|
||||
}
|
||||
}
|
||||
return found_nodes;
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: TaskTree,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
_ = fmt;
|
||||
_ = options;
|
||||
|
||||
var root_nodes = try self.listByParent(null);
|
||||
defer root_nodes.deinit();
|
||||
|
||||
for (root_nodes.items) |root_node| {
|
||||
try self.formatNode(root_node, 0, writer);
|
||||
}
|
||||
}
|
||||
|
||||
fn formatNode(self: TaskTree, node_id: usize, level: u32, writer: anytype) !void {
|
||||
const node = self.nodes.items[node_id];
|
||||
try writer.writeBytesNTimes(" ", level);
|
||||
switch (node.character_task) {
|
||||
.fight => |args| {
|
||||
const item = self.api.getItemCode(args.target.id).?;
|
||||
try writer.print("Task{{ .fight = {{ \"{s}\" x {} }}, .at = {} }}\n", .{item, args.target.quantity, args.at});
|
||||
},
|
||||
.gather => |args| {
|
||||
const item = self.api.getItemCode(args.target.id).?;
|
||||
try writer.print("Task{{ .gather = {{ \"{s}\" x {} }}, .at = {} }}\n", .{item, args.target.quantity, args.at});
|
||||
},
|
||||
.craft => |args| {
|
||||
const item = self.api.getItemCode(args.target.id).?;
|
||||
try writer.print("Task{{ .craft = {{ \"{s}\" x {} }}, .at = {} }}\n", .{item, args.target.quantity, args.at});
|
||||
},
|
||||
}
|
||||
|
||||
var child_nodes = try self.listByParent(node_id);
|
||||
defer child_nodes.deinit();
|
||||
|
||||
for (child_nodes.items) |child_node| {
|
||||
try self.formatNode(child_node, level + 1, writer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
@ -90,6 +264,8 @@ pub fn main() !void {
|
||||
var api = try Server.init(allocator);
|
||||
defer api.deinit();
|
||||
|
||||
try api.prefetch();
|
||||
|
||||
const token = (try getAPITokenFromArgs(allocator)) orelse return error.MissingToken;
|
||||
defer allocator.free(token);
|
||||
|
||||
@ -105,7 +281,7 @@ pub fn main() !void {
|
||||
try goal_manager.addCharacter(char.name);
|
||||
}
|
||||
|
||||
goal_manager.characters.items[0].routine = .{
|
||||
goal_manager.characters.items[0].task = .{
|
||||
.fight = .{
|
||||
.at = Position.init(0, 1),
|
||||
.target = .{
|
||||
@ -115,7 +291,7 @@ pub fn main() !void {
|
||||
},
|
||||
};
|
||||
|
||||
goal_manager.characters.items[1].routine = .{
|
||||
goal_manager.characters.items[1].task = .{
|
||||
.gather = .{
|
||||
.at = Position.init(-1, 0),
|
||||
.target = .{
|
||||
@ -125,7 +301,7 @@ pub fn main() !void {
|
||||
}
|
||||
};
|
||||
|
||||
goal_manager.characters.items[2].routine = .{
|
||||
goal_manager.characters.items[2].task = .{
|
||||
.gather = .{
|
||||
.at = Position.init(2, 0),
|
||||
.target = .{
|
||||
@ -135,7 +311,7 @@ pub fn main() !void {
|
||||
}
|
||||
};
|
||||
|
||||
goal_manager.characters.items[3].routine = .{
|
||||
goal_manager.characters.items[3].task = .{
|
||||
.gather = .{
|
||||
.at = Position.init(4, 2),
|
||||
.target = .{
|
||||
@ -145,7 +321,7 @@ pub fn main() !void {
|
||||
}
|
||||
};
|
||||
|
||||
goal_manager.characters.items[4].routine = .{
|
||||
goal_manager.characters.items[4].task = .{
|
||||
.fight = .{
|
||||
.at = Position.init(0, 1),
|
||||
.target = .{
|
||||
@ -155,11 +331,23 @@ pub fn main() !void {
|
||||
},
|
||||
};
|
||||
|
||||
const APIError = Server.APIError;
|
||||
var task_tree = TaskTree.init(allocator, &api);
|
||||
defer task_tree.deinit();
|
||||
|
||||
try task_tree.appendSubTree("sticky_dagger", 5, null);
|
||||
// try task_tree.appendSubTree("copper_boots" , 5, null);
|
||||
// try task_tree.appendSubTree("copper_helmet", 5, null);
|
||||
// try task_tree.appendSubTree("copper_legs_armor", 5, null);
|
||||
// try task_tree.appendSubTree("copper_armor", 5, null);
|
||||
// try task_tree.appendSubTree("copper_ring", 5, null);
|
||||
|
||||
std.debug.print("{}", .{task_tree});
|
||||
|
||||
if (false) {
|
||||
std.log.info("Starting main loop", .{});
|
||||
while (true) {
|
||||
goal_manager.runNextAction() catch |err| switch (err) {
|
||||
APIError.ServerUnavailable => {
|
||||
Server.APIError.ServerUnavailable => {
|
||||
// If the server is down, wait for a moment and try again.
|
||||
std.time.sleep(std.time.ns_per_min * 5);
|
||||
continue;
|
||||
@ -174,7 +362,7 @@ pub fn main() !void {
|
||||
|
||||
if (brain.isRoutineFinished()) {
|
||||
if (!try brain.depositItemsToBank(&api)) {
|
||||
brain.routine = .idle;
|
||||
brain.routine = null;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -182,4 +370,5 @@ pub fn main() !void {
|
||||
try brain.performRoutine(&api);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user