From cc289194a8ca6a44885d9f5966fe39d1f76a4a58 Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Sun, 8 Sep 2024 01:09:45 +0300 Subject: [PATCH] add api store for static data --- api/root.zig | 6 +- api/schemas/bank_gold_transaction.zig | 6 +- api/schemas/bank_item_transaction.zig | 6 +- api/schemas/character.zig | 9 +- api/schemas/character_fight.zig | 8 +- api/schemas/character_movement.zig | 6 +- api/schemas/craft.zig | 6 +- api/schemas/drop_rate.zig | 15 +- api/schemas/equip_request.zig | 8 +- api/schemas/equipment.zig | 65 +-- api/schemas/fight.zig | 6 +- api/schemas/item.zig | 6 +- api/schemas/{slot.zig => item_quantity.zig} | 20 +- api/schemas/map.zig | 9 +- api/schemas/map_content.zig | 14 +- api/schemas/monster.zig | 11 +- api/schemas/resource.zig | 11 +- api/schemas/single_item.zig | 6 +- api/schemas/skill_data.zig | 8 +- api/schemas/skill_info.zig | 6 +- api/schemas/slot_array.zig | 30 +- api/schemas/status.zig | 6 +- api/schemas/task.zig | 12 +- api/schemas/task_data.zig | 8 +- api/server.zig | 415 +++----------------- api/store.zig | 302 ++++++++++++++ lib/action.zig | 12 +- lib/brain.zig | 33 +- lib/root.zig | 16 +- lib/task.zig | 32 +- lib/task_graph.zig | 63 +-- 31 files changed, 571 insertions(+), 590 deletions(-) rename api/schemas/{slot.zig => item_quantity.zig} (52%) create mode 100644 api/store.zig diff --git a/api/root.zig b/api/root.zig index fcd4016..a149f1f 100644 --- a/api/root.zig +++ b/api/root.zig @@ -2,12 +2,14 @@ pub const parseDateTime = @import("./date_time/parse.zig").parseDateTime; pub const Server = @import("server.zig"); +pub const Store = @import("store.zig"); +pub const Character = @import("./schemas/character.zig"); pub const Position = @import("position.zig"); pub const BoundedSlotsArray = @import("schemas/slot_array.zig").BoundedSlotsArray; pub const Slot = Server.Slot; -pub const ItemId = Server.ItemId; -pub const ItemIdQuantity = Server.ItemIdQuantity; +pub const CodeId = Store.CodeId; +pub const ItemQuantity = @import("./schemas/item_quantity.zig"); const errors = @import("errors.zig"); pub const FetchError = errors.FetchError; diff --git a/api/schemas/bank_gold_transaction.zig b/api/schemas/bank_gold_transaction.zig index 75a3f09..5175ae4 100644 --- a/api/schemas/bank_gold_transaction.zig +++ b/api/schemas/bank_gold_transaction.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -12,12 +12,12 @@ const BankGoldTransaction = @This(); cooldown: Cooldown, character: Character, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !BankGoldTransaction { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !BankGoldTransaction { const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty; return BankGoldTransaction{ .cooldown = try Cooldown.parse(cooldown), - .character = try Character.parse(api, character, allocator) + .character = try Character.parse(store, character, allocator) }; } diff --git a/api/schemas/bank_item_transaction.zig b/api/schemas/bank_item_transaction.zig index c2f00de..8b04a0e 100644 --- a/api/schemas/bank_item_transaction.zig +++ b/api/schemas/bank_item_transaction.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -12,12 +12,12 @@ const BankItemTransaction = @This(); cooldown: Cooldown, character: Character, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !BankItemTransaction { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !BankItemTransaction { const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty; return BankItemTransaction{ .cooldown = try Cooldown.parse(cooldown), - .character = try Character.parse(api, character, allocator) + .character = try Character.parse(store, character, allocator) }; } diff --git a/api/schemas/character.zig b/api/schemas/character.zig index 4aa6607..0f72c4e 100644 --- a/api/schemas/character.zig +++ b/api/schemas/character.zig @@ -1,9 +1,8 @@ const std = @import("std"); const json_utils = @import("../json_utils.zig"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const Position = @import("../position.zig"); const parseDateTime = @import("../date_time/parse.zig").parseDateTime; -const ItemId = Server.ItemId; const Allocator = std.mem.Allocator; const json = std.json; const assert = std.debug.assert; @@ -56,7 +55,7 @@ inventory: Inventory, task: ?TaskMasterTask, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Character { const inventory = json_utils.getArray(obj, "inventory") orelse return error.MissingProperty; const cooldown_expiration = json_utils.getString(obj, "cooldown_expiration") orelse return error.MissingProperty; @@ -119,10 +118,10 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Character .earth = try CombatStats.parse(obj, "attack_earth", "dmg_earth", "res_earth"), .air = try CombatStats.parse(obj, "attack_air", "dmg_air", "res_air"), - .equipment = try Equipment.parse(api, obj), + .equipment = try Equipment.parse(store, obj), .inventory_max_items = @intCast(inventory_max_items), - .inventory = try Inventory.parse(api, inventory), + .inventory = try Inventory.parse(store, inventory), .task = task }; diff --git a/api/schemas/character_fight.zig b/api/schemas/character_fight.zig index 93acc7a..2f37717 100644 --- a/api/schemas/character_fight.zig +++ b/api/schemas/character_fight.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -14,14 +14,14 @@ cooldown: Cooldown, fight: Fight, character: Character, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !CharacterFight { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !CharacterFight { const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; const fight = json_utils.getObject(obj, "fight") orelse return error.MissingProperty; const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty; return CharacterFight{ .cooldown = try Cooldown.parse(cooldown), - .fight = try Fight.parse(api, fight), - .character = try Character.parse(api, character, allocator) + .fight = try Fight.parse(store, fight), + .character = try Character.parse(store, character, allocator) }; } diff --git a/api/schemas/character_movement.zig b/api/schemas/character_movement.zig index 50d1476..b1e7c6f 100644 --- a/api/schemas/character_movement.zig +++ b/api/schemas/character_movement.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -12,12 +12,12 @@ const CharacterMovement = @This(); cooldown: Cooldown, character: Character, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !CharacterMovement { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !CharacterMovement { const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty; return CharacterMovement{ .cooldown = try Cooldown.parse(cooldown), - .character = try Character.parse(api, character, allocator) + .character = try Character.parse(store, character, allocator) }; } diff --git a/api/schemas/craft.zig b/api/schemas/craft.zig index c5fdce9..9c5f0f1 100644 --- a/api/schemas/craft.zig +++ b/api/schemas/craft.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const BoundedSlotsArray = @import("./slot_array.zig").BoundedSlotsArray; const json = std.json; @@ -16,7 +16,7 @@ level: u64, quantity: u64, items: Items, -pub fn parse(api: *Server, obj: json.ObjectMap) !Craft { +pub fn parse(store: *Store, obj: json.ObjectMap) !Craft { const skill = json_utils.getString(obj, "skill") orelse return error.MissingProperty; const level = json_utils.getInteger(obj, "level") orelse return error.MissingProperty; if (level < 1) return error.InvalidLevel; @@ -30,6 +30,6 @@ pub fn parse(api: *Server, obj: json.ObjectMap) !Craft { .skill = SkillUtils.fromString(skill) orelse return error.InvalidSkill, .level = @intCast(level), .quantity = @intCast(quantity), - .items = try Items.parse(api, items) + .items = try Items.parse(store, items) }; } diff --git a/api/schemas/drop_rate.zig b/api/schemas/drop_rate.zig index 0557f00..c2179ab 100644 --- a/api/schemas/drop_rate.zig +++ b/api/schemas/drop_rate.zig @@ -1,17 +1,16 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); -const ItemId = Server.ItemId; const json = std.json; const DropRate = @This(); -item_id: ItemId, +item_id: Store.CodeId, rate: u64, min_quantity: u64, max_quantity: u64, -pub fn parse(api: *Server, obj: json.ObjectMap) !DropRate { +pub fn parse(store: *Store, obj: json.ObjectMap) !DropRate { const rate = try json_utils.getIntegerRequired(obj, "rate"); if (rate < 1) { return error.InvalidRate; @@ -28,7 +27,7 @@ pub fn parse(api: *Server, obj: json.ObjectMap) !DropRate { } const code_str = try json_utils.getStringRequired(obj, "code"); - const item_id = try api.getItemId(code_str); + const item_id = try store.getItemId(code_str); return DropRate{ .item_id = item_id, @@ -40,16 +39,16 @@ pub fn parse(api: *Server, obj: json.ObjectMap) !DropRate { pub const DropRates = std.BoundedArray(DropRate, 8); // TODO: Maybe rename to "List"? -pub fn parseList(api: *Server, array: json.Array) !DropRates { +pub fn parseList(store: *Store, array: json.Array) !DropRates { var drops = DropRates.init(0) catch unreachable; for (array.items) |drop_value| { const drop_obj = json_utils.asObject(drop_value) orelse return error.InvalidObject; - try drops.append(try DropRate.parse(api, drop_obj)); + try drops.append(try DropRate.parse(store, drop_obj)); } return drops; } -pub fn doesListContain(drops: *DropRates, item_id: ItemId) bool { +pub fn doesListContain(drops: *DropRates, item_id: Store.CodeId) bool { for (drops.constSlice()) |drop| { if (drop.item_id == item_id) { return true; diff --git a/api/schemas/equip_request.zig b/api/schemas/equip_request.zig index 274e591..878656d 100644 --- a/api/schemas/equip_request.zig +++ b/api/schemas/equip_request.zig @@ -1,8 +1,8 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; -const ItemId = Server.ItemId; +const ItemId = Store.ItemId; const Cooldown = @import("./cooldown.zig"); @@ -11,13 +11,13 @@ const EquipRequest = @This(); cooldown: Cooldown, item: ItemId, -pub fn parse(api: *Server, obj: json.ObjectMap) !EquipRequest { +pub fn parse(store: *Store, obj: json.ObjectMap) !EquipRequest { const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; const item = json_utils.getObject(obj, "item") orelse return error.MissingProperty; const item_code = json_utils.getString(item, "code") orelse return error.MissingProperty; - const item_id = try api.getItemId(item_code); + const item_id = try store.getItemId(item_code); // TODO: Might as well save information about time, because full details about it are given diff --git a/api/schemas/equipment.zig b/api/schemas/equipment.zig index 81b2340..824a2a2 100644 --- a/api/schemas/equipment.zig +++ b/api/schemas/equipment.zig @@ -1,18 +1,19 @@ const std = @import("std"); const json_utils = @import("../json_utils.zig"); -const Server = @import("../server.zig"); -const ItemId = Server.ItemId; +const Store = @import("../store.zig"); const json = std.json; const Equipment = @This(); +const CodeId = Store.CodeId; + pub const Consumable = struct { - id: ?ItemId, + code_id: ?CodeId, quantity: i64, - fn parse(api: *Server, obj: json.ObjectMap, name: []const u8, quantity: []const u8) !Consumable { + fn parse(store: *Store, obj: json.ObjectMap, name: []const u8, quantity: []const u8) !Consumable { return Consumable{ - .id = try api.getItemIdJson(obj, name), + .code_id = try store.getCodeIdJson(obj, name), .quantity = try json_utils.getIntegerRequired(obj, quantity), }; } @@ -52,39 +53,39 @@ pub const Slot = enum { } }; -weapon: ?ItemId, -shield: ?ItemId, -helmet: ?ItemId, -body_armor: ?ItemId, -leg_armor: ?ItemId, -boots: ?ItemId, +weapon: ?CodeId, +shield: ?CodeId, +helmet: ?CodeId, +body_armor: ?CodeId, +leg_armor: ?CodeId, +boots: ?CodeId, -ring1: ?ItemId, -ring2: ?ItemId, -amulet: ?ItemId, +ring1: ?CodeId, +ring2: ?CodeId, +amulet: ?CodeId, -artifact1: ?ItemId, -artifact2: ?ItemId, -artifact3: ?ItemId, +artifact1: ?CodeId, +artifact2: ?CodeId, +artifact3: ?CodeId, consumable1: Consumable, consumable2: Consumable, -pub fn parse(api: *Server, obj: json.ObjectMap) !Equipment { +pub fn parse(store: *Store, obj: json.ObjectMap) !Equipment { return Equipment{ - .weapon = try api.getItemIdJson(obj, "weapon_slot"), - .shield = try api.getItemIdJson(obj, "shield_slot"), - .helmet = try api.getItemIdJson(obj, "helmet_slot"), - .body_armor = try api.getItemIdJson(obj, "body_armor_slot"), - .leg_armor = try api.getItemIdJson(obj, "leg_armor_slot"), - .boots = try api.getItemIdJson(obj, "boots_slot"), - .ring1 = try api.getItemIdJson(obj, "ring1_slot"), - .ring2 = try api.getItemIdJson(obj, "ring2_slot"), - .amulet = try api.getItemIdJson(obj, "amulet_slot"), - .artifact1 = try api.getItemIdJson(obj, "artifact1_slot"), - .artifact2 = try api.getItemIdJson(obj, "artifact2_slot"), - .artifact3 = try api.getItemIdJson(obj, "artifact3_slot"), - .consumable1 = try Consumable.parse(api, obj, "consumable1_slot", "consumable1_slot_quantity"), - .consumable2 = try Consumable.parse(api, obj, "consumable2_slot", "consumable2_slot_quantity"), + .weapon = try store.getCodeIdJson(obj, "weapon_slot"), + .shield = try store.getCodeIdJson(obj, "shield_slot"), + .helmet = try store.getCodeIdJson(obj, "helmet_slot"), + .body_armor = try store.getCodeIdJson(obj, "body_armor_slot"), + .leg_armor = try store.getCodeIdJson(obj, "leg_armor_slot"), + .boots = try store.getCodeIdJson(obj, "boots_slot"), + .ring1 = try store.getCodeIdJson(obj, "ring1_slot"), + .ring2 = try store.getCodeIdJson(obj, "ring2_slot"), + .amulet = try store.getCodeIdJson(obj, "amulet_slot"), + .artifact1 = try store.getCodeIdJson(obj, "artifact1_slot"), + .artifact2 = try store.getCodeIdJson(obj, "artifact2_slot"), + .artifact3 = try store.getCodeIdJson(obj, "artifact3_slot"), + .consumable1 = try Consumable.parse(store, obj, "consumable1_slot", "consumable1_slot_quantity"), + .consumable2 = try Consumable.parse(store, obj, "consumable2_slot", "consumable2_slot_quantity"), }; } diff --git a/api/schemas/fight.zig b/api/schemas/fight.zig index 8775a3a..7e89237 100644 --- a/api/schemas/fight.zig +++ b/api/schemas/fight.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const BoundedSlotsArray = @import("./slot_array.zig").BoundedSlotsArray; const json_utils = @import("../json_utils.zig"); const json = std.json; @@ -13,7 +13,7 @@ gold: u64, drops: Drops, won: bool, -pub fn parse(api: *Server, obj: json.ObjectMap) !Fight { +pub fn parse(store: *Store, obj: json.ObjectMap) !Fight { const result = try json_utils.getStringRequired(obj, "result"); var won = false; @@ -40,7 +40,7 @@ pub fn parse(api: *Server, obj: json.ObjectMap) !Fight { return Fight{ .xp = @intCast(xp), .gold = @intCast(gold), - .drops = try Drops.parse(api, drops_obj), + .drops = try Drops.parse(store, drops_obj), .won = won, }; } diff --git a/api/schemas/item.zig b/api/schemas/item.zig index af0ddf9..66512d5 100644 --- a/api/schemas/item.zig +++ b/api/schemas/item.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const EnumStringUtils = @import("../enum_string_utils.zig").EnumStringUtils; const json = std.json; const Allocator = std.mem.Allocator; @@ -48,7 +48,7 @@ description: []u8, craft: ?Craft, // TODO: effects -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Item { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Item { const level = json_utils.getInteger(obj, "level") orelse return error.MissingProperty; if (level < 1) return error.InvalidLevel; @@ -63,7 +63,7 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Item { .type = TypeUtils.fromString(item_type_str) orelse return error.InvalidType, .subtype = (try json_utils.dupeString(allocator, obj, "subtype")) orelse return error.MissingProperty, .description = (try json_utils.dupeString(allocator, obj, "description")) orelse return error.MissingProperty, - .craft = if (craft != null) try Craft.parse(api, craft.?) else null + .craft = if (craft != null) try Craft.parse(store, craft.?) else null }; } diff --git a/api/schemas/slot.zig b/api/schemas/item_quantity.zig similarity index 52% rename from api/schemas/slot.zig rename to api/schemas/item_quantity.zig index eb547c5..29185cb 100644 --- a/api/schemas/slot.zig +++ b/api/schemas/item_quantity.zig @@ -1,23 +1,29 @@ const std = @import("std"); const json_utils = @import("../json_utils.zig"); -const Server = @import("../server.zig"); -const ItemId = Server.ItemId; +const Store = @import("../store.zig"); const json = std.json; -const ItemSlot = @This(); +const ItemQuantity = @This(); -id: ItemId, +id: Store.CodeId, quantity: u64, -pub fn parse(api: *Server, slot_obj: json.ObjectMap) !?ItemSlot { +pub fn init(id: Store.CodeId, quantity: u64) ItemQuantity { + return ItemQuantity{ + .id = id, + .quantity = quantity + }; +} + +pub fn parse(store: *Store, slot_obj: json.ObjectMap) !?ItemQuantity { const code = try json_utils.getStringRequired(slot_obj, "code"); if (code.len == 0) return null; const quantity = try json_utils.getIntegerRequired(slot_obj, "quantity"); if (quantity < 0) return error.InvalidQuantity; - return ItemSlot{ - .id = try api.getItemId(code), + return ItemQuantity{ + .id = try store.getCodeId(code), .quantity = @intCast(quantity), }; } diff --git a/api/schemas/map.zig b/api/schemas/map.zig index 6e3b0c9..9f293a8 100644 --- a/api/schemas/map.zig +++ b/api/schemas/map.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const Position = @import("../position.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; @@ -14,7 +14,7 @@ skin: []u8, position: Position, content: ?MapContent, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Map { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Map { const content = json_utils.getObject(obj, "content"); const x = json_utils.getInteger(obj, "x") orelse return error.MissingProperty; @@ -24,14 +24,11 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Map { .name = (try json_utils.dupeString(allocator, obj, "name")) orelse return error.MissingProperty, .skin = (try json_utils.dupeString(allocator, obj, "skin")) orelse return error.MissingProperty, .position = Position.init(x, y), - .content = try MapContent.parse(api, content, allocator) + .content = try MapContent.parse(store, content) }; } pub fn deinit(self: Map, allocator: Allocator) void { allocator.free(self.name); allocator.free(self.skin); - if (self.content) |content| { - content.deinit(allocator); - } } diff --git a/api/schemas/map_content.zig b/api/schemas/map_content.zig index c5c82f2..1ac4a82 100644 --- a/api/schemas/map_content.zig +++ b/api/schemas/map_content.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -25,19 +25,13 @@ pub const TypeUtils = EnumStringUtils(Type, .{ }); type: Type, -code: []u8, - -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) MapContent { - _ = api; +code_id: Store.CodeId, +pub fn parse(store: *Store, obj: json.ObjectMap) MapContent { const content_type = json_utils.getString(obj, "type") orelse return error.MissingProperty; return MapContent{ .type = TypeUtils.fromString(content_type) orelse return error.InvalidContentType, - .code = (try json_utils.dupeString(allocator, obj, "code")) orelse return error.MissingProperty, + .code = (try store.getCodeIdJson(obj, "code")) orelse return error.MissingProperty }; } - -pub fn deinit(self: MapContent, allocator: Allocator) void { - allocator.free(self.code); -} diff --git a/api/schemas/monster.zig b/api/schemas/monster.zig index 18828ee..68488c0 100644 --- a/api/schemas/monster.zig +++ b/api/schemas/monster.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -22,7 +22,7 @@ pub const ElementalStats = struct { }; name: []u8, -code: []u8, +code_id: Store.CodeId, level: u64, hp: u64, min_gold: u64, @@ -35,7 +35,7 @@ air: ElementalStats, drops: DropRates, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Monster { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Monster { const drops_array = json_utils.getArray(obj, "drops") orelse return error.MissingProperty; const min_gold = try json_utils.getIntegerRequired(obj, "min_gold"); @@ -60,7 +60,7 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Monster { return Monster{ .name = try json_utils.dupeStringRequired(allocator, obj, "name"), - .code = try json_utils.dupeStringRequired(allocator, obj, "code"), + .code = (try store.getCodeIdJson(allocator, obj, "code")) orelse return error.MissingProperty, .level = @intCast(level), .hp = @intCast(hp), @@ -71,11 +71,10 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Monster { .min_gold = @intCast(min_gold), .max_gold = @intCast(max_gold), - .drops = try DropRate.parseList(api, drops_array) + .drops = try DropRate.parseList(store, drops_array) }; } pub fn deinit(self: Monster, allocator: Allocator) void { allocator.free(self.name); - allocator.free(self.code); } diff --git a/api/schemas/resource.zig b/api/schemas/resource.zig index 9a9a5a2..c12569e 100644 --- a/api/schemas/resource.zig +++ b/api/schemas/resource.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const EnumStringUtils = @import("../enum_string_utils.zig").EnumStringUtils; const json_utils = @import("../json_utils.zig"); const json = std.json; @@ -22,12 +22,12 @@ pub const SkillUtils = EnumStringUtils(Skill, .{ }); name: []u8, -code: []u8, +code_id: Store.CodeId, skill: Skill, level: u64, drops: DropRates, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Resource { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Resource { const drops_array = json_utils.getArray(obj, "drops") orelse return error.MissingProperty; const level = try json_utils.getIntegerRequired(obj, "level"); @@ -39,14 +39,13 @@ pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !Resource return Resource{ .name = try json_utils.dupeStringRequired(allocator, obj, "name"), - .code = try json_utils.dupeStringRequired(allocator, obj, "code"), + .code = (try store.getCodeIdJson(allocator, obj, "code")) orelse return error.MissingProperty, .level = @intCast(level), .skill = SkillUtils.fromString(skill_str) orelse return error.InvalidSkill, - .drops = try DropRate.parseList(api, drops_array) + .drops = try DropRate.parseList(store, drops_array) }; } pub fn deinit(self: Resource, allocator: Allocator) void { allocator.free(self.name); - allocator.free(self.code); } diff --git a/api/schemas/single_item.zig b/api/schemas/single_item.zig index 18a211c..ce67237 100644 --- a/api/schemas/single_item.zig +++ b/api/schemas/single_item.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -11,12 +11,12 @@ const SingleItem = @This(); item: Item, // TODO: Grand exchange -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !SingleItem { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !SingleItem { const item_obj = json_utils.getObject(obj, "item") orelse return error.MissingProperty; const ge_obj = json_utils.getObject(obj, "ge") orelse return error.MissingProperty; _ = ge_obj; return SingleItem{ - .item = try Item.parse(api, item_obj, allocator), + .item = try Item.parse(store, item_obj, allocator), }; } diff --git a/api/schemas/skill_data.zig b/api/schemas/skill_data.zig index 73da878..4c4c2d7 100644 --- a/api/schemas/skill_data.zig +++ b/api/schemas/skill_data.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -14,14 +14,14 @@ cooldown: Cooldown, details: SkillInfo, character: Character, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !SkillData { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !SkillData { const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; const details = json_utils.getObject(obj, "details") orelse return error.MissingProperty; const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty; return SkillData{ .cooldown = try Cooldown.parse(cooldown), - .details = try SkillInfo.parse(api, details), - .character = try Character.parse(api, character, allocator) + .details = try SkillInfo.parse(store, details), + .character = try Character.parse(store, character, allocator) }; } diff --git a/api/schemas/skill_info.zig b/api/schemas/skill_info.zig index 09256fd..ccdfa3f 100644 --- a/api/schemas/skill_info.zig +++ b/api/schemas/skill_info.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const BoundedSlotsArray = @import("./slot_array.zig").BoundedSlotsArray; const json_utils = @import("../json_utils.zig"); const json = std.json; @@ -11,11 +11,11 @@ const SkillInfo = @This(); xp: i64, items: Items, -pub fn parse(api: *Server, obj: json.ObjectMap) !SkillInfo { +pub fn parse(store: *Store, obj: json.ObjectMap) !SkillInfo { const items = json_utils.getArray(obj, "items") orelse return error.MissingProperty; return SkillInfo{ .xp = try json_utils.getIntegerRequired(obj, "xp"), - .items = try Items.parse(api, items), + .items = try Items.parse(store, items), }; } diff --git a/api/schemas/slot_array.zig b/api/schemas/slot_array.zig index 10bd278..3e00f1f 100644 --- a/api/schemas/slot_array.zig +++ b/api/schemas/slot_array.zig @@ -1,14 +1,14 @@ const std = @import("std"); const json_utils = @import("../json_utils.zig"); -const Server = @import("../server.zig"); -const ItemId = Server.ItemId; +const Store = @import("../store.zig"); const assert = std.debug.assert; const json = std.json; -const Slot = @import("./slot.zig"); +const ItemQuantity = @import("./item_quantity.zig"); pub fn BoundedSlotsArray(comptime slot_count: u32) type { - const Slots = std.BoundedArray(Slot, slot_count); + const Slots = std.BoundedArray(ItemQuantity, slot_count); + const CodeId = Store.CodeId; return struct { slots: Slots, @@ -19,13 +19,13 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type { }; } - pub fn parse(api: *Server, slots_array: json.Array) !@This() { + pub fn parse(api: *Store, slots_array: json.Array) !@This() { var slots = Slots.init(0) catch unreachable; for (slots_array.items) |slot_value| { const slot_obj = json_utils.asObject(slot_value) orelse return error.InvalidType; - if (try Slot.parse(api, slot_obj)) |slot| { + if (try ItemQuantity.parse(api, slot_obj)) |slot| { try slots.append(slot); } } @@ -33,7 +33,7 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type { return @This(){ .slots = slots }; } - fn findSlotIndex(self: *const @This(), id: ItemId) ?usize { + fn findSlotIndex(self: *const @This(), id: CodeId) ?usize { for (0.., self.slots.slice()) |i, *slot| { if (slot.id == id) { return i; @@ -43,7 +43,7 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type { return null; } - fn findSlot(self: *@This(), id: ItemId) ?*Slot { + fn findSlot(self: *@This(), id: CodeId) ?*ItemQuantity { if (self.findSlotIndex(id)) |index| { return &self.slots.buffer[index]; } @@ -51,7 +51,7 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type { return null; } - pub fn remove(self: *@This(), id: ItemId, quantity: u64) void { + pub fn remove(self: *@This(), id: CodeId, quantity: u64) void { const slot_index = self.findSlotIndex(id) orelse unreachable; const slot = self.slots.get(slot_index); assert(slot.quantity >= quantity); @@ -62,29 +62,29 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type { } } - pub fn add(self: *@This(), id: ItemId, quantity: u64) !void { + pub fn add(self: *@This(), id: CodeId, quantity: u64) !void { if (quantity == 0) return; if (self.findSlot(id)) |slot| { slot.quantity += quantity; } else { - try self.slots.append(Slot{ .id = id, .quantity = quantity }); + try self.slots.append(ItemQuantity.init(id, quantity)); } } - pub fn addSlice(self: *@This(), items: []const Server.ItemIdQuantity) void { + pub fn addSlice(self: *@This(), items: []const ItemQuantity) void { for (items) |item| { self.add(item.id, item.quantity); } } - pub fn removeSlice(self: *@This(), items: []const Server.ItemIdQuantity) void { + pub fn removeSlice(self: *@This(), items: []const ItemQuantity) void { for (items) |item| { self.remove(item.id, item.quantity); } } - pub fn getQuantity(self: *const @This(), id: ItemId) u64 { + pub fn getQuantity(self: *const @This(), id: CodeId) u64 { if (self.findSlotIndex(id)) |index| { return self.slots.get(index).quantity; } @@ -100,7 +100,7 @@ pub fn BoundedSlotsArray(comptime slot_count: u32) type { return count; } - pub fn slice(self: *@This()) []Slot { + pub fn slice(self: *@This()) []ItemQuantity { return self.slots.slice(); } }; diff --git a/api/schemas/status.zig b/api/schemas/status.zig index 5f5af55..13514ea 100644 --- a/api/schemas/status.zig +++ b/api/schemas/status.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -11,8 +11,8 @@ status: []const u8, version: []const u8, characters_online: u64, -pub fn parse(api: *Server, object: json.ObjectMap, allocator: Allocator) !ServerStatus { - _ = api; +pub fn parse(store: *Store, object: json.ObjectMap, allocator: Allocator) !ServerStatus { + _ = store; const characters_online = json_utils.getInteger(object, "characters_online") orelse return error.MissingProperty; if (characters_online < 0) { return error.InvalidCharactersOnline; diff --git a/api/schemas/task.zig b/api/schemas/task.zig index 4e3575e..ec13241 100644 --- a/api/schemas/task.zig +++ b/api/schemas/task.zig @@ -1,9 +1,8 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const EnumStringUtils = @import("../enum_string_utils.zig").EnumStringUtils; const json = std.json; -const ItemId = Server.ItemId; const Task = @This(); @@ -18,22 +17,19 @@ pub const TypeUtils = EnumStringUtils(Type, .{ .{ "crafts" , Type.crafts }, }); -id: ItemId, // TODO: Refactor `ItemId` to include other object types +code_id: Store.CodeId, type: Type, total: u64, -pub fn parse(api: *Server, obj: json.ObjectMap) !Task { +pub fn parse(store: *Store, obj: json.ObjectMap) !Task { const task_type = try json_utils.getStringRequired(obj, "type"); const total = try json_utils.getIntegerRequired(obj, "total"); if (total < 0) { return error.InvalidTaskTotal; } - const code = json_utils.getStringRequired(obj, "code"); - const id = try api.getItemId(code); - return Task{ - .id = id, + .code_id = (try store.getCodeIdJson(obj, "code")) orelse return error.MissingProperty, .type = TypeUtils.fromString(task_type) orelse return error.InvalidTaskType, .total = @intCast(total) }; diff --git a/api/schemas/task_data.zig b/api/schemas/task_data.zig index e099cb5..3d11797 100644 --- a/api/schemas/task_data.zig +++ b/api/schemas/task_data.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const Server = @import("../server.zig"); +const Store = @import("../store.zig"); const json_utils = @import("../json_utils.zig"); const json = std.json; const Allocator = std.mem.Allocator; @@ -14,14 +14,14 @@ cooldown: Cooldown, character: Character, task: Task, -pub fn parse(api: *Server, obj: json.ObjectMap, allocator: Allocator) !TaskData { +pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !TaskData { const cooldown = json_utils.getObject(obj, "cooldown") orelse return error.MissingProperty; const character = json_utils.getObject(obj, "character") orelse return error.MissingProperty; const task = json_utils.getObject(obj, "task") orelse return error.MissingProperty; return TaskData{ .cooldown = try Cooldown.parse(cooldown), - .character = try Character.parse(api, character, allocator), - .task = try Task.parse(api, task) + .character = try Character.parse(store, character, allocator), + .task = try Task.parse(store, task) }; } diff --git a/api/server.zig b/api/server.zig index e3282aa..e93cae8 100644 --- a/api/server.zig +++ b/api/server.zig @@ -1,12 +1,13 @@ const std = @import("std"); +const json_utils = @import("json_utils.zig"); const assert = std.debug.assert; const json = std.json; const Allocator = std.mem.Allocator; -const json_utils = @import("json_utils.zig"); -pub const Character = @import("./schemas/character.zig"); +const Character = @import("./schemas/character.zig"); const EnumStringUtils = @import("./enum_string_utils.zig").EnumStringUtils; -pub const Position = @import("./position.zig"); +const Position = @import("./position.zig"); +const Store = @import("./store.zig"); const errors = @import("./errors.zig"); const FetchError = errors.FetchError; @@ -16,33 +17,20 @@ const FetchError = errors.FetchError; const Server = @This(); const log = std.log.scoped(.api); -pub const ItemId = u32; allocator: Allocator, client: std.http.Client, server: []u8, server_uri: std.Uri, - token: ?[]u8 = null, -item_codes: std.ArrayList([]u8), -characters: std.ArrayList(Character), - -items: std.StringHashMap(Item), -maps: std.AutoHashMap(Position, MapTile), -resources: std.StringHashMap(Resource), -monsters: std.StringHashMap(Monster), - -prefetched_resources: bool = false, -prefetched_maps: bool = false, -prefetched_monsters: bool = false, -prefetched_items: bool = false, +store: Store, +prefetched: bool = false, // ------------------------- API result structs ------------------------ -pub const Slot = @import("./schemas/slot.zig"); -const BoundedSlotsArray = @import("./schemas/slot_array.zig").BoundedSlotsArray; +const BoundedSlotsArray = @import("./schemas/slot_array.zig").BoundedSlotsArray; pub const EquipmentSlot = @import("./schemas/equipment.zig").Slot; pub const ServerStatus = @import("./schemas/status.zig"); pub const Cooldown = @import("./schemas/cooldown.zig"); @@ -64,29 +52,7 @@ pub const ItemWithGE = @import("./schemas/single_item.zig"); pub const Resource = @import("./schemas/resource.zig"); pub const Monster = @import("./schemas/monster.zig"); -// TODO: Replace this with ItemSlot struct -pub const ItemIdQuantity = struct { - id: ItemId, - quantity: u64, - - pub fn init(id: ItemId, quantity: u64) ItemIdQuantity { - return ItemIdQuantity{ - .id = id, - .quantity = quantity - }; - } - - pub fn parse(api: *Server, obj: json.ObjectMap) !ItemIdQuantity { - const code = try json_utils.getStringRequired(obj, "code"); - const quantity = try json_utils.getIntegerRequired(obj, "quantity"); - if (quantity < 1) return error.InvalidQuantity; - - return ItemIdQuantity{ - .id = try api.getItemId(code), - .quantity = @intCast(quantity) - }; - } -}; +const ItemQuantity = @import("./schemas/item_quantity.zig"); pub const ArtifactsFetchResult = struct { arena: std.heap.ArenaAllocator, @@ -110,12 +76,7 @@ pub fn init(allocator: Allocator) !Server { .server = url, .server_uri = uri, - .item_codes = std.ArrayList([]u8).init(allocator), - .characters = std.ArrayList(Character).init(allocator), - .items = std.StringHashMap(Item).init(allocator), - .maps = std.AutoHashMap(Position, MapTile).init(allocator), - .resources = std.StringHashMap(Resource).init(allocator), - .monsters = std.StringHashMap(Monster).init(allocator), + .store = Store.init(allocator), }; } @@ -124,39 +85,7 @@ pub fn deinit(self: *Server) void { self.allocator.free(self.server); if (self.token) |str| self.allocator.free(str); - for (self.item_codes.items) |code| { - self.allocator.free(code); - } - self.item_codes.deinit(); - - for (self.characters.items) |*char| { - char.deinit(); - } - self.characters.deinit(); - - var itemsIter = self.items.valueIterator(); - while (itemsIter.next()) |item| { - item.deinit(); - } - self.items.deinit(); - - var mapsIter = self.maps.valueIterator(); - while (mapsIter.next()) |map| { - map.deinit(self.allocator); - } - self.maps.deinit(); - - var resourcesIter = self.resources.valueIterator(); - while (resourcesIter.next()) |resource| { - resource.deinit(self.allocator); - } - self.resources.deinit(); - - var monstersIter = self.monsters.valueIterator(); - while (monstersIter.next()) |monster| { - monster.deinit(self.allocator); - } - self.monsters.deinit(); + self.store.deinit(); } const FetchOptions = struct { @@ -354,7 +283,7 @@ fn fetchOptionalObject( } const body = json_utils.asObject(result.body.?) orelse return FetchError.ParseFailed; - return @call(.auto, parseObject, .{ self, body } ++ parseObjectArgs) catch return FetchError.ParseFailed; + return @call(.auto, parseObject, .{ &self.store, body } ++ parseObjectArgs) catch return FetchError.ParseFailed; } fn fetchObject( @@ -417,7 +346,7 @@ fn fetchOptionalArray( for (result_data.items) |result_item| { const item_obj = json_utils.asObject(result_item) orelse return FetchError.ParseFailed; - const parsed_item = @call(.auto, parseObject, .{ self, item_obj } ++ parseObjectArgs) catch return FetchError.ParseFailed; + const parsed_item = @call(.auto, parseObject, .{ &self.store, item_obj } ++ parseObjectArgs) catch return FetchError.ParseFailed; array.append(parsed_item) catch return FetchError.OutOfMemory; } @@ -459,154 +388,22 @@ pub fn setToken(self: *Server, token: ?[]const u8) !void { self.token = new_token; } -pub fn getItemId(self: *Server, code: []const u8) !ItemId { - assert(code.len != 0); - - for (0.., self.item_codes.items) |i, item_code| { - if (std.mem.eql(u8, code, item_code)) { - return @intCast(i); - } - } - - const code_dupe = try self.allocator.dupe(u8, code); - errdefer self.allocator.free(code_dupe); - try self.item_codes.append(code_dupe); - - return @intCast(self.item_codes.items.len - 1); -} - -pub fn getItemCode(self: *const Server, id: ItemId) ?[]const u8 { - if (id >= self.item_codes.items.len) { - return null; - } - - return self.item_codes.items[id]; -} - -pub fn getItemIdJson(self: *Server, object: json.ObjectMap, name: []const u8) !?ItemId { - const code = try json_utils.getStringRequired(object, name); - if (code.len == 0) { - return null; - } - - return try self.getItemId(code); -} - -fn findCharacterIndex(self: *const Server, name: []const u8) ?usize { - for (0.., self.characters.items) |i, character| { - if (std.mem.eql(u8, character.name, name)) { - return i; - } - } - - return null; -} - -fn addOrUpdateCharacter(self: *Server, character: Character) !void { - if (self.findCharacterIndex(character.name)) |found| { - self.characters.items[found].deinit(); - self.characters.items[found] = character; - } else { - try self.characters.append(character); - } -} - -pub fn findCharacter(self: *const Server, name: []const u8) ?Character { - if (self.findCharacterIndex(name)) |index| { - return self.characters.items[index]; - } - - return null; -} - -pub fn findCharacterPtr(self: *Server, name: []const u8) ?*Character { - if (self.findCharacterIndex(name)) |index| { - return &self.characters.items[index]; - } - - return null; -} - -// TODO: Remove this function -pub fn findItem(self: *const Server, name: []const u8) ?Item { - return self.items.get(name); -} - -// TODO: Remove this function -pub fn findMap(self: *const Server, position: Position) ?MapTile { - return self.maps.get(position); -} - -fn addOrUpdateItem(self: *Server, item: Item) !void { - var entry = try self.items.getOrPut(item.code); - if (entry.found_existing) { - entry.value_ptr.deinit(); - } - entry.value_ptr.* = item; -} - -fn addOrUpdateMap(self: *Server, map: MapTile) !void { - var entry = try self.maps.getOrPut(map.position); - if (entry.found_existing) { - entry.value_ptr.deinit(self.allocator); - } - entry.value_ptr.* = map; -} - -fn addOrUpdateResource(self: *Server, resource: Resource) !void { - var entry = try self.resources.getOrPut(resource.code); - if (entry.found_existing) { - entry.value_ptr.deinit(self.allocator); - } - entry.value_ptr.* = resource; -} - -fn addOrUpdateMonster(self: *Server, monster: Monster) !void { - var entry = try self.monsters.getOrPut(monster.code); - if (entry.found_existing) { - entry.value_ptr.deinit(self.allocator); - } - entry.value_ptr.* = monster; -} - pub fn prefetch(self: *Server) !void { - self.prefetched_resources = false; - self.prefetched_maps = false; - self.prefetched_monsters = false; - self.prefetched_items = false; + self.prefetched = false; - try self.prefetchResources(); - try self.prefetchMaps(); - try self.prefetchMonsters(); - try self.prefetchItems(); -} - -pub fn prefetchResources(self: *Server) !void { - var resources = try self.getResources(.{}); + const resources = try self.getResources(.{}); defer resources.deinit(); - self.prefetched_resources = true; -} - -pub fn prefetchMaps(self: *Server) !void { - var maps = try self.getMaps(.{}); + const maps = try self.getMaps(.{}); defer maps.deinit(); - self.prefetched_maps = true; -} - -pub fn prefetchMonsters(self: *Server) !void { - var monsters = try self.getMonsters(.{}); + const monsters = try self.getMonsters(.{}); defer monsters.deinit(); - self.prefetched_monsters = true; -} - -pub fn prefetchItems(self: *Server) !void { - var items = try self.getItems(.{}); + const items = try self.getItems(.{}); defer items.deinit(); - self.prefetched_items = true; + self.prefetched = true; } // ------------------------- Endpoints ------------------------ @@ -635,7 +432,7 @@ pub fn getCharacter(self: *Server, name: []const u8) FetchError!?Character { if (maybe_character) |*character| { errdefer character.deinit(); - try self.addOrUpdateCharacter(character.*); + try self.store.putCharacter(character.*); return character.*; } else { return null; @@ -653,7 +450,7 @@ pub fn listMyCharacters(self: *Server) FetchError!std.ArrayList(Character) { ); errdefer characters.deinit(); for (characters.items) |character| { - try self.addOrUpdateCharacter(character); + try self.store.putCharacter(character); } return characters; @@ -670,7 +467,7 @@ pub fn actionFight(self: *Server, name: []const u8) errors.FightError!FightResul FightResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -686,7 +483,7 @@ pub fn actionGather(self: *Server, name: []const u8) errors.GatherError!GatherRe GatherResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -705,7 +502,7 @@ pub fn actionMove(self: *Server, name: []const u8, x: i64, y: i64) errors.MoveEr MoveResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -728,7 +525,7 @@ pub fn actionBankDepositGold( GoldTransactionResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -752,7 +549,7 @@ pub fn actionBankDepositItem( ItemTransactionResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -775,7 +572,7 @@ pub fn actionBankWithdrawGold( GoldTransactionResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -799,7 +596,7 @@ pub fn actionBankWithdrawItem( ItemTransactionResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -823,7 +620,7 @@ pub fn actionCraft( CraftResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -846,7 +643,7 @@ pub fn actionUnequip( UnequipResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -870,7 +667,7 @@ pub fn actionEquip( EquipResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path, .payload = payload } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } @@ -893,13 +690,13 @@ pub fn getBankGold(self: *Server) FetchError!u64 { return @intCast(quantity); } -pub fn getBankItems(self: *Server, allocator: Allocator) FetchError!std.ArrayList(ItemIdQuantity) { +pub fn getBankItems(self: *Server, allocator: Allocator) FetchError!std.ArrayList(ItemQuantity) { return self.fetchArray( allocator, FetchError, null, - ItemIdQuantity, - ItemIdQuantity.parse, .{}, + ItemQuantity, + ItemQuantity.parse, .{}, .{ .method = .GET, .path = "/my/bank/items", .paginated = true } ); } @@ -912,8 +709,8 @@ pub fn getBankItemQuantity(self: *Server, code: []const u8) FetchError!?u64 { self.allocator, FetchError, null, - ItemIdQuantity, - ItemIdQuantity.parse, .{}, + ItemQuantity, + ItemQuantity.parse, .{}, .{ .method = .GET, .path = "/my/bank/items", .query = query, .paginated = true } ); if (maybe_items == null) { @@ -931,13 +728,11 @@ pub fn getBankItemQuantity(self: *Server, code: []const u8) FetchError!?u64 { } pub fn getMap(self: *Server, x: i64, y: i64) FetchError!?MapTile { - const position = Position.init(x, y); - - if (self.findMap(position)) |map| { + if (self.store.getMap(x, y)) |map| { return map; } - if (self.prefetched_maps) { + if (self.prefetched) { return null; } @@ -953,7 +748,7 @@ pub fn getMap(self: *Server, x: i64, y: i64) FetchError!?MapTile { ); if (result) |map| { - self.addOrUpdateMap(map); + self.store.putMap(map); } return result; @@ -965,22 +760,8 @@ pub const MapOptions = struct { }; pub fn getMaps(self: *Server, opts: MapOptions) FetchError!std.ArrayList(MapTile) { - if (self.prefetched_maps) { - var found = std.ArrayList(MapTile).init(self.allocator); - var mapIter = self.maps.valueIterator(); - while (mapIter.next()) |map| { - if (opts.type) |content_type| { - if (map.content == null) continue; - if (map.content.?.type != content_type) continue; - } - if (opts.code) |content_code| { - if (map.content == null) continue; - if (!std.mem.eql(u8, map.content.?.code, content_code)) continue; - } - - try found.append(map.*); - } - return found; + if (self.prefetched) { + return try self.store.getMaps(opts); } var query = std.ArrayList(u8).init(self.allocator); @@ -1003,18 +784,18 @@ pub fn getMaps(self: *Server, opts: MapOptions) FetchError!std.ArrayList(MapTile ); for (result.items) |map| { - try self.addOrUpdateMap(map); + try self.store.putMap(map); } return result; } pub fn getItem(self: *Server, code: []const u8) FetchError!?Item { - if (self.items.get(code)) |item| { + if (self.store.getItem(code)) |item| { return item; } - if (self.prefetched_items) { + if (self.prefetched) { return null; } @@ -1030,7 +811,7 @@ pub fn getItem(self: *Server, code: []const u8) FetchError!?Item { ); if (result) |item_with_ge| { - try self.addOrUpdateItem(item_with_ge.item); + try self.store.putItem(item_with_ge.item); return item_with_ge.item; } else { @@ -1038,8 +819,8 @@ pub fn getItem(self: *Server, code: []const u8) FetchError!?Item { } } -pub fn getItemById(self: *Server, id: ItemId) FetchError!?Item { - const code = self.getItemCode(id) orelse return null; +pub fn getItemById(self: *Server, id: Store.CodeId) FetchError!?Item { + const code = self.store.getCode(id) orelse return null; return self.getItem(code); } @@ -1053,38 +834,8 @@ pub const ItemOptions = struct { }; pub fn getItems(self: *Server, opts: ItemOptions) FetchError!std.ArrayList(Item) { - if (self.prefetched_items) { - var found = std.ArrayList(Item).init(self.allocator); - var itemIter = self.items.valueIterator(); - while (itemIter.next()) |item| { - if (opts.craft_skill) |craft_skill| { - if (item.craft == null) continue; - if (item.craft.?.skill != craft_skill) continue; - } - if (opts.craft_material) |craft_material| { - if (item.craft == null) continue; - const recipe = item.craft.?; - - const craft_material_id = try self.getItemId(craft_material); - const material_quantity = recipe.items.getQuantity(craft_material_id); - if (material_quantity == 0) continue; - } - if (opts.min_level) |min_level| { - if (item.level < min_level) continue; - } - if (opts.max_level) |max_level| { - if (item.level > max_level) continue; - } - if (opts.type) |item_type| { - if (item.type != item_type) continue; - } - if (opts.name) |name| { - if (std.mem.indexOf(u8, item.name, name) == null) continue; - } - - try found.append(item.*); - } - return found; + if (self.prefetched) { + return try self.store.getItems(opts); } var str_arena = std.heap.ArenaAllocator.init(self.allocator); @@ -1122,18 +873,18 @@ pub fn getItems(self: *Server, opts: ItemOptions) FetchError!std.ArrayList(Item) errdefer result.deinit(); for (result.items) |item| { - try self.addOrUpdateItem(item); + try self.store.putItem(item); } return result; } pub fn getResource(self: *Server, code: []const u8) FetchError!?Resource { - if (self.resources.get(code)) |resource| { + if (self.store.getResource(code)) |resource| { return resource; } - if (self.prefetched_resources) { + if (self.prefetched) { return null; } @@ -1149,7 +900,7 @@ pub fn getResource(self: *Server, code: []const u8) FetchError!?Resource { ); if (result) |resource| { - try self.addOrUpdateResource(resource); + try self.store.putResource(resource); } return result; @@ -1163,26 +914,8 @@ pub const ResourceOptions = struct { }; pub fn getResources(self: *Server, opts: ResourceOptions) FetchError!std.ArrayList(Resource) { - if (self.prefetched_resources) { - var found = std.ArrayList(Resource).init(self.allocator); - var resourceIter = self.resources.valueIterator(); - while (resourceIter.next()) |resource| { - if (opts.min_level) |min_level| { - if (resource.level < min_level) continue; - } - if (opts.max_level) |max_level| { - if (resource.level > max_level) continue; - } - if (opts.drop) |drop| { - const item_id = try self.getItemId(drop); - if (!DropRate.doesListContain(&resource.drops, item_id)) { - continue; - } - } - - try found.append(resource.*); - } - return found; + if (self.prefetched) { + return self.store.getResources(opts); } var str_arena = std.heap.ArenaAllocator.init(self.allocator); @@ -1217,18 +950,18 @@ pub fn getResources(self: *Server, opts: ResourceOptions) FetchError!std.ArrayLi errdefer result.deinit(); for (result.items) |resource| { - try self.addOrUpdateResource(resource); + try self.store.putResource(resource); } return result; } pub fn getMonster(self: *Server, code: []const u8) FetchError!?Monster { - if (self.monsters.get(code)) |monster| { + if (self.store.getMonster(code)) |monster| { return monster; } - if (self.prefetched_monsters) { + if (self.prefetched) { return null; } @@ -1244,7 +977,7 @@ pub fn getMonster(self: *Server, code: []const u8) FetchError!?Monster { ); if (result) |monster| { - try self.addOrUpdateMonster(monster); + try self.store.putMonster(monster); } return result; @@ -1257,26 +990,8 @@ pub const MonsterOptions = struct { }; pub fn getMonsters(self: *Server, opts: MonsterOptions) FetchError!std.ArrayList(Monster) { - if (self.prefetched_monsters) { - var found = std.ArrayList(Monster).init(self.allocator); - var monsterIter = self.monsters.valueIterator(); - while (monsterIter.next()) |monster| { - if (opts.min_level) |min_level| { - if (monster.level < min_level) continue; - } - if (opts.max_level) |max_level| { - if (monster.level > max_level) continue; - } - if (opts.drop) |drop| { - const item_id = try self.getItemId(drop); - if (!DropRate.doesListContain(&monster.drops, item_id)) { - continue; - } - } - - try found.append(monster.*); - } - return found; + if (self.prefetched) { + return try self.store.getMonsters(opts); } var str_arena = std.heap.ArenaAllocator.init(self.allocator); @@ -1307,24 +1022,24 @@ pub fn getMonsters(self: *Server, opts: MonsterOptions) FetchError!std.ArrayList ); for (result.items) |monster| { - try self.addOrUpdateMonster(monster); + try self.store.putMonster(monster); } return result; } -pub fn acceptTask(self: *Server, name: []const u8) errors.TaskAcceptError { +pub fn acceptTask(self: *Server, name: []const u8) errors.TaskAcceptError!AcceptTaskResult { const path = try std.fmt.allocPrint(self.allocator, "/my/{s}/action/task/new", .{name}); defer self.allocator.free(path); const result = try self.fetchObject( errors.TaskAcceptError, errors.parseTaskAcceptError, - EquipResult, - EquipResult.parse, .{ self.allocator }, + AcceptTaskResult, + AcceptTaskResult.parse, .{ self.allocator }, .{ .method = .POST, .path = path } ); - try self.addOrUpdateCharacter(result.character); + try self.store.putCharacter(result.character); return result; } diff --git a/api/store.zig b/api/store.zig new file mode 100644 index 0000000..250c03f --- /dev/null +++ b/api/store.zig @@ -0,0 +1,302 @@ +const std = @import("std"); +const json_utils = @import("json_utils.zig"); +const Server = @import("./server.zig"); +const json = std.json; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; + +const Store = @This(); + +const Character = @import("./schemas/character.zig"); +const Item = @import("./schemas/item.zig"); +const Position = @import("./position.zig"); +const Map = @import("./schemas/map.zig"); +const Resource = @import("./schemas/resource.zig"); +const Monster = @import("./schemas/monster.zig"); +const DropRate = @import("./schemas/drop_rate.zig"); + +pub const CodeId = u16; + +allocator: Allocator, +codes: std.ArrayList([]u8), +characters: std.ArrayList(Character), +items: std.StringHashMap(Item), +maps: std.AutoHashMap(Position, Map), +resources: std.StringHashMap(Resource), +monsters: std.StringHashMap(Monster), +// TODO: bank + +pub fn init(allocator: Allocator) Store { + return Store{ + .allocator = allocator, + .codes = std.ArrayList([]u8).init(allocator), + .characters = std.ArrayList(Character).init(allocator), + .items = std.StringHashMap(Item).init(allocator), + .maps = std.AutoHashMap(Position, Map).init(allocator), + .resources = std.StringHashMap(Resource).init(allocator), + .monsters = std.StringHashMap(Monster).init(allocator), + }; +} + +pub fn deinit(self: *Store) void { + for (self.codes.items) |code| { + self.allocator.free(code); + } + self.codes.deinit(); + + for (self.characters.items) |*char| { + char.deinit(); + } + self.characters.deinit(); + + var itemsIter = self.items.valueIterator(); + while (itemsIter.next()) |item| { + item.deinit(); + } + self.items.deinit(); + + var mapsIter = self.maps.valueIterator(); + while (mapsIter.next()) |map| { + map.deinit(self.allocator); + } + self.maps.deinit(); + + var resourcesIter = self.resources.valueIterator(); + while (resourcesIter.next()) |resource| { + resource.deinit(self.allocator); + } + self.resources.deinit(); + + var monstersIter = self.monsters.valueIterator(); + while (monstersIter.next()) |monster| { + monster.deinit(self.allocator); + } + self.monsters.deinit(); +} + +pub fn getCodeId(self: *Store, code: []const u8) !CodeId { + assert(code.len != 0); + + for (0.., self.codes.items) |i, item_code| { + if (std.mem.eql(u8, code, item_code)) { + return @intCast(i); + } + } + + const code_dupe = try self.allocator.dupe(u8, code); + errdefer self.allocator.free(code_dupe); + try self.codes.append(code_dupe); + + return @intCast(self.codes.items.len - 1); +} + +pub fn getCode(self: *const Store, id: CodeId) ?[]const u8 { + if (id >= self.codes.items.len) { + return null; + } + + return self.codes.items[id]; +} + +pub fn getCodeIdJson(self: *Store, object: json.ObjectMap, name: []const u8) !?CodeId { + const code = try json_utils.getStringRequired(object, name); + if (code.len == 0) { + return null; + } + + return try self.getCodeId(code); +} + +// ----------------------- Character ------------------------------ + +fn getCharacterIndex(self: *const Store, name: []const u8) ?usize { + for (0.., self.characters.items) |i, character| { + if (std.mem.eql(u8, character.name, name)) { + return i; + } + } + + return null; +} + +pub fn getCharacter(self: *Store, name: []const u8) ?Character { + if (self.getCharacterIndex(name)) |index| { + return self.characters.items[index]; + } + + return null; +} + +pub fn putCharacter(self: *Store, character: Character) !void { + if (self.getCharacterIndex(character.name)) |index| { + self.characters.items[index].deinit(); + self.characters.items[index] = character; + } else { + try self.characters.append(character); + } +} + +// ----------------------- Map ------------------------------ + +pub fn getMap(self: *Store, x: i64, y: i64) ?Map { + const pos = Position.init(x, y); + return self.maps.get(pos); +} + +pub fn getMaps(self: *Store, opts: Server.MapOptions) !std.ArrayList(Map) { + var found = std.ArrayList(Map).init(self.allocator); + errdefer found.deinit(); + + var mapIter = self.maps.valueIterator(); + while (mapIter.next()) |map| { + if (opts.type) |content_type| { + if (map.content == null) continue; + if (map.content.?.type != content_type) continue; + } + if (opts.code) |content_code| { + if (map.content == null) continue; + if (!std.mem.eql(u8, map.content.?.code, content_code)) continue; + } + + try found.append(map.*); + } + + return found; +} + +pub fn putMap(self: *Store, map: Map) !void { + var entry = try self.maps.getOrPut(map.position); + if (entry.found_existing) { + entry.value_ptr.deinit(self.allocator); + } + entry.value_ptr.* = map; +} + +// ----------------------- Item ------------------------------ + +pub fn getItem(self: *Store, code: []const u8) ?Item { + return self.items.get(code); +} + +pub fn getItems(self: *Store, opts: Server.ItemOptions) !std.ArrayList(Item) { + var found = std.ArrayList(Item).init(self.allocator); + errdefer found.deinit(); + + var itemIter = self.items.valueIterator(); + while (itemIter.next()) |item| { + if (opts.craft_skill) |craft_skill| { + if (item.craft == null) continue; + if (item.craft.?.skill != craft_skill) continue; + } + if (opts.craft_material) |craft_material| { + if (item.craft == null) continue; + const recipe = item.craft.?; + + const craft_material_id = try self.getItemId(craft_material); + const material_quantity = recipe.items.getQuantity(craft_material_id); + if (material_quantity == 0) continue; + } + if (opts.min_level) |min_level| { + if (item.level < min_level) continue; + } + if (opts.max_level) |max_level| { + if (item.level > max_level) continue; + } + if (opts.type) |item_type| { + if (item.type != item_type) continue; + } + if (opts.name) |name| { + if (std.mem.indexOf(u8, item.name, name) == null) continue; + } + + try found.append(item.*); + } + + return found; +} + +pub fn putItem(self: *Store, item: Item) !void { + var entry = try self.items.getOrPut(item.code); + if (entry.found_existing) { + entry.value_ptr.deinit(); + } + entry.value_ptr.* = item; +} + +// ----------------------- Monster ------------------------------ + +pub fn getMonster(self: *Store, code: []const u8) ?Monster { + return self.monsters.get(code); +} + +pub fn getMonsters(self: *Store, opts: Server.MonsterOptions) !std.ArrayList(Monster) { + var found = std.ArrayList(Monster).init(self.allocator); + errdefer found.deinit(); + + var monsterIter = self.monsters.valueIterator(); + while (monsterIter.next()) |monster| { + if (opts.min_level) |min_level| { + if (monster.level < min_level) continue; + } + if (opts.max_level) |max_level| { + if (monster.level > max_level) continue; + } + if (opts.drop) |drop| { + const item_id = try self.getItemId(drop); + if (!DropRate.doesListContain(&monster.drops, item_id)) { + continue; + } + } + + try found.append(monster.*); + } + + return found; +} + +pub fn putMonster(self: *Store, monster: Monster) !void { + var entry = try self.resources.getOrPut(monster.code_id); + if (entry.found_existing) { + entry.value_ptr.deinit(self.allocator); + } + entry.value_ptr.* = monster; +} + +// ----------------------- Resource ------------------------------ + +pub fn getResource(self: *Store, code: []const u8) ?Resource { + return self.resources.get(code); +} + +pub fn getResources(self: *Store, opts: Server.ResourceOptions) !std.ArrayList(Resource) { + var found = std.ArrayList(Resource).init(self.allocator); + errdefer found.deinit(); + + var resourceIter = self.resources.valueIterator(); + while (resourceIter.next()) |resource| { + if (opts.min_level) |min_level| { + if (resource.level < min_level) continue; + } + if (opts.max_level) |max_level| { + if (resource.level > max_level) continue; + } + if (opts.drop) |drop| { + const item_id = try self.getItemId(drop); + if (!DropRate.doesListContain(&resource.drops, item_id)) { + continue; + } + } + + try found.append(resource.*); + } + + return found; +} + +pub fn putResource(self: *Store, resource: Resource) !void { + var entry = try self.resources.getOrPut(resource.code); + if (entry.found_existing) { + entry.value_ptr.deinit(self.allocator); + } + entry.value_ptr.* = resource; +} diff --git a/lib/action.zig b/lib/action.zig index bb14083..ea78f94 100644 --- a/lib/action.zig +++ b/lib/action.zig @@ -9,9 +9,9 @@ pub const Action = union(enum) { fight, gather, deposit_gold: u64, - deposit_item: Server.ItemIdQuantity, - withdraw_item: Server.ItemIdQuantity, - craft_item: Server.ItemIdQuantity, + deposit_item: Api.ItemQuantity, + withdraw_item: Api.ItemQuantity, + craft_item: Api.ItemQuantity, pub fn perform(self: Action, api: *Server, name: []const u8) !ActionResult { const log = std.log.default; @@ -36,14 +36,14 @@ pub const Action = union(enum) { }; }, .deposit_item => |item| { - const code = api.getItemCode(item.id) orelse return error.ItemNotFound; + const code = api.store.getCode(item.id) orelse return error.ItemNotFound; log.debug("[{s}] deposit {s} (x{})", .{name, code, item.quantity}); return .{ .deposit_item = api.actionBankDepositItem(name, code, item.quantity) }; }, .withdraw_item => |item| { - const code = api.getItemCode(item.id) orelse return error.ItemNotFound; + const code = api.store.getCode(item.id) orelse return error.ItemNotFound; log.debug("[{s}] withdraw {s} (x{})", .{name, code, item.quantity}); return .{ .withdraw_item = api.actionBankWithdrawItem(name, code, item.quantity) @@ -56,7 +56,7 @@ pub const Action = union(enum) { }; }, .craft_item => |item| { - const code = api.getItemCode(item.id) orelse return error.ItemNotFound; + const code = api.store.getCode(item.id) orelse return error.ItemNotFound; log.debug("[{s}] craft {s} (x{})", .{name, code, item.quantity}); return .{ .craft_item = api.actionCraft(name, code, item.quantity) diff --git a/lib/brain.zig b/lib/brain.zig index 86bb76d..ad73d3f 100644 --- a/lib/brain.zig +++ b/lib/brain.zig @@ -14,6 +14,7 @@ const Brain = @This(); name: []const u8, action_queue: std.ArrayList(QueuedAction), task: ?CharacterTask = null, +paused_until: ?i64 = null, pub fn init(allocator: Allocator, name: []const u8) !Brain { return Brain{ @@ -28,17 +29,11 @@ pub fn deinit(self: Brain) void { self.action_queue.deinit(); } -fn currentTime() f64 { - const timestamp: f64 = @floatFromInt(std.time.milliTimestamp()); - return timestamp / std.time.ms_per_s; -} - pub fn performNextAction(self: *Brain, api: *Server) !void { const log = std.log.default; assert(self.action_queue.items.len > 0); - const retry_delay = 0.5; // 500ms - var character = api.findCharacterPtr(self.name).?; + const retry_delay = std.time.ns_per_ms * 500; // 500ms const next_action = self.action_queue.items[0]; const action_result = try next_action.perform(api, self.name); @@ -46,12 +41,14 @@ pub fn performNextAction(self: *Brain, api: *Server) !void { if (action_result.getErrorResponse()) |error_response| { switch (error_response) { .retry => { - character.cooldown_expiration = currentTime() + retry_delay; - log.warn("[{s}] retry withdrawing item", .{self.name}); + self.paused_until = std.time.timestamp() + retry_delay; + log.warn("[{s}] retry action", .{self.name}); + return; }, .restart => { log.warn("[{s}] clear action queue", .{self.name}); self.action_queue.clearAndFree(); + return; }, .abort => { try action_result.getError(); @@ -71,6 +68,13 @@ pub fn performNextAction(self: *Brain, api: *Server) !void { } pub fn step(self: *Brain, api: *Api.Server) !void { + if (self.paused_until) |paused_until| { + if (std.time.timestamp() < paused_until) { + return; + } + self.paused_until = null; + } + if (self.action_queue.items.len > 0) return; if (self.task) |task| { @@ -86,3 +90,14 @@ pub fn step(self: *Brain, api: *Api.Server) !void { try task.queueActions(api, self.name, &self.action_queue); } } + +pub fn cooldown(self: *Brain, api: *Server) i64 { + const character = api.store.getCharacter(self.name).?; + const cooldown_expiration: i64 = @intFromFloat(character.cooldown_expiration * std.time.ns_per_s); + + if (self.paused_until) |pause_until| { + return @max(cooldown_expiration, pause_until); + } else { + return cooldown_expiration; + } +} diff --git a/lib/root.zig b/lib/root.zig index 59ac1ee..b637680 100644 --- a/lib/root.zig +++ b/lib/root.zig @@ -66,13 +66,12 @@ pub fn step(self: *Artificer) !void { for (self.characters.items) |*brain| { if (brain.task == null) { - const character = self.server.findCharacter(brain.name).?; + const character = self.server.store.getCharacter(brain.name).?; if (character.task == null) { brain.task = .{ .accept_task = .{} }; } } - try brain.step(&self.server); } } @@ -90,9 +89,7 @@ fn earliestCooldown(characters: []Brain, api: *Api.Server) ?i64 { for (characters) |*brain| { if (brain.action_queue.items.len == 0) continue; - const character = api.findCharacter(brain.name).?; - const cooldown: i64 = @intFromFloat(character.cooldown_expiration * std.time.ns_per_s); - + const cooldown = brain.cooldown(api); if (earliest_cooldown == null or earliest_cooldown.? > cooldown) { earliest_cooldown = cooldown; } @@ -111,16 +108,9 @@ fn runNextActions(characters: []Brain, api: *Api.Server) !void { for (characters) |*brain| { if (brain.action_queue.items.len == 0) continue; - const character = api.findCharacter(brain.name).?; - const cooldown: u64 = @intFromFloat(character.cooldown_expiration * std.time.ns_per_s); - + const cooldown = brain.cooldown(api); if (earliest_cooldown > cooldown) { try brain.performNextAction(api); } } } - -fn currentTime() f64 { - const timestamp: f64 = @floatFromInt(std.time.milliTimestamp()); - return timestamp / std.time.ms_per_s; -} diff --git a/lib/task.zig b/lib/task.zig index dadb1e9..fd730c0 100644 --- a/lib/task.zig +++ b/lib/task.zig @@ -4,14 +4,14 @@ const Position = Api.Position; const Action = @import("./action.zig").Action; const ActionResult = @import("./action.zig").ActionResult; -const ItemId = Api.ItemId; -const Slot = Api.Slot; +const CodeId = Api.CodeId; +const ItemQuantity = Api.ItemQuantity; const bank_position: Position = .{ .x = 4, .y = 1 }; // TODO: Figure this out dynamically pub const UntilCondition = union(enum) { xp: u64, - item: Api.ItemIdQuantity, + item: Api.ItemQuantity, }; pub const Task = union(enum) { @@ -27,7 +27,7 @@ pub const Task = union(enum) { }, craft: struct { at: Position, - target: Api.ItemIdQuantity, + target: Api.ItemQuantity, progress: u64 = 0, }, accept_task: struct { @@ -104,8 +104,12 @@ const TaskContext = struct { name: []const u8, action_queue: *std.ArrayList(Action), + fn getCharacter(self: TaskContext) Api.Character { + return self.api.store.getCharacter(self.name).?; + } + fn moveIfNeeded(self: TaskContext, pos: Position) !bool { - const character = self.api.findCharacter(self.name).?; + const character = self.getCharacter(); if (character.position.eql(pos)) { return false; @@ -117,7 +121,7 @@ const TaskContext = struct { } pub fn depositItemsToBank(self: TaskContext) !bool { - var character = self.api.findCharacter(self.name).?; + var character = self.getCharacter(); const action_queue = self.action_queue; // Deposit items and gold to bank if full @@ -139,7 +143,7 @@ const TaskContext = struct { } fn depositIfFull(self: TaskContext) !bool { - const character = self.api.findCharacter(self.name).?; + const character = self.getCharacter(); if (character.getItemCount() < character.inventory_max_items) { return false; } @@ -177,8 +181,8 @@ const TaskContext = struct { try self.action_queue.append(.{ .gather = {} }); } - fn withdrawFromBank(self: TaskContext, items: []const Slot) !bool { - var character = self.api.findCharacter(self.name).?; + fn withdrawFromBank(self: TaskContext, items: []const ItemQuantity) !bool { + const character = self.getCharacter(); var has_all_items = true; for (items) |item_quantity| { @@ -207,8 +211,8 @@ const TaskContext = struct { return true; } - fn craftItem(self: TaskContext, workstation: Position, id: ItemId, quantity: u64) !bool { - var character = self.api.findCharacter(self.name).?; + fn craftItem(self: TaskContext, workstation: Position, id: CodeId, quantity: u64) !bool { + var character = self.getCharacter(); const inventory_quantity = character.inventory.getQuantity(id); if (inventory_quantity >= quantity) { @@ -227,8 +231,8 @@ const TaskContext = struct { return true; } - fn craftRoutine(self: TaskContext, workstation: Position, id: ItemId, quantity: u64) !void { - var character = self.api.findCharacter(self.name).?; + fn craftRoutine(self: TaskContext, workstation: Position, id: CodeId, quantity: u64) !void { + var character = self.getCharacter(); const inventory_quantity = character.inventory.getQuantity(id); if (inventory_quantity >= quantity) { if (try self.depositItemsToBank()) { @@ -236,7 +240,7 @@ const TaskContext = struct { } } - const code = self.api.getItemCode(id) orelse return error.InvalidItemId; + const code = self.api.store.getCode(id) orelse return error.InvalidItemId; const target_item = try self.api.getItem(code) orelse return error.ItemNotFound; if (target_item.craft == null) { return error.NotCraftable; diff --git a/lib/task_graph.zig b/lib/task_graph.zig index 2641351..002a93f 100644 --- a/lib/task_graph.zig +++ b/lib/task_graph.zig @@ -13,43 +13,6 @@ const TaskNode = struct { task: CharacterTask, dependencies: Dependencies = Dependencies.init(0) catch unreachable, missing_items: MissingItems = MissingItems.init(), - - pub fn format( - self: TaskNode, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - _ = options; - _ = fmt; - - switch (self.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); - // } - // for (node.additional_items.slots.constSlice()) |slot| { - // const item_code = self.api.getItemCode(slot.id).?; - // try writer.writeBytesNTimes(" ", level+1); - // try writer.print("+ {{ \"{s}\" x {} }}\n", .{item_code, slot.quantity}); - // } - } }; const Nodes = std.ArrayList(TaskNode); @@ -73,8 +36,8 @@ fn addTask(self: *TaskGraph, node: TaskNode) !TaskNodeId { return @intCast(self.nodes.items.len-1); } -fn addFightTask(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quantity: u64) !TaskNodeId { - const item_code = api.getItemCode(item_id) orelse return error.ItemNotFound; +fn addFightTask(self: *TaskGraph, api: *Api.Server, item_id: Api.CodeId, quantity: u64) !TaskNodeId { + const item_code = api.store.getCode(item_id) orelse return error.ItemNotFound; const monsters = try api.getMonsters(.{ .drop = item_code }); defer monsters.deinit(); @@ -95,14 +58,14 @@ fn addFightTask(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quantit .task = .{ .fight = .{ .at = resource_map.position, - .until = .{ .item = Api.ItemIdQuantity.init(item_id, quantity) } + .until = .{ .item = Api.ItemQuantity.init(item_id, quantity) } } } }); } -fn addGatherTask(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quantity: u64) !TaskNodeId { - const item_code = api.getItemCode(item_id) orelse return error.ItemNotFound; +fn addGatherTask(self: *TaskGraph, api: *Api.Server, item_id: Api.CodeId, quantity: u64) !TaskNodeId { + const item_code = api.store.getCode(item_id) orelse return error.ItemNotFound; const resources = try api.getResources(.{ .drop = item_code }); defer resources.deinit(); @@ -121,13 +84,13 @@ fn addGatherTask(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quanti .task = .{ .gather = .{ .at = resource_map.position, - .until = .{ .item = Api.ItemIdQuantity.init(item_id, quantity) } + .until = .{ .item = Api.ItemQuantity.init(item_id, quantity) } } } }); } -fn addCraftTaskShallow(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quantity: u64) !TaskNodeId { +fn addCraftTaskShallow(self: *TaskGraph, api: *Api.Server, item_id: Api.CodeId, quantity: u64) !TaskNodeId { const item = (try api.getItemById(item_id)) orelse return error.ItemNotFound; const recipe = item.craft orelse return error.RecipeNotFound; @@ -142,13 +105,13 @@ fn addCraftTaskShallow(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, .task = .{ .craft = .{ .at = workshop_maps.items[0].position, - .target = Api.ItemIdQuantity.init(item_id, quantity) + .target = Api.ItemQuantity.init(item_id, quantity) } } }); } -fn addCraftTask(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quantity: u64) !TaskNodeId { +fn addCraftTask(self: *TaskGraph, api: *Api.Server, item_id: Api.CodeId, quantity: u64) !TaskNodeId { const node_id = try self.addCraftTaskShallow(api, item_id, quantity); var node = self.get(node_id); @@ -171,7 +134,7 @@ fn addCraftTask(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quantit } // TODO: Remove `anyerror` from function declaration -fn addAutoTask(self: *TaskGraph, api: *Api.Server, item_id: Api.ItemId, quantity: u64) anyerror!?TaskNodeId { +fn addAutoTask(self: *TaskGraph, api: *Api.Server, item_id: Api.CodeId, quantity: u64) anyerror!?TaskNodeId { const item = (try self.api.getItemById(item_id)) orelse return error.ItemNotFound; if (item.craft != null) { @@ -209,16 +172,16 @@ fn printTaskLevel(self: *TaskGraph, api: *Api.Server, node_id: TaskNodeId, level switch (node.task) { .fight => |args| { const target_item = args.until.item; - const item = api.getItemCode(target_item.id).?; + const item = api.store.getCode(target_item.id).?; print("Task{{ .fight = {{ \"{s}\" x {} }}, .at = {} }}\n", .{item, target_item.quantity, args.at}); }, .gather => |args| { const target_item = args.until.item; - const item = api.getItemCode(target_item.id).?; + const item = api.store.getCode(target_item.id).?; print("Task{{ .gather = {{ \"{s}\" x {} }}, .at = {} }}\n", .{item, target_item.quantity, args.at}); }, .craft => |args| { - const item = api.getItemCode(args.target.id).?; + const item = api.store.getCode(args.target.id).?; print("Task{{ .craft = {{ \"{s}\" x {} }}, .at = {} }}\n", .{item, args.target.quantity, args.at}); }, }