Various inital workings

main
copygirl 1 year ago
parent e5d1a3ffd8
commit 0c6477c852
  1. 3
      .gitmodules
  2. 52
      build.zig
  3. 3
      src/c.zig
  4. 1
      src/component.zig
  5. 16
      src/entity.zig
  6. 10
      src/id.zig
  7. 10
      src/iter.zig
  8. 29
      src/main.zig
  9. 0
      src/system.zig
  10. 9
      src/util.zig
  11. 92
      src/world.zig
  12. 4
      tests/main.zig
  13. 75
      tests/util.zig
  14. 71
      tests/world.zig

3
.gitmodules vendored

@ -1,3 +0,0 @@
[submodule "flecs"]
path = libs/flecs
url = https://github.com/SanderMertens/flecs.git

@ -7,7 +7,6 @@ pub fn build(b: *std.Build) !void {
const module = b.createModule(.{ const module = b.createModule(.{
.source_file = .{ .path = "src/main.zig" }, .source_file = .{ .path = "src/main.zig" },
}); });
try b.modules.put(b.dupe("flecs-zig-ble"), module); try b.modules.put(b.dupe("flecs-zig-ble"), module);
const lib = b.addStaticLibrary(.{ const lib = b.addStaticLibrary(.{
@ -16,48 +15,33 @@ pub fn build(b: *std.Build) !void {
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
setupFlecs(lib);
lib.linkLibC();
lib.addIncludePath(.{ .path = thisDir() ++ "/flecs" });
lib.addCSourceFile(.{
.file = .{ .path = thisDir() ++ "/flecs/flecs.c" },
.flags = &.{"-fno-sanitize=undefined"},
});
lib.defineCMacro("FLECS_NO_CPP", null);
lib.defineCMacro("FLECS_USE_OS_ALLOC", null);
if (@import("builtin").mode == .Debug)
lib.defineCMacro("FLECS_SANITIZE", null);
if (lib.target.isWindows())
lib.linkSystemLibraryName("ws2_32");
b.installArtifact(lib); b.installArtifact(lib);
const main_tests = b.addTest(.{ const main_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" }, .root_source_file = .{ .path = "tests/main.zig" },
// Has to be specified so tests can run autodoc tests from src/.
.main_pkg_path = .{ .path = "." },
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
setupFlecs(main_tests);
main_tests.linkLibC();
main_tests.addIncludePath(.{ .path = thisDir() ++ "/flecs" });
main_tests.addCSourceFile(.{
.file = .{ .path = thisDir() ++ "/flecs/flecs.c" },
.flags = &.{"-fno-sanitize=undefined"},
});
main_tests.defineCMacro("FLECS_NO_CPP", null);
main_tests.defineCMacro("FLECS_USE_OS_ALLOC", null);
main_tests.defineCMacro("FLECS_SANITIZE", null);
if (main_tests.target.isWindows())
main_tests.linkSystemLibraryName("ws2_32");
const run_main_tests = b.addRunArtifact(main_tests); const run_main_tests = b.addRunArtifact(main_tests);
const test_step = b.step("test", "Run library tests"); const test_step = b.step("test", "Run library tests");
test_step.dependOn(&run_main_tests.step); test_step.dependOn(&run_main_tests.step);
} }
inline fn thisDir() []const u8 { fn setupFlecs(step: *std.Build.CompileStep) void {
return comptime std.fs.path.dirname(@src().file) orelse "."; step.linkLibC();
step.addIncludePath(.{ .path = "libs/flecs" });
step.addCSourceFile(.{
.file = .{ .path = "libs/flecs/flecs.c" },
.flags = &.{"-fno-sanitize=undefined"},
});
step.defineCMacro("FLECS_NO_CPP", null);
step.defineCMacro("FLECS_USE_OS_ALLOC", null);
if (@import("builtin").mode == .Debug)
step.defineCMacro("FLECS_SANITIZE", null);
if (step.target.isWindows())
step.linkSystemLibraryName("ws2_32");
} }

@ -0,0 +1,3 @@
pub usingnamespace @cImport({
@cInclude("flecs.h");
});

@ -0,0 +1 @@
const c = @import("./c.zig");

@ -0,0 +1,16 @@
const c = @import("./c.zig");
const World = @import("./world.zig").World;
pub fn Entity(comptime ctx: anytype) type {
return struct {
world: *World(ctx),
raw: c.ecs_entity_t,
const Self = @This();
pub fn fromRaw(world: *World(ctx), raw: c.ecs_entity_t) Self {
return .{ .world = world, .raw = raw };
}
};
}

@ -0,0 +1,10 @@
const c = @import("./c.zig");
const World = @import("./world.zig").World;
pub fn Id(comptime ctx: anytype) type {
return struct {
world: *World(ctx),
raw: c.ecs_id_t,
};
}

@ -0,0 +1,10 @@
const c = @import("./c.zig");
const World = @import("./world.zig").World;
pub fn Iter(comptime ctx: anytype) type {
return struct {
world: *World(ctx),
raw: c.ecs_iter_t,
};
}

@ -0,0 +1,29 @@
pub usingnamespace @import("./component.zig");
pub usingnamespace @import("./entity.zig");
pub usingnamespace @import("./id.zig");
pub usingnamespace @import("./iter.zig");
pub usingnamespace @import("./system.zig");
pub usingnamespace @import("./world.zig");
pub const c = @import("./c.zig");
pub fn Context(comptime ctx: anytype) type {
return struct {
pub const Entity = @import("./entity.zig").Entity(ctx);
pub const Id = @import("./id.zig").Id(ctx);
pub const Iter = @import("./iter.zig").Iter(ctx);
pub const World = @import("./world.zig").World(ctx);
pub fn Lookup(comptime T: type) type {
_ = T; // Only necessary to create a unique type.
return struct {
pub var id: c.ecs_id_t = 0;
};
}
};
}
test {
const std = @import("std");
std.testing.refAllDecls(@This());
}

@ -0,0 +1,9 @@
const std = @import("std");
/// Gets the simplified type name of the specified type.
/// That is, without any namespace qualifiers.
pub fn simpleTypeName(comptime T: type) [:0]const u8 {
const fullName = @typeName(T);
const index = std.mem.lastIndexOf(u8, fullName, ".");
return if (index) |i| fullName[(i + 1)..] else fullName;
}

@ -0,0 +1,92 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
const c = @import("./c.zig");
const util = @import("./util.zig");
pub fn World(comptime ctx: anytype) type {
const Lookup = @import("./main.zig").Context(ctx).Lookup;
const Entity = @import("./entity.zig").Entity(ctx);
return struct {
raw: *c.ecs_world_t,
allocator: Allocator,
const Self = @This();
pub fn init(alloc: Allocator) !*Self {
var result = try alloc.create(Self);
result.raw = c.ecs_init().?;
result.allocator = alloc;
return result;
}
pub fn initWithArgs(alloc: Allocator, args: [][*:0]u8) !*Self {
var result = try alloc.create(Self);
result.raw = c.ecs_init_w_args(args.len, args.ptr).?;
result.allocator = alloc;
return result;
}
pub fn deinit(self: *Self) void {
_ = c.ecs_fini(self.raw);
self.allocator.destroy(self);
}
pub fn progress(self: *Self, delta_time: f32) bool {
return c.ecs_progress(self.raw, delta_time);
}
pub fn entity(
self: *Self,
comptime add: []const type,
) Entity {
var desc = std.mem.zeroes(c.ecs_entity_desc_t);
if (add.len > c.FLECS_ID_DESC_MAX)
@compileLog("add.len > FLECS_ID_DESC_MAX");
inline for (add, 0..) |T, i|
desc.add[i] = Lookup(T).id;
const result = c.ecs_entity_init(self.*.raw, &desc);
return Entity.fromRaw(self, result);
}
pub fn component(
self: *Self,
comptime T: type,
) Entity {
const name = util.simpleTypeName(T);
const entDesc = std.mem.zeroInit(c.ecs_entity_desc_t, .{
.name = name,
.symbol = name,
.use_low_id = true,
});
const compDesc = std.mem.zeroInit(c.ecs_component_desc_t, .{
.entity = c.ecs_entity_init(self.raw, &entDesc),
.type = .{ .size = @sizeOf(T), .alignment = @alignOf(T) },
});
Lookup(T).id = c.ecs_component_init(self.raw, &compDesc);
return Entity.fromRaw(self, Lookup(T).id);
}
pub fn system(
self: *Self,
name: [:0]const u8,
callback: c.ecs_iter_action_t,
phase: ?Entity,
expr: [:0]const u8,
) Entity {
var entDesc = std.mem.zeroes(c.ecs_entity_desc_t);
entDesc.name = name;
if (phase) |p| {
entDesc.add[0] = c.ecs_pair(c.EcsDependsOn, p.raw);
entDesc.add[1] = p.raw;
}
var sysDesc = std.mem.zeroes(c.ecs_system_desc_t);
sysDesc.entity = c.ecs_entity_init(self.raw, &entDesc);
sysDesc.query.filter.expr = expr;
sysDesc.callback = callback;
const result = c.ecs_system_init(self.raw, *sysDesc);
return Entity.fromRaw(self, result);
}
};
}

@ -0,0 +1,4 @@
test {
_ = @import("../src/main.zig");
_ = @import("./world.zig");
}

@ -0,0 +1,75 @@
const std = @import("std");
const expect = std.testing.expect;
const expectEql = std.testing.expectEqual;
const expectStrEql = std.testing.expectEqualStrings;
const flecs = @import("../src/main.zig");
const c = flecs.c;
const context = flecs.Context(void);
const Entity = context.Entity;
const Iter = context.Iter;
const Id = context.Id;
pub const MAX_SYS_COLUMNS = 20;
pub const MAX_ENTITIES = 256;
pub const MAX_INVOCATIONS = 1024;
pub const Probe = struct {
system: c.ecs_entity_t,
event: c.ecs_entity_t,
eventId: c.ecs_id_t,
offset: i32,
count: i32,
invoked: i32,
termCount: i32,
termIndex: i32,
e: [MAX_ENTITIES]c.ecs_entity_t,
c: [MAX_SYS_COLUMNS][MAX_INVOCATIONS]c.ecs_entity_t,
s: [MAX_SYS_COLUMNS][MAX_INVOCATIONS]c.ecs_entity_t,
param: ?*anyopaque,
pub fn init() Probe {
return std.mem.zeroes(Probe);
}
pub fn probeSystemWithContext(ctx: *Probe, it: *Iter) !void {
ctx.*.param = it.*.param;
ctx.*.system = it.*.system;
ctx.*.event = it.*.event;
ctx.*.eventId = it.*.eventId;
ctx.*.offset = 0;
ctx.*.termCount = it.*.fieldCount;
ctx.*.termIndex = it.*.termIndex;
for (0..ctx.*.term_count) |i| {
ctx.*.c[ctx.*.invoked][i] = it.*.ids[i];
ctx.*.s[ctx.*.invoked][i] = it.fieldSource(i + 1);
const e = it.fieldId(i + 1);
try expect(e != 0);
}
for (0..it.*.count) |i| {
if (i + ctx.*.count < 256) {
ctx.*.e[i + ctx.*.count] = it.*.entities[i];
} else {
// Can't store more than that, tests shouldn't
// rely on getting back more than 256 results.
unreachable;
}
}
ctx.*.count += it.*.count;
ctx.*.invoked += 1;
}
pub fn probeIter(it: *Iter) !void {
var ctx = c.ecs_get_context(it.*.world);
if (ctx == null) ctx = it.*.ctx;
if (ctx) |ct| {
var p: *Probe = @ptrCast(ct);
p.probeSystemWithContext(it);
}
}
};

@ -0,0 +1,71 @@
// Reimplementations of the following tests from Flecs:
// https://github.com/SanderMertens/flecs/blob/master/test/api/src/World.c
const std = @import("std");
const expect = std.testing.expect;
const expectEql = std.testing.expectEqual;
const expectStrEql = std.testing.expectEqualStrings;
const util = @import("./util.zig");
const flecs = @import("../src/main.zig");
const c = flecs.c;
const context = flecs.Context(void);
const Lookup = context.Lookup;
const World = context.World;
const Iter = context.Iter;
const Entity = context.Entity;
const Position = struct { x: f32, y: f32 };
const Velocity = struct { x: f32, y: f32 };
fn move(it: *Iter) void {
var pos = it.field(Position, 1);
var vel = it.field(Velocity, 2);
util.Probe.probeIter(it);
for (0..it.count) |row| {
var p = &pos[row];
var v = &vel[row];
p.*.x += v.*.x * it.delta_time;
p.*.y += v.*.y * it.delta_time;
}
}
test "World_progress_w_0" {
var world = try World.init(std.testing.allocator);
defer world.deinit();
_ = world.component(Position);
_ = world.component(Velocity);
// const e1 = world.entity(&.{ Position, Velocity });
// // const phase = Entity.fromRaw(world, c.EcsOnUpdate);
// // const move_system = world.system("move", move, phase, "Position, Velocity");
// var ctx = util.Probe.init();
// c.ecs_set_context(world.raw, &ctx);
// // e1.set(Position, .{ 0, 0 });
// // e1.set(Velocity, .{ 1, 2 });
// _ = world.progress(0);
// try expectEql(ctx.count, 1);
// try expectEql(ctx.invoked, 1);
// // try expectEql(ctx.system, move_system);
// try expectEql(ctx.termCount, 2);
// try expectEql(ctx.param, null);
// try expectEql(ctx.e[0], e1.raw);
// try expectEql(ctx.c[0][0], Lookup(Position).id);
// try expectEql(ctx.c[0][1], Lookup(Velocity).id);
// try expectEql(ctx.s[0][0], 0);
// try expectEql(ctx.s[0][1], 0);
// // const p = try e1.get(Position);
// // try expect(p.x != 0);
// // try expect(p.y != 0);
}
Loading…
Cancel
Save