From f578e1f5e2513c4d3ddd5ed452196d4df8068642 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Sat, 6 Apr 2024 14:53:06 -0700 Subject: [PATCH] {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 --- examples/custom-renderer/Game.zig | 3 +-- examples/glyphs/Game.zig | 3 +-- examples/glyphs/Text.zig | 1 - examples/sprite/Game.zig | 3 +-- examples/text/Game.zig | 9 ++++----- src/engine.zig | 2 -- src/gfx/Sprite.zig | 1 - src/gfx/Text.zig | 3 +-- src/main.zig | 12 ++++++------ src/{ecs => module}/Archetype.zig | 0 src/{ecs => module}/StringTable.zig | 0 src/{ecs => module}/entities.zig | 2 +- src/{ecs => module}/main.zig | 26 +++----------------------- src/{ => module}/module.zig | 8 ++++---- src/{ecs => module}/query.zig | 2 +- 15 files changed, 23 insertions(+), 52 deletions(-) rename src/{ecs => module}/Archetype.zig (100%) rename src/{ecs => module}/StringTable.zig (100%) rename src/{ecs => module}/entities.zig (99%) rename src/{ecs => module}/main.zig (78%) rename src/{ => module}/module.zig (99%) rename src/{ecs => module}/query.zig (98%) diff --git a/examples/custom-renderer/Game.zig b/examples/custom-renderer/Game.zig index 350cf020..9e71a077 100644 --- a/examples/custom-renderer/Game.zig +++ b/examples/custom-renderer/Game.zig @@ -1,6 +1,5 @@ const std = @import("std"); const mach = @import("mach"); -const ecs = mach.ecs; const core = mach.core; const math = mach.math; const Renderer = @import("Renderer.zig"); @@ -12,7 +11,7 @@ const Vec3 = math.Vec3; // Global state for our game module. timer: mach.Timer, -player: ecs.EntityID, +player: mach.EntityID, direction: Vec2 = vec2(0, 0), spawning: bool = false, spawn_timer: mach.Timer, diff --git a/examples/glyphs/Game.zig b/examples/glyphs/Game.zig index fd6d4e84..40560989 100644 --- a/examples/glyphs/Game.zig +++ b/examples/glyphs/Game.zig @@ -3,7 +3,6 @@ const std = @import("std"); const mach = @import("mach"); const core = mach.core; const gpu = mach.gpu; -const ecs = mach.ecs; const Sprite = mach.gfx.Sprite; const math = mach.math; const vec2 = math.vec2; @@ -16,7 +15,7 @@ const Mat4x4 = math.Mat4x4; const Text = @import("Text.zig"); timer: mach.Timer, -player: mach.ecs.EntityID, +player: mach.EntityID, direction: Vec2 = vec2(0, 0), spawning: bool = false, spawn_timer: mach.Timer, diff --git a/examples/glyphs/Text.zig b/examples/glyphs/Text.zig index 7d5e73cd..61f90f4e 100644 --- a/examples/glyphs/Text.zig +++ b/examples/glyphs/Text.zig @@ -1,7 +1,6 @@ // TODO(important): review all code in this file in-depth const mach = @import("mach"); const gpu = mach.gpu; -const ecs = mach.ecs; const ft = @import("freetype"); const std = @import("std"); const assets = @import("assets"); diff --git a/examples/sprite/Game.zig b/examples/sprite/Game.zig index 58b525b2..7fd337cb 100644 --- a/examples/sprite/Game.zig +++ b/examples/sprite/Game.zig @@ -5,7 +5,6 @@ const assets = @import("assets"); const mach = @import("mach"); const core = mach.core; const gpu = mach.gpu; -const ecs = mach.ecs; const Sprite = mach.gfx.Sprite; const math = mach.math; @@ -19,7 +18,7 @@ const Mat4x4 = math.Mat4x4; var gpa = std.heap.GeneralPurposeAllocator(.{}){}; timer: mach.Timer, -player: mach.ecs.EntityID, +player: mach.EntityID, direction: Vec2 = vec2(0, 0), spawning: bool = false, spawn_timer: mach.Timer, diff --git a/examples/text/Game.zig b/examples/text/Game.zig index f9f5528d..14d16329 100644 --- a/examples/text/Game.zig +++ b/examples/text/Game.zig @@ -6,7 +6,6 @@ const mach = @import("mach"); const core = mach.core; const gfx = mach.gfx; const gpu = mach.gpu; -const ecs = mach.ecs; const Text = mach.gfx.Text; const math = mach.math; @@ -21,7 +20,7 @@ const Mat4x4 = math.Mat4x4; var gpa = std.heap.GeneralPurposeAllocator(.{}){}; timer: mach.Timer, -player: mach.ecs.EntityID, +player: mach.EntityID, direction: Vec2 = vec2(0, 0), spawning: bool = false, spawn_timer: mach.Timer, @@ -30,7 +29,7 @@ frame_count: usize, texts: usize, rand: std.rand.DefaultPrng, time: f32, -style1: mach.ecs.EntityID, +style1: mach.EntityID, allocator: std.mem.Allocator, const d0 = 0.000001; @@ -106,7 +105,7 @@ fn init( // TODO: better storage mechanism for this // TODO: this is a leak 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[1] = style2; styles[2] = style3; @@ -189,7 +188,7 @@ fn tick( // TODO: better storage mechanism for this // 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; try text_mod.set(new_entity, .text, text2); try text_mod.set(new_entity, .style, styles); diff --git a/src/engine.zig b/src/engine.zig index 46f9e9bc..ba2e6868 100644 --- a/src/engine.zig +++ b/src/engine.zig @@ -2,8 +2,6 @@ const std = @import("std"); const mach = @import("main.zig"); const core = mach.core; const gpu = mach.core.gpu; -const ecs = mach.ecs; -const module = @import("module.zig"); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); diff --git a/src/gfx/Sprite.zig b/src/gfx/Sprite.zig index c9603a81..1174544c 100644 --- a/src/gfx/Sprite.zig +++ b/src/gfx/Sprite.zig @@ -2,7 +2,6 @@ const std = @import("std"); const mach = @import("../main.zig"); const core = mach.core; const gpu = mach.core.gpu; -const ecs = mach.ecs; const Engine = mach.Engine; const math = mach.math; diff --git a/src/gfx/Text.zig b/src/gfx/Text.zig index 373e937a..529f806c 100644 --- a/src/gfx/Text.zig +++ b/src/gfx/Text.zig @@ -2,7 +2,6 @@ const std = @import("std"); const mach = @import("../main.zig"); const core = mach.core; const gpu = mach.gpu; -const ecs = mach.ecs; const Engine = mach.Engine; const gfx = mach.gfx; @@ -49,7 +48,7 @@ pub const components = .{ \\ 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. \\ \\ Expected to match the length of the text component. diff --git a/src/main.zig b/src/main.zig index 267e5b6a..dd0ca7f6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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 {}; // Mach standard library -pub const ecs = @import("ecs/main.zig"); // gamemode requires libc on linux 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 {}; @@ -21,17 +20,19 @@ pub const sysgpu = if (build_options.want_sysgpu) @import("sysgpu/main.zig") els // Engine exports pub const App = @import("engine.zig").App; 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: { if (!@hasDecl(@import("root"), "modules")) { @compileError("expected `pub const modules = .{};` in root file"); } 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 EntityID = @import("module/main.zig").EntityID; +pub const Archetype = @import("module/main.zig").Archetype; test { const std = @import("std"); @@ -44,8 +45,7 @@ test { _ = gfx; _ = math; _ = testing; - std.testing.refAllDeclsRecursive(@import("module.zig")); - std.testing.refAllDeclsRecursive(ecs); + std.testing.refAllDeclsRecursive(@import("module/main.zig")); std.testing.refAllDeclsRecursive(gamemode); std.testing.refAllDeclsRecursive(math); } diff --git a/src/ecs/Archetype.zig b/src/module/Archetype.zig similarity index 100% rename from src/ecs/Archetype.zig rename to src/module/Archetype.zig diff --git a/src/ecs/StringTable.zig b/src/module/StringTable.zig similarity index 100% rename from src/ecs/StringTable.zig rename to src/module/StringTable.zig diff --git a/src/ecs/entities.zig b/src/module/entities.zig similarity index 99% rename from src/ecs/entities.zig rename to src/module/entities.zig index 7e1d229f..701c4f7c 100644 --- a/src/ecs/entities.zig +++ b/src/module/entities.zig @@ -6,7 +6,7 @@ const assert = std.debug.assert; const query_mod = @import("query.zig"); const Archetype = @import("Archetype.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. pub const EntityID = u64; diff --git a/src/ecs/main.zig b/src/module/main.zig similarity index 78% rename from src/ecs/main.zig rename to src/module/main.zig index b31010de..92778500 100644 --- a/src/ecs/main.zig +++ b/src/module/main.zig @@ -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 mach = @import("../main.zig"); const testing = std.testing; @@ -18,15 +5,8 @@ const testing = std.testing; pub const EntityID = @import("entities.zig").EntityID; pub const Entities = @import("entities.zig").Entities; pub const Archetype = @import("Archetype.zig"); - -pub const Modules = @import("../module.zig").Modules; - -// TODO: -// * Iteration -// * Querying -// * Multi threading -// * Multiple entities having one value -// * Sparse storage? +pub const ModSet = @import("module.zig").ModSet; +pub const Modules = @import("module.zig").Modules; test { std.testing.refAllDeclsRecursive(@This()); @@ -36,7 +16,7 @@ test { std.testing.refAllDeclsRecursive(@import("StringTable.zig")); } -test "example" { +test "entities DB" { const allocator = testing.allocator; const root = struct { diff --git a/src/module.zig b/src/module/module.zig similarity index 99% rename from src/module.zig rename to src/module/module.zig index c2a035c3..b34d1c69 100644 --- a/src/module.zig +++ b/src/module/module.zig @@ -61,11 +61,11 @@ const builtin = @import("builtin"); const std = @import("std"); -const testing = @import("testing.zig"); +const testing = @import("../testing.zig"); -const Entities = @import("ecs/entities.zig").Entities; -const EntityID = @import("ecs/entities.zig").EntityID; -const is_debug = @import("ecs/Archetype.zig").is_debug; +const Entities = @import("entities.zig").Entities; +const EntityID = @import("entities.zig").EntityID; +const is_debug = @import("Archetype.zig").is_debug; /// Verifies that M matches the basic layout of a Mach module fn ModuleInterface(comptime M: type) type { diff --git a/src/ecs/query.zig b/src/module/query.zig similarity index 98% rename from src/ecs/query.zig rename to src/module/query.zig index 652614b7..093014e1 100644 --- a/src/ecs/query.zig +++ b/src/module/query.zig @@ -1,6 +1,6 @@ const std = @import("std"); const testing = std.testing; -const ComponentTypesByName = @import("../module.zig").ComponentTypesByName; +const ComponentTypesByName = @import("module.zig").ComponentTypesByName; pub const QueryTag = enum { any,