{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 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,

View file

@ -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,

View file

@ -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");

View file

@ -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,

View file

@ -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);

View file

@ -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();

View file

@ -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;

View file

@ -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.

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 {};
// 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);
}

View file

@ -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;

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 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 {

View file

@ -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 {

View file

@ -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,