add cached prefetch
This commit is contained in:
parent
429b1dcd6e
commit
f5a4ba4503
@ -4,6 +4,7 @@ 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 ServerStatus = @import("./schemas/status.zig");
|
||||
pub const Map = @import("./schemas/map.zig");
|
||||
pub const Position = @import("position.zig");
|
||||
pub const BoundedSlotsArray = @import("schemas/slot_array.zig").BoundedSlotsArray;
|
||||
|
@ -27,7 +27,7 @@ pub fn parse(store: *Store, obj: json.ObjectMap) !DropRate {
|
||||
}
|
||||
|
||||
const code_str = try json_utils.getStringRequired(obj, "code");
|
||||
const item_id = try store.getItemId(code_str);
|
||||
const item_id = try store.getCodeId(code_str);
|
||||
|
||||
return DropRate{
|
||||
.item_id = item_id,
|
||||
|
@ -38,9 +38,8 @@ pub const TypeUtils = EnumStringUtils(Type, .{
|
||||
.{ "currency" , .currency },
|
||||
});
|
||||
|
||||
allocator: Allocator,
|
||||
name: []u8,
|
||||
code: []u8,
|
||||
code_id: Store.CodeId,
|
||||
level: u64,
|
||||
type: Type,
|
||||
subtype: []u8,
|
||||
@ -56,9 +55,8 @@ pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Item {
|
||||
const item_type_str = try json_utils.getStringRequired(obj, "type");
|
||||
|
||||
return Item{
|
||||
.allocator = allocator,
|
||||
.name = (try json_utils.dupeString(allocator, obj, "name")) orelse return error.MissingProperty,
|
||||
.code = (try json_utils.dupeString(allocator, obj, "code")) orelse return error.MissingProperty,
|
||||
.code_id = (try store.getCodeIdJson(obj, "code")) orelse return error.MissingProperty,
|
||||
.level = @intCast(level),
|
||||
.type = TypeUtils.fromString(item_type_str) orelse return error.InvalidType,
|
||||
.subtype = (try json_utils.dupeString(allocator, obj, "subtype")) orelse return error.MissingProperty,
|
||||
@ -67,9 +65,8 @@ pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Item {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Item) void {
|
||||
self.allocator.free(self.name);
|
||||
self.allocator.free(self.code);
|
||||
self.allocator.free(self.subtype);
|
||||
self.allocator.free(self.description);
|
||||
pub fn deinit(self: Item, allocator: Allocator) void {
|
||||
allocator.free(self.name);
|
||||
allocator.free(self.subtype);
|
||||
allocator.free(self.description);
|
||||
}
|
||||
|
@ -59,10 +59,10 @@ pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Monster
|
||||
}
|
||||
|
||||
return Monster{
|
||||
.name = try json_utils.dupeStringRequired(allocator, obj, "name"),
|
||||
.code = (try store.getCodeIdJson(allocator, obj, "code")) orelse return error.MissingProperty,
|
||||
.level = @intCast(level),
|
||||
.hp = @intCast(hp),
|
||||
.name = try json_utils.dupeStringRequired(allocator, obj, "name"),
|
||||
.code_id = (try store.getCodeIdJson(obj, "code")) orelse return error.MissingProperty,
|
||||
.level = @intCast(level),
|
||||
.hp = @intCast(hp),
|
||||
|
||||
.fire = try ElementalStats.parse(obj, "attack_fire" , "res_fire" ),
|
||||
.earth = try ElementalStats.parse(obj, "attack_earth", "res_earth"),
|
||||
|
@ -38,11 +38,11 @@ pub fn parse(store: *Store, obj: json.ObjectMap, allocator: Allocator) !Resource
|
||||
const skill_str = try json_utils.getStringRequired(obj, "skill");
|
||||
|
||||
return Resource{
|
||||
.name = try json_utils.dupeStringRequired(allocator, obj, "name"),
|
||||
.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(store, drops_array)
|
||||
.name = try json_utils.dupeStringRequired(allocator, obj, "name"),
|
||||
.code_id = (try store.getCodeIdJson(obj, "code")) orelse return error.MissingProperty,
|
||||
.level = @intCast(level),
|
||||
.skill = SkillUtils.fromString(skill_str) orelse return error.InvalidSkill,
|
||||
.drops = try DropRate.parseList(store, drops_array)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -407,6 +407,26 @@ pub fn prefetch(self: *Server) !void {
|
||||
self.prefetched = true;
|
||||
}
|
||||
|
||||
pub fn prefetchCached(self: *Server, cache_path: []const u8) !void {
|
||||
var status: ServerStatus = try self.getServerStatus();
|
||||
defer status.deinit();
|
||||
|
||||
if (std.fs.openFileAbsolute(cache_path, .{})) |file| {
|
||||
defer file.close();
|
||||
|
||||
if (self.store.load(status.version, file.reader())) {
|
||||
self.prefetched = true;
|
||||
return;
|
||||
} else |_| { }
|
||||
} else |_| { }
|
||||
|
||||
try self.prefetch();
|
||||
|
||||
const file = try std.fs.createFileAbsolute(cache_path, .{});
|
||||
defer file.close();
|
||||
try self.store.save(status.version, file.writer());
|
||||
}
|
||||
|
||||
// ------------------------- Endpoints ------------------------
|
||||
|
||||
pub fn getServerStatus(self: *Server) FetchError!ServerStatus {
|
||||
|
200
api/store.zig
200
api/store.zig
@ -1,6 +1,7 @@
|
||||
const std = @import("std");
|
||||
const json_utils = @import("json_utils.zig");
|
||||
const Server = @import("./server.zig");
|
||||
const s2s = @import("s2s");
|
||||
const json = std.json;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
@ -17,61 +18,48 @@ const DropRate = @import("./schemas/drop_rate.zig");
|
||||
|
||||
pub const CodeId = u16;
|
||||
|
||||
const Characters = std.ArrayList(Character);
|
||||
const ItemsMap = std.StringHashMap(Item);
|
||||
const MapsMap = std.AutoHashMap(Position, Map);
|
||||
const ResourcesMap = std.StringHashMap(Resource);
|
||||
const MonstersMap = std.StringHashMap(Monster);
|
||||
|
||||
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),
|
||||
characters: Characters,
|
||||
items: ItemsMap,
|
||||
maps: MapsMap,
|
||||
resources: ResourcesMap,
|
||||
monsters: MonstersMap,
|
||||
// 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),
|
||||
.characters = Characters.init(allocator),
|
||||
.items = ItemsMap.init(allocator),
|
||||
.maps = MapsMap.init(allocator),
|
||||
.resources = ResourcesMap.init(allocator),
|
||||
.monsters = MonstersMap.init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Store) void {
|
||||
for (self.codes.items) |code| {
|
||||
self.allocator.free(code);
|
||||
}
|
||||
self.codes.deinit();
|
||||
self.clearCodes();
|
||||
|
||||
for (self.characters.items) |*char| {
|
||||
char.deinit();
|
||||
}
|
||||
self.characters.deinit();
|
||||
|
||||
var itemsIter = self.items.valueIterator();
|
||||
while (itemsIter.next()) |item| {
|
||||
item.deinit();
|
||||
}
|
||||
self.items.deinit();
|
||||
self.clearItems();
|
||||
|
||||
var mapsIter = self.maps.valueIterator();
|
||||
while (mapsIter.next()) |map| {
|
||||
map.deinit(self.allocator);
|
||||
}
|
||||
self.maps.deinit();
|
||||
self.clearMaps();
|
||||
|
||||
var resourcesIter = self.resources.valueIterator();
|
||||
while (resourcesIter.next()) |resource| {
|
||||
resource.deinit(self.allocator);
|
||||
}
|
||||
self.resources.deinit();
|
||||
self.clearResources();
|
||||
|
||||
var monstersIter = self.monsters.valueIterator();
|
||||
while (monstersIter.next()) |monster| {
|
||||
monster.deinit(self.allocator);
|
||||
}
|
||||
self.monsters.deinit();
|
||||
self.clearMonsters();
|
||||
}
|
||||
|
||||
pub fn getCodeId(self: *Store, code: []const u8) !CodeId {
|
||||
@ -107,6 +95,101 @@ pub fn getCodeIdJson(self: *Store, object: json.ObjectMap, name: []const u8) !?C
|
||||
return try self.getCodeId(code);
|
||||
}
|
||||
|
||||
fn clearCodes(self: *Store) void {
|
||||
for (self.codes.items) |code| {
|
||||
self.allocator.free(code);
|
||||
}
|
||||
self.codes.clearAndFree();
|
||||
}
|
||||
|
||||
// ----------------------- Storing to file ------------------------------
|
||||
|
||||
const SaveData = struct {
|
||||
api_version: []const u8,
|
||||
|
||||
codes: [][]u8,
|
||||
items: []Item,
|
||||
maps: []Map,
|
||||
resources: []Resource,
|
||||
monsters: []Monster,
|
||||
};
|
||||
|
||||
fn allocHashMapValues(Value: type, allocator: Allocator, hashmap: anytype) ![]Value {
|
||||
var values = try allocator.alloc(Value, hashmap.count());
|
||||
errdefer allocator.free(values);
|
||||
|
||||
var valueIter = hashmap.valueIterator();
|
||||
var index: usize = 0;
|
||||
while (valueIter.next()) |value| {
|
||||
values[index] = value.*;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
pub fn save(self: *Store, api_version: []const u8, writer: anytype) !void {
|
||||
const items = try allocHashMapValues(Item, self.allocator, self.items);
|
||||
defer self.allocator.free(items);
|
||||
|
||||
const maps = try allocHashMapValues(Map, self.allocator, self.maps);
|
||||
defer self.allocator.free(maps);
|
||||
|
||||
const resources = try allocHashMapValues(Resource, self.allocator, self.resources);
|
||||
defer self.allocator.free(resources);
|
||||
|
||||
const monsters = try allocHashMapValues(Monster, self.allocator, self.monsters);
|
||||
defer self.allocator.free(monsters);
|
||||
|
||||
const data = SaveData{
|
||||
.api_version = api_version,
|
||||
.codes = self.codes.items,
|
||||
.items = items,
|
||||
.maps = maps,
|
||||
.resources = resources,
|
||||
.monsters = monsters
|
||||
};
|
||||
|
||||
try s2s.serialize(writer, SaveData, data);
|
||||
}
|
||||
|
||||
pub fn load(self: *Store, api_version: []const u8, reader: anytype) !void {
|
||||
var data = try s2s.deserializeAlloc(reader, SaveData, self.allocator);
|
||||
if (!std.mem.eql(u8, data.api_version, api_version)) {
|
||||
s2s.free(self.allocator, SaveData, &data);
|
||||
return error.InvalidVersion;
|
||||
}
|
||||
defer self.allocator.free(data.api_version);
|
||||
|
||||
self.clearCodes();
|
||||
try self.codes.appendSlice(data.codes);
|
||||
defer self.allocator.free(data.codes);
|
||||
|
||||
self.clearItems();
|
||||
for (data.items) |item| {
|
||||
try self.putItem(item);
|
||||
}
|
||||
defer self.allocator.free(data.items);
|
||||
|
||||
self.clearMaps();
|
||||
for (data.maps) |map| {
|
||||
try self.putMap(map);
|
||||
}
|
||||
defer self.allocator.free(data.maps);
|
||||
|
||||
self.clearResources();
|
||||
for (data.resources) |resource| {
|
||||
try self.putResource(resource);
|
||||
}
|
||||
defer self.allocator.free(data.resources);
|
||||
|
||||
self.clearMonsters();
|
||||
for (data.monsters) |monster| {
|
||||
try self.putMonster(monster);
|
||||
}
|
||||
defer self.allocator.free(data.monsters);
|
||||
}
|
||||
|
||||
// ----------------------- Character ------------------------------
|
||||
|
||||
fn getCharacterIndex(self: *const Store, name: []const u8) ?usize {
|
||||
@ -174,6 +257,14 @@ pub fn putMap(self: *Store, map: Map) !void {
|
||||
entry.value_ptr.* = map;
|
||||
}
|
||||
|
||||
fn clearMaps(self: *Store) void {
|
||||
var mapsIter = self.maps.valueIterator();
|
||||
while (mapsIter.next()) |map| {
|
||||
map.deinit(self.allocator);
|
||||
}
|
||||
self.maps.clearAndFree();
|
||||
}
|
||||
|
||||
// ----------------------- Item ------------------------------
|
||||
|
||||
pub fn getItem(self: *Store, code: []const u8) ?Item {
|
||||
@ -194,7 +285,7 @@ pub fn getItems(self: *Store, opts: Server.ItemOptions) !std.ArrayList(Item) {
|
||||
if (item.craft == null) continue;
|
||||
const recipe = item.craft.?;
|
||||
|
||||
const craft_material_id = try self.getItemId(craft_material);
|
||||
const craft_material_id = try self.getCodeId(craft_material);
|
||||
const material_quantity = recipe.items.getQuantity(craft_material_id);
|
||||
if (material_quantity == 0) continue;
|
||||
}
|
||||
@ -218,13 +309,22 @@ pub fn getItems(self: *Store, opts: Server.ItemOptions) !std.ArrayList(Item) {
|
||||
}
|
||||
|
||||
pub fn putItem(self: *Store, item: Item) !void {
|
||||
var entry = try self.items.getOrPut(item.code);
|
||||
const code = self.getCode(item.code_id).?;
|
||||
var entry = try self.items.getOrPut(code);
|
||||
if (entry.found_existing) {
|
||||
entry.value_ptr.deinit();
|
||||
entry.value_ptr.deinit(self.allocator);
|
||||
}
|
||||
entry.value_ptr.* = item;
|
||||
}
|
||||
|
||||
fn clearItems(self: *Store) void {
|
||||
var itemsIter = self.items.valueIterator();
|
||||
while (itemsIter.next()) |item| {
|
||||
item.deinit(self.allocator);
|
||||
}
|
||||
self.items.clearAndFree();
|
||||
}
|
||||
|
||||
// ----------------------- Monster ------------------------------
|
||||
|
||||
pub fn getMonster(self: *Store, code: []const u8) ?Monster {
|
||||
@ -244,7 +344,7 @@ pub fn getMonsters(self: *Store, opts: Server.MonsterOptions) !std.ArrayList(Mon
|
||||
if (monster.level > max_level) continue;
|
||||
}
|
||||
if (opts.drop) |drop| {
|
||||
const item_id = try self.getItemId(drop);
|
||||
const item_id = try self.getCodeId(drop);
|
||||
if (!DropRate.doesListContain(&monster.drops, item_id)) {
|
||||
continue;
|
||||
}
|
||||
@ -257,13 +357,22 @@ pub fn getMonsters(self: *Store, opts: Server.MonsterOptions) !std.ArrayList(Mon
|
||||
}
|
||||
|
||||
pub fn putMonster(self: *Store, monster: Monster) !void {
|
||||
var entry = try self.resources.getOrPut(monster.code_id);
|
||||
const code = self.getCode(monster.code_id).?;
|
||||
var entry = try self.monsters.getOrPut(code);
|
||||
if (entry.found_existing) {
|
||||
entry.value_ptr.deinit(self.allocator);
|
||||
}
|
||||
entry.value_ptr.* = monster;
|
||||
}
|
||||
|
||||
fn clearMonsters(self: *Store) void {
|
||||
var monstersIter = self.monsters.valueIterator();
|
||||
while (monstersIter.next()) |monster| {
|
||||
monster.deinit(self.allocator);
|
||||
}
|
||||
self.monsters.clearAndFree();
|
||||
}
|
||||
|
||||
// ----------------------- Resource ------------------------------
|
||||
|
||||
pub fn getResource(self: *Store, code: []const u8) ?Resource {
|
||||
@ -283,7 +392,7 @@ pub fn getResources(self: *Store, opts: Server.ResourceOptions) !std.ArrayList(R
|
||||
if (resource.level > max_level) continue;
|
||||
}
|
||||
if (opts.drop) |drop| {
|
||||
const item_id = try self.getItemId(drop);
|
||||
const item_id = try self.getCodeId(drop);
|
||||
if (!DropRate.doesListContain(&resource.drops, item_id)) {
|
||||
continue;
|
||||
}
|
||||
@ -296,9 +405,18 @@ pub fn getResources(self: *Store, opts: Server.ResourceOptions) !std.ArrayList(R
|
||||
}
|
||||
|
||||
pub fn putResource(self: *Store, resource: Resource) !void {
|
||||
var entry = try self.resources.getOrPut(resource.code);
|
||||
const code = self.getCode(resource.code_id).?;
|
||||
var entry = try self.resources.getOrPut(code);
|
||||
if (entry.found_existing) {
|
||||
entry.value_ptr.deinit(self.allocator);
|
||||
}
|
||||
entry.value_ptr.* = resource;
|
||||
}
|
||||
|
||||
fn clearResources(self: *Store) void {
|
||||
var resourcesIter = self.resources.valueIterator();
|
||||
while (resourcesIter.next()) |resource| {
|
||||
resource.deinit(self.allocator);
|
||||
}
|
||||
self.resources.clearAndFree();
|
||||
}
|
||||
|
@ -9,6 +9,11 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
var api: *Module = undefined;
|
||||
{
|
||||
const s2s_dep = b.dependency("s2s", .{
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
api = b.createModule(.{
|
||||
.root_source_file = b.path("api/root.zig"),
|
||||
.target = target,
|
||||
@ -17,6 +22,7 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
api.addIncludePath(b.path("api/date_time"));
|
||||
api.addCSourceFile(.{ .file = b.path("api/date_time/timegm.c") });
|
||||
api.addImport("s2s", s2s_dep.module("s2s"));
|
||||
}
|
||||
|
||||
var lib: *Module = undefined;
|
||||
@ -47,6 +53,7 @@ pub fn build(b: *std.Build) void {
|
||||
.optimize = optimize,
|
||||
});
|
||||
cli.root_module.addImport("artificer", lib);
|
||||
cli.root_module.addImport("artifacts-api", api);
|
||||
b.installArtifact(cli);
|
||||
|
||||
const run_cmd = b.addRunArtifact(cli);
|
||||
|
@ -3,10 +3,14 @@
|
||||
.version = "0.1.0",
|
||||
.minimum_zig_version = "0.12.0",
|
||||
.dependencies = .{
|
||||
.@"raylib-zig" = .{
|
||||
.url = "https://github.com/Not-Nik/raylib-zig/archive/43d15b05c2b97cab30103fa2b46cff26e91619ec.tar.gz",
|
||||
.hash = "12204a223b19043e17b79300413d02f60fc8004c0d9629b8d8072831e352a78bf212"
|
||||
},
|
||||
.@"raylib-zig" = .{
|
||||
.url = "https://github.com/Not-Nik/raylib-zig/archive/43d15b05c2b97cab30103fa2b46cff26e91619ec.tar.gz",
|
||||
.hash = "12204a223b19043e17b79300413d02f60fc8004c0d9629b8d8072831e352a78bf212"
|
||||
},
|
||||
.s2s = .{
|
||||
.url = "https://github.com/ziglibs/s2s/archive/b30205d5e9204899fb6d0fdf28d00ed4d18fe9c9.tar.gz",
|
||||
.hash = "12202c39c98f05041f1052c268132669dbfcda87e4dbb0353cd84a6070924c8ac0e3",
|
||||
},
|
||||
},
|
||||
.paths = .{ "" },
|
||||
.paths = .{""},
|
||||
}
|
||||
|
15
cli/main.zig
15
cli/main.zig
@ -1,8 +1,10 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Artificer = @import("artificer");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const Artificer = @import("artificer");
|
||||
const Api = @import("artifacts-api");
|
||||
|
||||
fn getAPITokenFromArgs(allocator: Allocator) !?[]u8 {
|
||||
const args = try std.process.argsAlloc(allocator);
|
||||
defer std.process.argsFree(allocator, args);
|
||||
@ -30,11 +32,13 @@ pub fn main() !void {
|
||||
var artificer = try Artificer.init(allocator, token);
|
||||
defer artificer.deinit();
|
||||
|
||||
if (builtin.mode != .Debug) {
|
||||
std.log.info("Prefetching server data", .{});
|
||||
try artificer.server.prefetch();
|
||||
}
|
||||
const cache_path = try std.fs.cwd().realpathAlloc(allocator, "api-store.bin");
|
||||
defer allocator.free(cache_path);
|
||||
|
||||
std.log.info("Prefetching server data", .{});
|
||||
try artificer.server.prefetchCached(cache_path);
|
||||
|
||||
if (false) {
|
||||
std.log.info("Starting main loop", .{});
|
||||
while (true) {
|
||||
const waitUntil = artificer.nextStepAt();
|
||||
@ -45,4 +49,5 @@ pub fn main() !void {
|
||||
|
||||
try artificer.step();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,12 @@ pub fn main() anyerror!void {
|
||||
var artificer = try Artificer.init(allocator, token);
|
||||
defer artificer.deinit();
|
||||
|
||||
const cache_path = try std.fs.cwd().realpathAlloc(allocator, "api-store.bin");
|
||||
defer allocator.free(cache_path);
|
||||
|
||||
std.log.info("Prefetching server data", .{});
|
||||
try artificer.server.prefetchCached(cache_path);
|
||||
|
||||
rl.initWindow(800, 450, "Artificer");
|
||||
defer rl.closeWindow();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user