all: move standalone libraries to libs/ subdirectory
The root dir of our repository has grown quite a lot the past few months.
I'd like to make it more clear where the bulk of the engine lives (`src/`) and
also make it more clear which Mach libraries are consumable as standalone projects.
As for the name of this directory, `libs` was my first choice but there's a bit of
a convention of that being external libraries in Zig projects _today_, while these
are libraries maintained as part of Mach in this repository - not external ones.
We will name this directory `libs`, and if we have a need for external libraries
we will use `external` or `deps` for that directory name. I considered other names
such as `components`, `systems`, `modules` (which are bad as they overlap with
major ECS / engine concepts), and it seems likely the official Zig package manager
will break the convention of using a `libs` dir anyway.
Performed via:
```sh
mkdir libs/
git mv freetype libs/
git mv basisu libs/
git mv gamemode libs/
git mv glfw libs/
git mv gpu libs/
git mv gpu-dawn libs/
git mv sysaudio libs/
git mv sysjs libs/
git mv ecs libs/
```
git-subtree-dir: glfw
git-subtree-mainline: 0d5b853443
git-subtree-split: 572d1144f11b353abdb64fff828b25a4f0fbb7ca
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
git mv ecs libs/
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
79ec61396f
commit
0645429df9
240 changed files with 6 additions and 6 deletions
291
libs/ecs/src/systems.zig
Normal file
291
libs/ecs/src/systems.zig
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const Allocator = mem.Allocator;
|
||||
const testing = std.testing;
|
||||
const math = std.math;
|
||||
const StructField = std.builtin.Type.StructField;
|
||||
const EnumField = std.builtin.Type.EnumField;
|
||||
const UnionField = std.builtin.Type.UnionField;
|
||||
|
||||
const Entities = @import("entities.zig").Entities;
|
||||
|
||||
/// An ECS module can provide components, systems, and global values.
|
||||
pub fn Module(comptime Params: anytype) @TypeOf(Params) {
|
||||
// TODO: validate the type
|
||||
return Params;
|
||||
}
|
||||
|
||||
/// Describes a set of ECS modules, each of which can provide components, systems, and more.
|
||||
pub fn Modules(modules: anytype) @TypeOf(modules) {
|
||||
// TODO: validate the type
|
||||
return modules;
|
||||
}
|
||||
|
||||
/// Returns a tagged union representing the messages, turning this:
|
||||
///
|
||||
/// ```
|
||||
/// .{ .tick = void, .foo = i32 }
|
||||
/// ```
|
||||
///
|
||||
/// Into `T`:
|
||||
///
|
||||
/// ```
|
||||
/// const T = union(MessagesTag(messages)) {
|
||||
/// .tick = void,
|
||||
/// .foo = i32,
|
||||
/// };
|
||||
/// ```
|
||||
pub fn Messages(messages: anytype) type {
|
||||
var fields: []const UnionField = &[0]UnionField{};
|
||||
const message_fields = std.meta.fields(@TypeOf(messages));
|
||||
inline for (message_fields) |message_field| {
|
||||
const message_type = @field(messages, message_field.name);
|
||||
fields = fields ++ [_]std.builtin.Type.UnionField{.{
|
||||
.name = message_field.name,
|
||||
.field_type = message_type,
|
||||
.alignment = if (message_type == void) 0 else @alignOf(message_type),
|
||||
}};
|
||||
}
|
||||
|
||||
// Hack to workaround stage1 compiler bug. https://github.com/ziglang/zig/issues/8114
|
||||
//
|
||||
// return @Type(.{
|
||||
// .Union = .{
|
||||
// .layout = .Auto,
|
||||
// .tag_type = MessagesTag(messages),
|
||||
// .fields = fields,
|
||||
// .decls = &[_]std.builtin.Type.Declaration{},
|
||||
// },
|
||||
// });
|
||||
//
|
||||
const Ref = union(enum) { temp };
|
||||
var info = @typeInfo(Ref);
|
||||
info.Union.tag_type = MessagesTag(messages);
|
||||
info.Union.fields = fields;
|
||||
return @Type(info);
|
||||
}
|
||||
|
||||
/// Returns the tag enum for a tagged union representing the messages, turning this:
|
||||
///
|
||||
/// ```
|
||||
/// .{ .tick = void, .foo = i32 }
|
||||
/// ```
|
||||
///
|
||||
/// Into this:
|
||||
///
|
||||
/// ```
|
||||
/// enum { .tick, .foo };
|
||||
/// ```
|
||||
pub fn MessagesTag(messages: anytype) type {
|
||||
var fields: []const EnumField = &[0]EnumField{};
|
||||
const message_fields = std.meta.fields(@TypeOf(messages));
|
||||
inline for (message_fields) |message_field, index| {
|
||||
fields = fields ++ [_]std.builtin.Type.EnumField{.{
|
||||
.name = message_field.name,
|
||||
.value = index,
|
||||
}};
|
||||
}
|
||||
|
||||
return @Type(.{
|
||||
.Enum = .{
|
||||
.layout = .Auto,
|
||||
.tag_type = std.meta.Int(.unsigned, @floatToInt(u16, math.ceil(math.log2(@intToFloat(f64, message_fields.len))))),
|
||||
.fields = fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
.is_exhaustive = true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the namespaced components struct **type**.
|
||||
//
|
||||
/// Consult `namespacedComponents` for how a value of this type looks.
|
||||
fn NamespacedComponents(comptime modules: anytype) type {
|
||||
var fields: []const StructField = &[0]StructField{};
|
||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||
const module = @field(modules, module_field.name);
|
||||
if (@hasField(@TypeOf(module), "components")) {
|
||||
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||
.name = module_field.name,
|
||||
.field_type = @TypeOf(module.components),
|
||||
.default_value = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(@TypeOf(module.components)),
|
||||
}};
|
||||
}
|
||||
}
|
||||
return @Type(.{
|
||||
.Struct = .{
|
||||
.layout = .Auto,
|
||||
.is_tuple = false,
|
||||
.fields = fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Extracts namespaces components from modules like this:
|
||||
///
|
||||
/// ```
|
||||
/// .{
|
||||
/// .renderer = .{
|
||||
/// .components = .{
|
||||
/// .location = Vec3,
|
||||
/// .rotation = Vec3,
|
||||
/// },
|
||||
/// ...
|
||||
/// },
|
||||
/// .physics2d = .{
|
||||
/// .components = .{
|
||||
/// .location = Vec2
|
||||
/// .velocity = Vec2,
|
||||
/// },
|
||||
/// ...
|
||||
/// },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Returning a namespaced components value like this:
|
||||
///
|
||||
/// ```
|
||||
/// .{
|
||||
/// .renderer = .{
|
||||
/// .location = Vec3,
|
||||
/// .rotation = Vec3,
|
||||
/// },
|
||||
/// .physics2d = .{
|
||||
/// .location = Vec2
|
||||
/// .velocity = Vec2,
|
||||
/// },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
fn namespacedComponents(comptime modules: anytype) NamespacedComponents(modules) {
|
||||
var x: NamespacedComponents(modules) = undefined;
|
||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||
const module = @field(modules, module_field.name);
|
||||
if (@hasField(@TypeOf(module), "components")) {
|
||||
@field(x, module_field.name) = module.components;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
/// Extracts namespaced globals from modules like this:
|
||||
///
|
||||
/// ```
|
||||
/// .{
|
||||
/// .renderer = .{
|
||||
/// .globals = struct{
|
||||
/// foo: *Bar,
|
||||
/// baz: Bam,
|
||||
/// },
|
||||
/// ...
|
||||
/// },
|
||||
/// .physics2d = .{
|
||||
/// .globals = struct{
|
||||
/// foo: *Instance,
|
||||
/// },
|
||||
/// ...
|
||||
/// },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Into a namespaced global type like this:
|
||||
///
|
||||
/// ```
|
||||
/// struct{
|
||||
/// renderer: struct{
|
||||
/// foo: *Bar,
|
||||
/// baz: Bam,
|
||||
/// },
|
||||
/// physics2d: struct{
|
||||
/// foo: *Instance,
|
||||
/// },
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
fn NamespacedGlobals(comptime modules: anytype) type {
|
||||
var fields: []const StructField = &[0]StructField{};
|
||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||
const module = @field(modules, module_field.name);
|
||||
if (@hasField(@TypeOf(module), "globals")) {
|
||||
fields = fields ++ [_]std.builtin.Type.StructField{.{
|
||||
.name = module_field.name,
|
||||
.field_type = module.globals,
|
||||
.default_value = null,
|
||||
.is_comptime = false,
|
||||
.alignment = @alignOf(module.globals),
|
||||
}};
|
||||
}
|
||||
}
|
||||
return @Type(.{
|
||||
.Struct = .{
|
||||
.layout = .Auto,
|
||||
.is_tuple = false,
|
||||
.fields = fields,
|
||||
.decls = &[_]std.builtin.Type.Declaration{},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
pub fn World(comptime modules: anytype) type {
|
||||
const all_components = namespacedComponents(modules);
|
||||
return struct {
|
||||
allocator: Allocator,
|
||||
entities: Entities(all_components),
|
||||
globals: NamespacedGlobals(modules),
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(allocator: Allocator) !Self {
|
||||
return Self{
|
||||
.allocator = allocator,
|
||||
.entities = try Entities(all_components).init(allocator),
|
||||
.globals = undefined,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(world: *Self) void {
|
||||
world.entities.deinit();
|
||||
}
|
||||
|
||||
/// Gets a global value called `.global_tag` from the module named `.module_tag`
|
||||
pub fn get(world: *Self, module_tag: anytype, global_tag: anytype) @TypeOf(@field(
|
||||
@field(world.globals, @tagName(module_tag)),
|
||||
@tagName(global_tag),
|
||||
)) {
|
||||
return comptime @field(
|
||||
@field(world.globals, @tagName(module_tag)),
|
||||
@tagName(global_tag),
|
||||
);
|
||||
}
|
||||
|
||||
/// Sets a global value called `.global_tag` in the module named `.module_tag`
|
||||
pub fn set(
|
||||
world: *Self,
|
||||
comptime module_tag: anytype,
|
||||
comptime global_tag: anytype,
|
||||
value: @TypeOf(@field(
|
||||
@field(world.globals, @tagName(module_tag)),
|
||||
@tagName(global_tag),
|
||||
)),
|
||||
) void {
|
||||
comptime @field(
|
||||
@field(world.globals, @tagName(module_tag)),
|
||||
@tagName(global_tag),
|
||||
) = value;
|
||||
}
|
||||
|
||||
/// Tick sends the global 'tick' message to all modules that are subscribed to it.
|
||||
pub fn tick(world: *Self) void {
|
||||
_ = world;
|
||||
inline for (std.meta.fields(@TypeOf(modules))) |module_field| {
|
||||
const module = @field(modules, module_field.name);
|
||||
if (@hasField(@TypeOf(module), "messages")) {
|
||||
if (@hasField(module.messages, "tick")) module.update(.tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue