|
|
|
@ -1,23 +1,22 @@ |
|
|
|
|
const std = @import("std"); |
|
|
|
|
const Allocator = std.mem.Allocator; |
|
|
|
|
|
|
|
|
|
const meta = @import("./meta.zig"); |
|
|
|
|
const flecs = @import("./main.zig"); |
|
|
|
|
const EntityError = flecs.EntityError; |
|
|
|
|
const Path = flecs.Path; |
|
|
|
|
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; |
|
|
|
|
const Path = @import("./path.zig").Path; |
|
|
|
|
|
|
|
|
|
pub fn World(comptime ctx: anytype) type { |
|
|
|
|
return struct { |
|
|
|
|
raw: *c.ecs_world_t, |
|
|
|
|
|
|
|
|
|
const Self = @This(); |
|
|
|
|
|
|
|
|
|
const Context = flecs.Context(ctx); |
|
|
|
|
const Entity = Context.Entity; |
|
|
|
|
const Iter = Context.Iter; |
|
|
|
|
|
|
|
|
|
raw: *c.ecs_world_t, |
|
|
|
|
|
|
|
|
|
pub fn init() !*Self { |
|
|
|
|
std.debug.assert(flecs.is_initialized); |
|
|
|
|
var result = try flecs.allocator.create(Self); |
|
|
|
@ -54,8 +53,8 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
|
|
|
|
|
/// 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); |
|
|
|
|
pub fn lookupAlive(self: *Self, id: c.ecs_entity_t) !Entity { |
|
|
|
|
const result = Entity.fromRaw(self, id); |
|
|
|
|
try result.ensureAlive(); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
@ -66,7 +65,7 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
/// Returns the `Entity` at the specified path, or an error if the |
|
|
|
|
/// entity does not exist. If the path is not absolute, the operation |
|
|
|
|
/// will use the current scope, or the world root. |
|
|
|
|
pub fn lookupByPath(self: *Self, path: Path) !Entity(ctx) { |
|
|
|
|
pub fn lookupByPath(self: *Self, path: Path) !Entity { |
|
|
|
|
var parent = if (path.absolute) 0 else c.ecs_get_scope(self.raw); |
|
|
|
|
var current: c.ecs_entity_t = undefined; |
|
|
|
|
for (path.parts) |part| { |
|
|
|
@ -80,43 +79,43 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
if (part == .id and parent != c.ecs_get_parent(self.raw, current)) return error.ParentMismatch; |
|
|
|
|
parent = current; |
|
|
|
|
} |
|
|
|
|
return Entity(ctx).fromRaw(self, current); |
|
|
|
|
return Entity.fromRaw(self, current); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 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); |
|
|
|
|
pub fn lookupByType(self: *Self, comptime T: type) !Entity { |
|
|
|
|
return lookupAlive(self, Context.lookup(T).*); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Creates or modifies an `Entity` in this `World`. |
|
|
|
|
/// See `Entity.init(...)` for more information. |
|
|
|
|
pub fn entity(self: *Self, config: Entity(ctx).Config, add: anytype) !Entity(ctx) { |
|
|
|
|
return Entity(ctx).init(self, config, add); |
|
|
|
|
pub fn entity(self: *Self, config: Entity.Config, add: anytype) !Entity { |
|
|
|
|
return Entity.init(self, config, add); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn tag(self: *Self, comptime T: type) !Entity(ctx) { |
|
|
|
|
pub fn tag(self: *Self, comptime T: type) !Entity { |
|
|
|
|
if (@sizeOf(T) > 0) @compileError("'" ++ @typeName(T) ++ "' must be a zero-sized type"); |
|
|
|
|
const name = util.simpleTypeName(T); |
|
|
|
|
const name = meta.simpleTypeName(T); |
|
|
|
|
const result = try self.entity(.{ .name = name, .symbol = name }, .{}); |
|
|
|
|
Lookup(ctx, T).id = result.raw; |
|
|
|
|
Context.lookup(T).* = result.raw; |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn component(self: *Self, comptime T: type) !Entity(ctx) { |
|
|
|
|
pub fn component(self: *Self, comptime T: type) !Entity { |
|
|
|
|
if (@sizeOf(T) == 0) @compileError("'" ++ @typeName(T) ++ "' must not be a zero-sized type"); |
|
|
|
|
const name = util.simpleTypeName(T); |
|
|
|
|
const entity2 = try self.entity(.{ .name = name, .symbol = name, .use_low_id = true }, .{}); |
|
|
|
|
const name = meta.simpleTypeName(T); |
|
|
|
|
const entity_ = try self.entity(.{ .name = name, .symbol = name, .use_low_id = true }, .{}); |
|
|
|
|
|
|
|
|
|
const desc = std.mem.zeroInit(c.ecs_component_desc_t, .{ |
|
|
|
|
.entity = entity2.raw, |
|
|
|
|
.entity = entity_.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); |
|
|
|
|
const result = c.ecs_component_init(self.raw, &desc); |
|
|
|
|
if (result == 0) return flecs.getLastErrorOrUnknown(); |
|
|
|
|
Context.lookup(T).* = result; |
|
|
|
|
return Entity.fromRaw(self, result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Registers a singleton component of type `T` with the specified value. |
|
|
|
@ -129,7 +128,7 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
/// Use `get()` and `set()` to get and set the value of this singleton. |
|
|
|
|
/// |
|
|
|
|
/// Returns the created component entity. |
|
|
|
|
pub fn singleton(self: *Self, comptime T: type, value: T) !Entity(ctx) { |
|
|
|
|
pub fn singleton(self: *Self, comptime T: type, value: T) !Entity { |
|
|
|
|
const single = try component(self, T); |
|
|
|
|
single.set(T, value); |
|
|
|
|
return single; |
|
|
|
@ -160,16 +159,16 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
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 }) |
|
|
|
|
) !Entity { |
|
|
|
|
const phase_ = Context.anyToEntity(phase); |
|
|
|
|
const entity_ = try if (phase_ != 0) |
|
|
|
|
self.entity(.{ .name = name }, .{ .{ c.EcsDependsOn, phase_ }, phase_ }) |
|
|
|
|
else |
|
|
|
|
self.entity(.{ .name = name }, .{}); |
|
|
|
|
|
|
|
|
|
var context = try SystemCallbackContext.init(self, callback); |
|
|
|
|
const context = try SystemCallbackContext.init(self, callback); |
|
|
|
|
var desc = std.mem.zeroInit(c.ecs_system_desc_t, .{ |
|
|
|
|
.entity = entity2.raw, |
|
|
|
|
.entity = entity_.raw, |
|
|
|
|
.callback = &SystemCallbackContext.invoke, |
|
|
|
|
.binding_ctx = context, |
|
|
|
|
.binding_ctx_free = &SystemCallbackContext.free, |
|
|
|
@ -177,11 +176,11 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
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); |
|
|
|
|
if (result == 0) return flecs.getLastErrorOrUnknown(); |
|
|
|
|
return Entity.fromRaw(self, result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const SystemCallback = *const fn (Iter(ctx)) void; |
|
|
|
|
const SystemCallback = *const fn (Iter) void; |
|
|
|
|
const SystemCallbackContext = struct { |
|
|
|
|
world: *Self, |
|
|
|
|
func: SystemCallback, |
|
|
|
@ -204,7 +203,7 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
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.?); |
|
|
|
|
const iter = Iter.fromRawPtr(context.world, it.?); |
|
|
|
|
context.func(iter); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
@ -212,7 +211,7 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
/// Creates a term iterator which allows querying for all entities |
|
|
|
|
/// that have a specific `Id`. This function supports wildcards. |
|
|
|
|
pub fn term(self: *Self, id: anytype) TermIterator { |
|
|
|
|
const id_ = util.anyToId(ctx, id); |
|
|
|
|
const id_ = Context.anyToId(id); |
|
|
|
|
var term_ = std.mem.zeroInit(c.ecs_term_t, .{ .id = id_ }); |
|
|
|
|
const iter = c.ecs_term_iter(self.raw, &term_); |
|
|
|
|
return .{ .world = self, .iter = iter }; |
|
|
|
@ -221,18 +220,18 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
// TODO: Move this logic to `Iter`, add `TermIter` type? |
|
|
|
|
// TODO: Rename `Iter` to `Iterator`? |
|
|
|
|
pub const TermIterator = struct { |
|
|
|
|
world: *World(ctx), |
|
|
|
|
world: *World, |
|
|
|
|
iter: c.ecs_iter_t, |
|
|
|
|
index: i32 = 0, |
|
|
|
|
|
|
|
|
|
pub fn next(self: *TermIterator) ?Entity(ctx) { |
|
|
|
|
pub fn next(self: *TermIterator) ?Entity { |
|
|
|
|
if (self.index >= self.iter.count) |
|
|
|
|
if (!c.ecs_term_next(&self.iter)) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
const result = self.iter.entities[@intCast(self.index)]; |
|
|
|
|
self.index += 1; |
|
|
|
|
return Entity(ctx).fromRaw(self.world, result); |
|
|
|
|
return Entity.fromRaw(self.world, result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: Check where else in the codebase it would make sense to have `deinit` take a pointer? |
|
|
|
@ -245,9 +244,9 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/// Gets the current scope of this `World`. See also: `setScope()`. |
|
|
|
|
pub fn getScope(self: *Self) ?Entity(ctx) { |
|
|
|
|
pub fn getScope(self: *Self) ?Entity { |
|
|
|
|
const result = c.ecs_get_scope(self.raw); |
|
|
|
|
return if (result != 0) Entity(ctx).fromRaw(self, result) else null; |
|
|
|
|
return if (result != 0) Entity.fromRaw(self, result) else null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the current scope of this `World` to the specified entity. |
|
|
|
@ -259,9 +258,9 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
/// Returns the previously set scope, if any. It's recommended to set |
|
|
|
|
/// the scope back to the previous value after you're done operating |
|
|
|
|
/// in the desired scope. |
|
|
|
|
pub fn setScope(self: *Self, value: anytype) ?Entity(ctx) { |
|
|
|
|
const result = c.ecs_set_scope(self.raw, util.anyToEntity(ctx, value)); |
|
|
|
|
return if (result != 0) Entity(ctx).fromRaw(self, result) else null; |
|
|
|
|
pub fn setScope(self: *Self, value: anytype) ?Entity { |
|
|
|
|
const result = c.ecs_set_scope(self.raw, Context.anyToEntity(value)); |
|
|
|
|
return if (result != 0) Entity.fromRaw(self, result) else null; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|