NYXA/src/world/query.zig

141 lines
4.3 KiB
Zig
Raw Normal View History

2026-07-09 22:30:04 +01:00
const std = @import("std");
const World = @import("world.zig");
const Archetype = @import("archetype.zig");
const QueryFilters = @import("query_filters.zig");
const TypeID = @import("../template/type_id.zig").of;
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn Query(
comptime Result: type,
comptime Filters: QueryFilters,
) type {
return struct {
const Self = @This();
const result_fields = std.meta.fields(Result);
//
// FIELDS
//
world: *World,
arch_cursor: usize,
row_cursor: usize,
current_arch: ?*Archetype,
current_row: usize,
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn init(world: *World) Self {
return .{
.world = world,
.arch_cursor = 0,
.row_cursor = 0,
.current_arch = null,
.current_row = 0,
};
}
/// ----------------------------------------------------
/// ----------------------------------------------------
fn matches(arch: *Archetype) bool {
// --- REQUIRED COMPONENTS ---
inline for (result_fields) |field| {
// TODO: Clean this shit up
const FieldT = field.type;
const is_optional = @typeInfo(FieldT) == .optional;
const PtrT = if (is_optional) @typeInfo(FieldT).optional.child else FieldT;
const T = std.meta.Child(PtrT);
if (!arch.type_map.contains(TypeID(T))) {
// --- SKIP OPTIONALS ---
if (!is_optional) {
return false;
}
}
}
// --- WITH ---
inline for (Filters.with) |T| {
if (!arch.type_map.contains(TypeID(T))) {
return false;
}
}
// --- WITHOUT ---
inline for (Filters.without) |T| {
if (arch.type_map.contains(TypeID(T))) {
return false;
}
}
return true;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn next(self: *Self) ?Result {
while (self.arch_cursor < self.world.archetypes.items.len) {
var arch = &self.world.archetypes.items[self.arch_cursor];
// --- SKIP ARCHETYPES ---
if (!matches(arch)) {
self.arch_cursor += 1;
self.row_cursor = 0;
continue;
}
// --- END ROWS ---
if (self.row_cursor >= arch.entities.items.len) {
self.arch_cursor += 1;
self.row_cursor = 0;
continue;
}
const row = self.row_cursor;
self.row_cursor += 1;
self.current_arch = arch;
self.current_row = row;
var result: Result = undefined;
inline for (result_fields) |field| {
// TODO: Clean this shit up
const FieldT = field.type;
const is_optional = @typeInfo(FieldT) == .optional;
const PtrT = if (is_optional) @typeInfo(FieldT).optional.child else FieldT;
const T = std.meta.Child(PtrT);
const ptr = arch.get(T, row);
if (is_optional) {
@field(result, field.name) = ptr;
} else {
@field(result, field.name) = ptr orelse return null;
}
}
return result;
}
return null;
}
/// ----------------------------------------------------
/// ----------------------------------------------------
pub fn entity(self: *const Self) u64 {
return self.current_arch.?.entities.items[
self.current_row
];
}
};
}