Compare commits

...

2 Commits

  1. 98
      src/entity.zig
  2. 28
      src/meta.zig

@ -1,6 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const meta = @import("./meta.zig");
const flecs = @import("./main.zig");
const Path = flecs.Path;
const c = flecs.c;
@ -84,10 +85,10 @@ pub fn Entity(comptime ctx: anytype) type {
/// To set their values you'll need to use `set(...)` on the
/// `Entity` returned from this function.
pub fn init(world: *World, config: Config, ids: anytype) !Self {
const meta = @typeInfo(@TypeOf(ids));
if (meta != .Struct or (meta.Struct.is_tuple == false and meta.Struct.fields.len > 0))
const info = @typeInfo(@TypeOf(ids));
if (info != .Struct or (info.Struct.is_tuple == false and info.Struct.fields.len > 0))
@compileError("Expected tuple or empty struct, got '" ++ @typeName(@TypeOf(ids)) ++ "'");
if (meta.Struct.fields.len > c.FLECS_ID_DESC_MAX)
if (info.Struct.fields.len > c.FLECS_ID_DESC_MAX)
@compileError("Adding more than FLECS_ID_DESC_MAX ids");
var id = if (config.id) |i| i.raw else null;
@ -303,7 +304,7 @@ pub fn Entity(comptime ctx: anytype) type {
/// Gets a value copy of a component.
/// If the component does not exist, returns null.
pub fn get(self: Self, comptime T: type) ?T {
pub fn get(self: Self, comptime T: anytype) ?meta.AnyToType(T) {
return if (getRef(self, T)) |p| p.* else null;
}
@ -313,8 +314,8 @@ pub fn Entity(comptime ctx: anytype) type {
/// The returned pointer may become invalid after calling other Flecs
/// functions, notably when an `Entity` moves to another table caused
/// by adding or removing other components.
pub fn getRef(self: Self, comptime T: type) ?*const T {
const id = Context.lookup(T).*;
pub fn getRef(self: Self, comptime T: anytype) ?*const meta.AnyToType(T) {
const id = Context.anyToId(T);
const ptr = c.ecs_get_id(self.world.raw, self.raw, id);
return @alignCast(@ptrCast(ptr));
}
@ -329,15 +330,90 @@ pub fn Entity(comptime ctx: anytype) type {
/// If `getMut` is called when the world is in deferred / readonly
/// mode, and the component does not yet exist, it will return a
/// pointer to a temp storage.
pub fn getMut(self: Self, comptime T: type) *T {
const id = Context.lookup(T).*;
pub fn getMut(self: Self, comptime T: anytype) *meta.AnyToType(T) {
const id = Context.anyToId(T);
const ptr = c.ecs_get_mut_id(self.world.raw, self.raw, id);
return @alignCast(@ptrCast(ptr));
}
pub fn set(self: Self, comptime T: type, value: T) void {
const id = Context.lookup(T).*;
_ = c.ecs_set_id(self.world.raw, self.raw, id, @sizeOf(T), &value);
pub fn set(self: Self, comptime T: anytype, value: meta.AnyToType(T)) void {
const id = Context.anyToId(T);
_ = c.ecs_set_id(self.world.raw, self.raw, id, @sizeOf(@TypeOf(value)), &value);
}
};
}
const expect = @import("./test/expect.zig");
test "Entity get and set" {
flecs.init(std.testing.allocator);
var world = try flecs.World(void).initMinimal();
defer world.deinit();
const Position = struct { x: f32, y: f32 };
const Velocity = struct { x: f32, y: f32 };
_ = try world.component(Position);
_ = try world.component(Velocity);
const entity = try world.entity(.{}, .{});
entity.set(Position, .{ .x = 10, .y = 20 });
entity.set(Velocity, .{ .x = 1, .y = 2 });
const pos = entity.get(Position).?;
const vel = entity.get(Velocity).?;
entity.set(Position, .{ .x = pos.x + vel.x, .y = pos.y + vel.y });
try expect.equal(.{ .x = 11, .y = 22 }, entity.get(Position).?);
}
test "Entity getMut" {
flecs.init(std.testing.allocator);
var world = try flecs.World(void).initMinimal();
defer world.deinit();
const Position = struct { x: f32, y: f32 };
const Velocity = struct { x: f32, y: f32 };
_ = try world.component(Position);
_ = try world.component(Velocity);
const entity = try world.entity(.{}, .{ Position, Velocity });
// Position and Velocity need to be present on the entity for component
// pointers to be stable. Otherwise, when `Velocity` is added, the entity
// would move tables and invalidate the `Position` pointer.
const pos = entity.getMut(Position);
const vel = entity.getMut(Velocity);
pos.* = .{ .x = 10, .y = 20 };
vel.* = .{ .x = 1, .y = 2 };
pos.* = .{ .x = pos.x + vel.x, .y = pos.y + vel.y };
try expect.equal(.{ .x = 11, .y = 22 }, pos.*);
}
test "Entity set and get with pair type" {
flecs.init(std.testing.allocator);
var world = try flecs.World(void).initMinimal();
defer world.deinit();
const Position = struct { x: f32, y: f32 };
const Rank = struct { value: i32 }; // Gives a component a certain "rank".
const Copy = struct {}; // Stores a "copy" of another component.
_ = try world.component(Position);
_ = try world.component(Rank);
_ = try world.tag(Copy);
const entity = try world.entity(.{}, .{});
entity.set(Position, .{ .x = 10, .y = 20 });
entity.set(.{ Rank, Position }, .{ .value = 9001 });
entity.set(.{ Copy, Position }, .{ .x = 60, .y = 80 });
try expect.equal(.{ .x = 10, .y = 20 }, entity.get(Position));
try expect.equal(.{ .value = 9001 }, entity.get(.{ Rank, Position }));
try expect.equal(.{ .x = 60, .y = 80 }, entity.get(.{ Copy, Position }));
}

@ -48,6 +48,34 @@ pub fn isZigString(comptime T: type) bool {
};
}
/// Returns the type of the specified expression, which must be either
/// just a `type`, or a tuple in the form of `.{ TRelation, TTarget }`,
/// representing a relationship pair in Flecs.
///
/// If the expression is a pair, the type is determined like this:
/// - If `TRelation` is a non-zero-sized type, it is returned.
/// - If `TTarget` is a non-zero-sized type, it is returned.
/// - Otherwise, a compile error is raised.
pub fn AnyToType(comptime expr: anytype) type {
switch (@typeInfo(@TypeOf(expr))) {
.Type => return expr,
.Struct => |s| {
if (!s.is_tuple or s.fields.len != 2)
@compileError("Expression must be a type or a tuple of two types");
const TRelation = expr[0];
const TTarget = expr[1];
if (@TypeOf(TRelation) != type) @compileError("TRelation must be a type, but is " ++ @typeName(@TypeOf(TRelation)));
if (@TypeOf(TTarget) != type) @compileError("TTarget must be a type, but is " ++ @typeName(@TypeOf(TTarget)));
if (@sizeOf(TRelation) > 0) return TRelation;
if (@sizeOf(TTarget) > 0) return TTarget;
@compileError("Either TRelation or TTarget must be a non-zero-sized type");
},
else => @compileError("Expression must be a type or a tuple of two types"),
}
}
/// Gets the simplified type name of the specified type.
/// That is, without any namespace qualifiers.
pub fn simpleTypeName(comptime T: type) [:0]const u8 {

Loading…
Cancel
Save