{module,ecs}: Mach ECS becomes the Mach' module system

This moves the bulk of the ECS code into `src/module`. It also makes
types like `mach.ecs.EntityID` accessible at the top-level `mach.EntityID`
instead.

The motivation of this change is to make the Mach module system a
first-class property of Mach.

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2024-04-06 14:53:06 -07:00 committed by Stephen Gutekanst
parent 679a05faf4
commit f578e1f5e2
15 changed files with 23 additions and 52 deletions

View file

@ -1,6 +1,5 @@
const std = @import("std"); const std = @import("std");
const mach = @import("mach"); const mach = @import("mach");
const ecs = mach.ecs;
const core = mach.core; const core = mach.core;
const math = mach.math; const math = mach.math;
const Renderer = @import("Renderer.zig"); const Renderer = @import("Renderer.zig");
@ -12,7 +11,7 @@ const Vec3 = math.Vec3;
// Global state for our game module. // Global state for our game module.
timer: mach.Timer, timer: mach.Timer,
player: ecs.EntityID, player: mach.EntityID,
direction: Vec2 = vec2(0, 0), direction: Vec2 = vec2(0, 0),
spawning: bool = false, spawning: bool = false,
spawn_timer: mach.Timer, spawn_timer: mach.Timer,

View file

@ -3,7 +3,6 @@ const std = @import("std");
const mach = @import("mach"); const mach = @import("mach");
const core = mach.core; const core = mach.core;
const gpu = mach.gpu; const gpu = mach.gpu;
const ecs = mach.ecs;
const Sprite = mach.gfx.Sprite; const Sprite = mach.gfx.Sprite;
const math = mach.math; const math = mach.math;
const vec2 = math.vec2; const vec2 = math.vec2;
@ -16,7 +15,7 @@ const Mat4x4 = math.Mat4x4;
const Text = @import("Text.zig"); const Text = @import("Text.zig");
timer: mach.Timer, timer: mach.Timer,
player: mach.ecs.EntityID, player: mach.EntityID,
direction: Vec2 = vec2(0, 0), direction: Vec2 = vec2(0, 0),
spawning: bool = false, spawning: bool = false,
spawn_timer: mach.Timer, spawn_timer: mach.Timer,

View file

@ -1,7 +1,6 @@
// TODO(important): review all code in this file in-depth // TODO(important): review all code in this file in-depth
const mach = @import("mach"); const mach = @import("mach");
const gpu = mach.gpu; const gpu = mach.gpu;
const ecs = mach.ecs;
const ft = @import("freetype"); const ft = @import("freetype");
const std = @import("std"); const std = @import("std");
const assets = @import("assets"); const assets = @import("assets");

View file

@ -5,7 +5,6 @@ const assets = @import("assets");
const mach = @import("mach"); const mach = @import("mach");
const core = mach.core; const core = mach.core;
const gpu = mach.gpu; const gpu = mach.gpu;
const ecs = mach.ecs;
const Sprite = mach.gfx.Sprite; const Sprite = mach.gfx.Sprite;
const math = mach.math; const math = mach.math;
@ -19,7 +18,7 @@ const Mat4x4 = math.Mat4x4;
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
timer: mach.Timer, timer: mach.Timer,
player: mach.ecs.EntityID, player: mach.EntityID,
direction: Vec2 = vec2(0, 0), direction: Vec2 = vec2(0, 0),
spawning: bool = false, spawning: bool = false,
spawn_timer: mach.Timer, spawn_timer: mach.Timer,

View file

@ -6,7 +6,6 @@ const mach = @import("mach");
const core = mach.core; const core = mach.core;
const gfx = mach.gfx; const gfx = mach.gfx;
const gpu = mach.gpu; const gpu = mach.gpu;
const ecs = mach.ecs;
const Text = mach.gfx.Text; const Text = mach.gfx.Text;
const math = mach.math; const math = mach.math;
@ -21,7 +20,7 @@ const Mat4x4 = math.Mat4x4;
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
timer: mach.Timer, timer: mach.Timer,
player: mach.ecs.EntityID, player: mach.EntityID,
direction: Vec2 = vec2(0, 0), direction: Vec2 = vec2(0, 0),
spawning: bool = false, spawning: bool = false,
spawn_timer: mach.Timer, spawn_timer: mach.Timer,
@ -30,7 +29,7 @@ frame_count: usize,
texts: usize, texts: usize,
rand: std.rand.DefaultPrng, rand: std.rand.DefaultPrng,
time: f32, time: f32,
style1: mach.ecs.EntityID, style1: mach.EntityID,
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
const d0 = 0.000001; const d0 = 0.000001;
@ -106,7 +105,7 @@ fn init(
// TODO: better storage mechanism for this // TODO: better storage mechanism for this
// TODO: this is a leak // TODO: this is a leak
const allocator = gpa.allocator(); const allocator = gpa.allocator();
const styles = try allocator.alloc(mach.ecs.EntityID, 3); const styles = try allocator.alloc(mach.EntityID, 3);
styles[0] = style1; styles[0] = style1;
styles[1] = style2; styles[1] = style2;
styles[2] = style3; styles[2] = style3;
@ -189,7 +188,7 @@ fn tick(
// TODO: better storage mechanism for this // TODO: better storage mechanism for this
// TODO: this is a leak // TODO: this is a leak
const styles = try game.state().allocator.alloc(mach.ecs.EntityID, 1); const styles = try game.state().allocator.alloc(mach.EntityID, 1);
styles[0] = game.state().style1; styles[0] = game.state().style1;
try text_mod.set(new_entity, .text, text2); try text_mod.set(new_entity, .text, text2);
try text_mod.set(new_entity, .style, styles); try text_mod.set(new_entity, .style, styles);

View file

@ -2,8 +2,6 @@ const std = @import("std");
const mach = @import("main.zig"); const mach = @import("main.zig");
const core = mach.core; const core = mach.core;
const gpu = mach.core.gpu; const gpu = mach.core.gpu;
const ecs = mach.ecs;
const module = @import("module.zig");
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator(); const allocator = gpa.allocator();

View file

@ -2,7 +2,6 @@ const std = @import("std");
const mach = @import("../main.zig"); const mach = @import("../main.zig");
const core = mach.core; const core = mach.core;
const gpu = mach.core.gpu; const gpu = mach.core.gpu;
const ecs = mach.ecs;
const Engine = mach.Engine; const Engine = mach.Engine;
const math = mach.math; const math = mach.math;

View file

@ -2,7 +2,6 @@ const std = @import("std");
const mach = @import("../main.zig"); const mach = @import("../main.zig");
const core = mach.core; const core = mach.core;
const gpu = mach.gpu; const gpu = mach.gpu;
const ecs = mach.ecs;
const Engine = mach.Engine; const Engine = mach.Engine;
const gfx = mach.gfx; const gfx = mach.gfx;
@ -49,7 +48,7 @@ pub const components = .{
\\ Expected to match the length of the style component. \\ Expected to match the length of the style component.
}, },
.style = .{ .type = []const mach.ecs.EntityID, .description = .style = .{ .type = []const mach.EntityID, .description =
\\ The style to apply to each segment of text. \\ The style to apply to each segment of text.
\\ \\
\\ Expected to match the length of the text component. \\ Expected to match the length of the text component.

View file

@ -8,7 +8,6 @@ pub const gpu = if (build_options.want_core) core.gpu else struct {};
pub const sysjs = if (build_options.want_core) @import("mach-sysjs") else struct {}; pub const sysjs = if (build_options.want_core) @import("mach-sysjs") else struct {};
// Mach standard library // Mach standard library
pub const ecs = @import("ecs/main.zig");
// gamemode requires libc on linux // gamemode requires libc on linux
pub const gamemode = if (builtin.os.tag != .linux or builtin.link_libc) @import("gamemode.zig"); pub const gamemode = if (builtin.os.tag != .linux or builtin.link_libc) @import("gamemode.zig");
pub const gfx = if (build_options.want_mach) @import("gfx/main.zig") else struct {}; pub const gfx = if (build_options.want_mach) @import("gfx/main.zig") else struct {};
@ -21,17 +20,19 @@ pub const sysgpu = if (build_options.want_sysgpu) @import("sysgpu/main.zig") els
// Engine exports // Engine exports
pub const App = @import("engine.zig").App; pub const App = @import("engine.zig").App;
pub const Engine = @import("engine.zig").Engine; pub const Engine = @import("engine.zig").Engine;
pub const ModSet = @import("module.zig").ModSet;
// TODO: perhaps this could be a comptime var rather than @import("root")? // Module system
pub const ModSet = @import("module/main.zig").ModSet;
pub const modules = blk: { pub const modules = blk: {
if (!@hasDecl(@import("root"), "modules")) { if (!@hasDecl(@import("root"), "modules")) {
@compileError("expected `pub const modules = .{};` in root file"); @compileError("expected `pub const modules = .{};` in root file");
} }
break :blk @import("root").modules; break :blk @import("root").modules;
}; };
pub const Modules = @import("module.zig").Modules(modules); pub const Modules = @import("module/main.zig").Modules(modules);
pub const Mod = ModSet(modules).Mod; pub const Mod = ModSet(modules).Mod;
pub const EntityID = @import("module/main.zig").EntityID;
pub const Archetype = @import("module/main.zig").Archetype;
test { test {
const std = @import("std"); const std = @import("std");
@ -44,8 +45,7 @@ test {
_ = gfx; _ = gfx;
_ = math; _ = math;
_ = testing; _ = testing;
std.testing.refAllDeclsRecursive(@import("module.zig")); std.testing.refAllDeclsRecursive(@import("module/main.zig"));
std.testing.refAllDeclsRecursive(ecs);
std.testing.refAllDeclsRecursive(gamemode); std.testing.refAllDeclsRecursive(gamemode);
std.testing.refAllDeclsRecursive(math); std.testing.refAllDeclsRecursive(math);
} }

View file

@ -6,7 +6,7 @@ const assert = std.debug.assert;
const query_mod = @import("query.zig"); const query_mod = @import("query.zig");
const Archetype = @import("Archetype.zig"); const Archetype = @import("Archetype.zig");
const StringTable = @import("StringTable.zig"); const StringTable = @import("StringTable.zig");
const ComponentTypesByName = @import("../module.zig").ComponentTypesByName; const ComponentTypesByName = @import("module.zig").ComponentTypesByName;
/// An entity ID uniquely identifies an entity globally within an Entities set. /// An entity ID uniquely identifies an entity globally within an Entities set.
pub const EntityID = u64; pub const EntityID = u64;

View file

@ -1,16 +1,3 @@
//! mach/ecs is an Entity component system implementation.
//!
//! ## Design principles:
//!
//! * Initially a 100% clean-room implementation, working from first-principles. Later informed by
//! research into how other ECS work, with advice from e.g. Bevy and Flecs authors at different
//! points (thank you!)
//! * Solve the problems ECS solves, in a way that is natural to Zig and leverages Zig comptime.
//! * Fast. Optimal for CPU caches, multi-threaded, leverage comptime as much as is reasonable.
//! * Simple. Small API footprint, should be natural and fun - not like you're writing boilerplate.
//! * Enable other libraries to provide tracing, editors, visualizers, profilers, etc.
//!
const std = @import("std"); const std = @import("std");
const mach = @import("../main.zig"); const mach = @import("../main.zig");
const testing = std.testing; const testing = std.testing;
@ -18,15 +5,8 @@ const testing = std.testing;
pub const EntityID = @import("entities.zig").EntityID; pub const EntityID = @import("entities.zig").EntityID;
pub const Entities = @import("entities.zig").Entities; pub const Entities = @import("entities.zig").Entities;
pub const Archetype = @import("Archetype.zig"); pub const Archetype = @import("Archetype.zig");
pub const ModSet = @import("module.zig").ModSet;
pub const Modules = @import("../module.zig").Modules; pub const Modules = @import("module.zig").Modules;
// TODO:
// * Iteration
// * Querying
// * Multi threading
// * Multiple entities having one value
// * Sparse storage?
test { test {
std.testing.refAllDeclsRecursive(@This()); std.testing.refAllDeclsRecursive(@This());
@ -36,7 +16,7 @@ test {
std.testing.refAllDeclsRecursive(@import("StringTable.zig")); std.testing.refAllDeclsRecursive(@import("StringTable.zig"));
} }
test "example" { test "entities DB" {
const allocator = testing.allocator; const allocator = testing.allocator;
const root = struct { const root = struct {

View file

@ -61,11 +61,11 @@
const builtin = @import("builtin"); const builtin = @import("builtin");
const std = @import("std"); const std = @import("std");
const testing = @import("testing.zig"); const testing = @import("../testing.zig");
const Entities = @import("ecs/entities.zig").Entities; const Entities = @import("entities.zig").Entities;
const EntityID = @import("ecs/entities.zig").EntityID; const EntityID = @import("entities.zig").EntityID;
const is_debug = @import("ecs/Archetype.zig").is_debug; const is_debug = @import("Archetype.zig").is_debug;
/// Verifies that M matches the basic layout of a Mach module /// Verifies that M matches the basic layout of a Mach module
fn ModuleInterface(comptime M: type) type { fn ModuleInterface(comptime M: type) type {

View file

@ -1,6 +1,6 @@
const std = @import("std"); const std = @import("std");
const testing = std.testing; const testing = std.testing;
const ComponentTypesByName = @import("../module.zig").ComponentTypesByName; const ComponentTypesByName = @import("module.zig").ComponentTypesByName;
pub const QueryTag = enum { pub const QueryTag = enum {
any, any,