From d9882bf86eb19911a4de4ff271af8f5e41a79f31 Mon Sep 17 00:00:00 2001 From: copygirl Date: Wed, 6 Mar 2024 12:17:48 +0100 Subject: [PATCH] Add lookups for (some) builtin Flecs types --- src/builtin/flecs.core.zig | 158 +++++++++++++++++++++++++++++++++ src/builtin/flecs.doc.zig | 13 +++ src/builtin/flecs.pipeline.zig | 19 ++++ src/builtin/flecs.system.zig | 4 + src/builtin/flecs.zig | 6 ++ src/context.zig | 66 ++++++++++++++ src/main.zig | 3 + src/meta.zig | 10 +++ src/world.zig | 3 + 9 files changed, 282 insertions(+) create mode 100644 src/builtin/flecs.core.zig create mode 100644 src/builtin/flecs.doc.zig create mode 100644 src/builtin/flecs.pipeline.zig create mode 100644 src/builtin/flecs.system.zig create mode 100644 src/builtin/flecs.zig diff --git a/src/builtin/flecs.core.zig b/src/builtin/flecs.core.zig new file mode 100644 index 0000000..544b2d9 --- /dev/null +++ b/src/builtin/flecs.core.zig @@ -0,0 +1,158 @@ +//! Module with Flecs builtin components. + +const c = @import("../c.zig"); + +/// Entity associated with world. +pub const World = struct {}; + +// Builtin components + +/// Component that is added to entities which are components. +pub const Component: type = c.EcsComponent; + +/// Component used for entity names, symbols, etc. +pub const Identifier: type = c.EcsIdentifier; + +/// Used as `.{ Identifier, Name }` to store entity name. +pub const Name = struct {}; +/// Used as `.{ Identifier, Symbol }` to store entity symbol. +pub const Symbol = struct {}; +/// Used as `.{ Identifier, Alias }` to store entity alias. +pub const Alias = struct {}; + +/// Internal component to make (query) entities iterable. +pub const Iterable: type = c.EcsIterable; +/// Internal component that stores pointer to poly objects. +pub const Poly: type = c.EcsPoly; + +// Builtin tags + +/// Tag that is added to modules, which act as a container for logically +/// grouping tags, components, systems, other sub-modules and more. +pub const Module = struct {}; +/// Tag that is added to prefabsm, which can be inherited from, and are ignored by queries (by default). +pub const Prefab = struct {}; +/// Tag that added to disabled entities, which are ignored by queries (by default). +pub const Disabled = struct {}; +/// Tag that is added to private components, which hides them in the explorer (by default). +pub const Private = struct {}; + +/// Tag that is added to query entities. +pub const Query = struct {}; +/// Tag that is added to observer entities, which listen for events involving entities. +pub const Observer = struct {}; + +/// Internal tag for tracking IDs with special ID flags. +pub const Flag = struct {}; + +// Builtin relationships + +/// Relationship used for expressing inheritance. +pub const IsA = struct {}; +/// Relationship used for expressing hierarchies. +pub const ChildOf = struct {}; +/// Relationship used for expressing dependencies. +pub const DependsOn = struct {}; +/// Relationship used for expressing prefab slots. +pub const SlotOf = struct {}; + +// Relationship properties + +/// Trait that enables transitive evaluation of relationships. +pub const Transitive = struct {}; +/// Trait that enables reflexive evaluation of relationships. +pub const Reflexive = struct {}; +/// Trait that indicates an entity cannot be inherited from. +pub const Final = struct {}; +/// Trait that indicates it should not be inherited. +pub const DontInherit = struct {}; +/// Trait that ensures a pair cannot contain a value. +pub const Tag = struct {}; +/// Trait that indicates a relationship is acyclic. +pub const Acyclic = struct {}; +/// Trait that indicates a relationship is traversable. +pub const Traversable = struct {}; +/// Trait that ensures a relationship can only have one target. +pub const Exclusive = struct {}; +/// Trait that causes a relationship to be two-way. +pub const Symmetric = struct {}; +/// Trait for adding additional components when a component is added. +pub const With = struct {}; +/// Trait that indicates a component should always be overridden. +pub const AlwaysOverride = struct {}; +/// Trait for creating a non-fragmenting relationship. +pub const Union = struct {}; +/// Trait that enforces target of relationship is a child of . +pub const OneOf = struct {}; + +// Builtin event tags + +/// Event emitted when component is added. +pub const OnAdd = struct {}; +/// Event emitted when component is removed. +pub const OnRemove = struct {}; +/// Event emitted when component is set. +pub const OnSet = struct {}; +/// Event emitted when component is unset. +pub const UnSet = struct {}; + +/// Event emitted when table is created. +pub const OnTableCreate = struct {}; +/// Event emitted when table becomes empty. +pub const OnTableDelete = struct {}; +/// Event emitted when table becomes empty. +pub const OnTableEmpty = struct {}; +/// Event emitted when table becomes non-empty. +pub const OnTableFilled = struct {}; + +// Cleanup policies +// For example, components have `.{ OnDelete, Panic }` by default. + +/// Cleanup trait for specifying what happens when component is deleted. +pub const OnDelete = struct {}; +/// Cleanup trait for specifying what happens when pair target is deleted. +pub const OnDeleteTarget = struct {}; + +/// Cleanup action to delete entity from other entities. (default) +pub const Remove = struct {}; +/// Cleanup action to delete all entities that have this entity. +pub const Delete = struct {}; +/// Cleanup action to panic when entity is deleted. +pub const Panic = struct {}; + +// Query markers + +/// Query marker to express `$this` variable. +pub const This = struct {}; +pub const _This_Name = "$"; +/// Query marker to express match all wildcard (`*` in query DSL). +pub const Wildcard = struct {}; +pub const _Wildcard_Name = "*"; +/// Query marker to express match at least one wildcard (`_` in query DSL). +pub const Any = struct {}; +pub const _Any_Name = "_"; + +/// Query marker to express `==` operator. +pub const PredEq = struct {}; +/// Query marker to express `~=` operator. +pub const PredMatch = struct {}; +/// Query marker to express by-name lookup. +pub const PredLookup = struct {}; + +/// Query marker to express scope open. +pub const ScopeOpen = struct {}; +/// Query marker to express scope close. +pub const ScopeClose = struct {}; + +/// Tag used to indicate a query has no results. +pub const Empty = struct {}; + +// Misc + +/// Internal component that stores information for flattened trees. +pub const Target: type = c.EcsTarget; +/// Tag that when added to assembly automatically flattens tree. +pub const Flatten = struct {}; + +/// Sets default component hint for children of entity. +pub const DefaultChildComponent = struct {}; diff --git a/src/builtin/flecs.doc.zig b/src/builtin/flecs.doc.zig new file mode 100644 index 0000000..8ea0cb4 --- /dev/null +++ b/src/builtin/flecs.doc.zig @@ -0,0 +1,13 @@ +//! Flecs module with documentation components. + +const c = @import("../c.zig"); + +/// Component used to add documentation. +pub const Description = c.EcsDocDescription; + +/// Used as `.{ Description, Brief }` to add a brief description. +pub const Brief = struct {}; +/// Used as `.{ Description, Detail }` to add a detailed description. +pub const Detail = struct {}; +/// Used as `.{ Description, Link }` to add a link. +pub const Link = struct {}; diff --git a/src/builtin/flecs.pipeline.zig b/src/builtin/flecs.pipeline.zig new file mode 100644 index 0000000..c2fa90c --- /dev/null +++ b/src/builtin/flecs.pipeline.zig @@ -0,0 +1,19 @@ +//! Module that schedules and runs systems. + +pub const Pipeline = struct {}; +pub const Phase = struct {}; + +// Builtin pipeline phases +// See also "Selecting a Phase" in the Flecs docs. + +pub const OnStart = struct {}; +pub const PreFrame = struct {}; +pub const OnLoad = struct {}; +pub const PostLoad = struct {}; +pub const PreUpdate = struct {}; +pub const OnUpdate = struct {}; +pub const OnValidate = struct {}; +pub const PostUpdate = struct {}; +pub const PreStore = struct {}; +pub const OnStore = struct {}; +pub const PostFrame = struct {}; diff --git a/src/builtin/flecs.system.zig b/src/builtin/flecs.system.zig new file mode 100644 index 0000000..bcdd7c3 --- /dev/null +++ b/src/builtin/flecs.system.zig @@ -0,0 +1,4 @@ +//! Module that implements Flecs systems. + +pub const System = struct {}; +pub const TickSource = struct {}; diff --git a/src/builtin/flecs.zig b/src/builtin/flecs.zig new file mode 100644 index 0000000..d364127 --- /dev/null +++ b/src/builtin/flecs.zig @@ -0,0 +1,6 @@ +//! Flecs root module. + +pub const core = @import("./flecs.core.zig"); +pub const doc = @import("./flecs.doc.zig"); +pub const pipeline = @import("./flecs.pipeline.zig"); +pub const system = @import("./flecs.system.zig"); diff --git a/src/context.zig b/src/context.zig index 4efccc4..4586360 100644 --- a/src/context.zig +++ b/src/context.zig @@ -1,3 +1,5 @@ +const std = @import("std"); + const c = @import("./c.zig"); const meta = @import("./meta.zig"); @@ -84,5 +86,69 @@ pub fn Context(comptime ctx: anytype) type { else => @compileError("Value of type " ++ @typeName(T) ++ " can't be converted to Id"), }; } + + /// Registers type lookups for the builtin Flecs entities. + /// These are defined in the `./src/builtin/` directory. + pub fn registerFlecsLookups(world: *World) !void { + const error_writer = std.io.getStdErr().writer(); + const flecs = @import("./builtin/flecs.zig"); + const root = try lookupAndRegister(world, null, "flecs", flecs, error_writer); + try lookupAndRegisterDeclarations(world, root, flecs, error_writer); + } + + pub fn registerFlecsCoreLookups(world: *World) !void { + const error_writer = std.io.getStdErr().writer(); + const flecs = @import("./builtin/flecs.zig"); + const flecs_core = @import("./builtin/flecs.core.zig"); + const root = try lookupAndRegister(world, null, "flecs", flecs, error_writer); + const core = try lookupAndRegister(world, root, "core", flecs_core, error_writer); + try lookupAndRegisterDeclarations(world, core, flecs_core, error_writer); + } + + fn lookupAndRegister( + world: *World, + parent: ?Entity, + comptime name: []const u8, + comptime T: type, + error_writer: anytype, + ) !Entity { + if (world.lookupChild(parent, name)) |entity| { + const id_ptr = lookupMut(T); + if (id_ptr.* != 0 and id_ptr.* != entity.raw) { + if (!meta.isNull(error_writer)) + try std.fmt.format(error_writer, "Trying to register lookup for entity '{any}' as {d}, but it's already registered as {d}", .{ entity, entity.raw, id_ptr.* }); + return error.LookupIdMismatch; + } + id_ptr.* = entity.raw; + return entity; + } else { + if (!meta.isNull(error_writer)) { + if (parent) |p| + try std.fmt.format(error_writer, "Could not find child '{s}' of parent '{any}'", .{ name, p }) + else + try std.fmt.format(error_writer, "Could not find root entity '{s}'", .{name}); + } + return error.EntityNotFound; + } + } + + fn lookupAndRegisterDeclarations( + world: *World, + parent: Entity, + comptime T: type, + error_writer: anytype, + ) !void { + inline for (@typeInfo(T).Struct.decls) |decl| { + const ChildType = @field(T, decl.name); + if (@TypeOf(ChildType) != type) continue; + + comptime var child_name = decl.name; + if (@hasDecl(T, "_" ++ decl.name ++ "_Name")) + child_name = @field(T, "_" ++ decl.name ++ "_Name"); + + const child = try lookupAndRegister(world, parent, child_name, ChildType, error_writer); + try lookupAndRegisterDeclarations(world, child, ChildType, error_writer); + } + } }; } diff --git a/src/main.zig b/src/main.zig index ba9d26d..80b55ab 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,6 +6,9 @@ const Allocator = std.mem.Allocator; pub const errors = @import("./errors.zig"); pub const c = @import("./c.zig"); +// Allows access to types built into Flecs, useful for type lookups. +pub const flecs = @import("./builtin/flecs.zig"); + pub const FlecsError = errors.FlecsError; pub const Path = @import("./path.zig"); diff --git a/src/meta.zig b/src/meta.zig index b6f4cec..427bd2c 100644 --- a/src/meta.zig +++ b/src/meta.zig @@ -3,6 +3,16 @@ const std = @import("std"); +/// Returns if the provided value is either a `null` +/// constant or an optional type with a `null` value. +pub fn isNull(value: anytype) bool { + return switch (@typeInfo(@TypeOf(value))) { + .Null => true, + .Optional => value == null, + else => false, + }; +} + /// Returns if the provided type is a tuple. pub fn isTuple(comptime T: type) bool { return @typeInfo(T) == .Struct and @typeInfo(T).Struct.is_tuple; diff --git a/src/world.zig b/src/world.zig index e209cb4..451c67a 100644 --- a/src/world.zig +++ b/src/world.zig @@ -21,6 +21,7 @@ pub fn World(comptime ctx: anytype) type { std.debug.assert(flecszigble.is_initialized); var result = try flecszigble.allocator.create(Self); result.raw = c.ecs_init().?; + try Context.registerFlecsLookups(result); return result; } @@ -28,6 +29,7 @@ pub fn World(comptime ctx: anytype) type { std.debug.assert(flecszigble.is_initialized); var result = try flecszigble.allocator.create(Self); result.raw = c.ecs_init_w_args(args.len, args.ptr).?; + try Context.registerFlecsLookups(result); return result; } @@ -35,6 +37,7 @@ pub fn World(comptime ctx: anytype) type { std.debug.assert(flecszigble.is_initialized); var result = try flecszigble.allocator.create(Self); result.raw = c.ecs_mini().?; + try Context.registerFlecsCoreLookups(result); return result; }