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); } }; }; }