High-level wrapper around Flecs, a powerful ECS (Entity Component System) library, written in Zig language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
5.2 KiB

const std = @import("std");
const Allocator = std.mem.Allocator;
const flecs = @import("./main.zig");
const c = flecs.c;
const err = @import("./error.zig");
const util = @import("./util.zig");
const Lookup = @import("./main.zig").Lookup;
const Entity = @import("./entity.zig").Entity;
const EntityError = @import("./entity.zig").EntityError;
const Iter = @import("./iter.zig").Iter;
pub fn World(comptime ctx: anytype) type {
return struct {
raw: *c.ecs_world_t,
const Self = @This();
pub fn init() !*Self {
std.debug.assert(flecs.is_initialized);
var result = try flecs.allocator.create(Self);
result.raw = c.ecs_init().?;
return result;
}
pub fn initWithArgs(args: [][*:0]const u8) !*Self {
std.debug.assert(flecs.is_initialized);
var result = try flecs.allocator.create(Self);
result.raw = c.ecs_init_w_args(args.len, args.ptr).?;
return result;
}
pub fn initMinimal() !*Self {
std.debug.assert(flecs.is_initialized);
var result = try flecs.allocator.create(Self);
result.raw = c.ecs_mini().?;
return result;
}
pub fn deinit(self: *Self) void {
_ = c.ecs_fini(self.raw);
flecs.allocator.destroy(self);
}
pub fn progress(self: *Self, delta_time: f32) bool {
return c.ecs_progress(self.raw, delta_time);
}
/// Returns an `Entity` for the specified `ecs_entity_t` value, or an
/// error if the entity is invalid or not alive in this `World`.
pub fn lookupAlive(self: *Self, id: c.ecs_entity_t) !Entity(ctx) {
const result = Entity(ctx).fromRaw(self, id);
try result.ensureAlive();
return result;
}
/// Returns the component `Entity` registered for the specified
/// type `T`, or an error if an association has not been made.
pub fn lookupByType(self: *Self, comptime T: type) !Entity(ctx) {
return lookupAlive(self, Lookup(ctx, T).id);
}
/// Creates a new `Entity` in this `World`.
/// See `Entity.new(...)` for more information.
pub fn entity(self: *Self, config: Entity(ctx).Config, add: anytype) !Entity(ctx) {
return Entity(ctx).new(self, config, add);
}
pub fn component(self: *Self, comptime T: type) !Entity(ctx) {
const name = util.simpleTypeName(T);
const entity2 = try self.entity(.{ .name = name, .symbol = name, .use_low_id = true }, .{});
const desc = std.mem.zeroInit(c.ecs_component_desc_t, .{
.entity = entity2.raw,
.type = .{ .size = @sizeOf(T), .alignment = @alignOf(T) },
});
var result = c.ecs_component_init(self.raw, &desc);
if (result == 0) return err.getLastErrorOrUnknown();
Lookup(ctx, T).id = result;
return Entity(ctx).fromRaw(self, result);
}
pub fn system(
self: *Self,
name: [:0]const u8,
callback: SystemCallback,
phase: anytype,
expr: [:0]const u8,
) !Entity(ctx) {
const phase2 = util.anyToEntity(ctx, phase);
const entity2 = try if (phase2 != 0)
self.entity(.{ .name = name }, .{ .{ c.EcsDependsOn, phase2 }, phase2 })
else
self.entity(.{ .name = name }, .{});
var context = try SystemCallbackContext.init(self, callback);
var desc = std.mem.zeroInit(c.ecs_system_desc_t, .{
.entity = entity2.raw,
.callback = &SystemCallbackContext.invoke,
.binding_ctx = context,
.binding_ctx_free = &SystemCallbackContext.free,
});
desc.query.filter.expr = expr;
const result = c.ecs_system_init(self.raw, &desc);
if (result == 0) return err.getLastErrorOrUnknown();
return Entity(ctx).fromRaw(self, result);
}
const SystemCallback = *const fn (Iter(ctx)) void;
const SystemCallbackContext = struct {
world: *Self,
func: SystemCallback,
pub fn init(world: *Self, callback: SystemCallback) !*SystemCallbackContext {
var result = try flecs.allocator.create(SystemCallbackContext);
result.world = world;
result.func = callback;
return result;
}
fn free(context: ?*anyopaque) callconv(.C) void {
const self: *SystemCallbackContext = @alignCast(@ptrCast(context));
flecs.allocator.destroy(self);
}
// FIXME: Dependency loop.
// Currently needs manual changing of the generated C code.
// fn invoke(it: ?*c.ecs_iter_t) callconv(.C) void {
fn invoke(it2: *anyopaque) callconv(.C) void {
const it: ?*c.ecs_iter_t = @alignCast(@ptrCast(it2));
const context: *SystemCallbackContext = @alignCast(@ptrCast(it.?.binding_ctx));
var iter = Iter(ctx).fromRawPtr(context.world, it.?);
context.func(iter);
}
};
};
}