HelloWorld
This commit is contained in:
commit
5fe126b696
39 changed files with 1861 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
.zig-cache/
|
||||||
27
build.zig
Normal file
27
build.zig
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
// --- TARGET ---
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
// --- MOD ---
|
||||||
|
const mod = b.addModule("app", .{
|
||||||
|
.root_source_file = b.path("src/app.zig"),
|
||||||
|
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- TESTS ---
|
||||||
|
const mod_tests = b.addTest(.{
|
||||||
|
.root_module = mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- RUN TESTS ---
|
||||||
|
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||||
|
|
||||||
|
// --- STEP BRO ---
|
||||||
|
const test_step = b.step("test", "Run tests");
|
||||||
|
test_step.dependOn(&run_mod_tests.step);
|
||||||
|
}
|
||||||
17
build.zig.zon
Normal file
17
build.zig.zon
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
.{
|
||||||
|
.name = .app,
|
||||||
|
|
||||||
|
.version = "0.0.0",
|
||||||
|
|
||||||
|
.fingerprint = 0xc96e70cffd7de500,
|
||||||
|
|
||||||
|
.minimum_zig_version = "0.16.0",
|
||||||
|
|
||||||
|
.dependencies = .{},
|
||||||
|
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
||||||
99
src/app.zig
Normal file
99
src/app.zig
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const root = @import("root");
|
||||||
|
pub const event = @import("event/root.zig");
|
||||||
|
pub const plugin = @import("plugin/root.zig");
|
||||||
|
pub const resource = @import("resource/root.zig");
|
||||||
|
pub const scheduler = @import("scheduler/root.zig");
|
||||||
|
pub const template = @import("template/root.zig");
|
||||||
|
pub const ecs = @import("world/root.zig");
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub const stage = struct {
|
||||||
|
pub const Init = struct {};
|
||||||
|
pub const Deinit = struct {};
|
||||||
|
pub const Inputs = struct {};
|
||||||
|
pub const Update = struct {};
|
||||||
|
pub const Draw = struct {};
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const Options = struct {
|
||||||
|
seed: u64 = 8490694,
|
||||||
|
};
|
||||||
|
pub const options: Options = if (@hasDecl(
|
||||||
|
root,
|
||||||
|
"app_options",
|
||||||
|
)) root.app_options else .{};
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
events: event.Storage,
|
||||||
|
plugins: plugin.Storage,
|
||||||
|
resources: resource.Storage,
|
||||||
|
schedules: scheduler.Storage,
|
||||||
|
world: ecs.World,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.events = .init(alloc),
|
||||||
|
.plugins = .init(alloc),
|
||||||
|
.resources = .init(alloc),
|
||||||
|
.schedules = .init(alloc),
|
||||||
|
.world = .init(alloc),
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.world.deinit();
|
||||||
|
self.events.deinit();
|
||||||
|
self.plugins.deinit();
|
||||||
|
self.resources.deinit();
|
||||||
|
self.schedules.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// EXAMPLES
|
||||||
|
//
|
||||||
|
|
||||||
|
const example = @import("example/root.zig");
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// ----------------------------------------------------
|
||||||
|
test "Main" {
|
||||||
|
// --- APP ---
|
||||||
|
var app: Self = .init(std.testing.allocator);
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
{ // PLUGINS
|
||||||
|
// --- ADD PLUGINS ---
|
||||||
|
try app.plugins.addMany(&.{
|
||||||
|
example.simple.plugin,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- BUILD PLUGINS ---
|
||||||
|
try app.plugins.build(.{
|
||||||
|
.app = &app,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
{ // SYSTEMS
|
||||||
|
try app.schedules.runMany(&.{
|
||||||
|
example.simple.stage.Init,
|
||||||
|
example.simple.stage.Inputs,
|
||||||
|
example.simple.stage.Update,
|
||||||
|
example.simple.stage.Draw,
|
||||||
|
example.simple.stage.Deinit,
|
||||||
|
}, .{
|
||||||
|
.app = &app,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/event/event.zig
Normal file
5
src/event/event.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
ptr: *anyopaque,
|
||||||
|
clear_fn: *const fn (*anyopaque) void,
|
||||||
|
destroy_fn: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||||
87
src/event/queue.zig
Normal file
87
src/event/queue.zig
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn Queue(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// TYPES
|
||||||
|
//
|
||||||
|
|
||||||
|
head: usize,
|
||||||
|
data: std.ArrayList(T),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.head = 0,
|
||||||
|
.data = .empty,
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.data.deinit(self.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn clear(self: *Self) void {
|
||||||
|
self.head = 0;
|
||||||
|
self.data.clearRetainingCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn append(self: *Self, data: T) !void {
|
||||||
|
try self.data.append(self.alloc, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn items(self: *Self) []const T {
|
||||||
|
return self.data.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn itemsMut(self: *Self) []T {
|
||||||
|
return self.data.items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn current(self: *Self) ?*T {
|
||||||
|
if (self.itemsMut().len == 0) return null;
|
||||||
|
return &self.itemsMut()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn consume(self: *Self) ?T {
|
||||||
|
if (self.head >= self.data.items.len)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
defer self.head += 1;
|
||||||
|
return self.data.items[self.head];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn len(self: *const Self) usize {
|
||||||
|
return self.data.items.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn isEmpty(self: *const Self) bool {
|
||||||
|
return self.len() == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
3
src/event/root.zig
Normal file
3
src/event/root.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub const Event = @import("event.zig");
|
||||||
|
pub const Storage = @import("storage.zig");
|
||||||
|
pub const Queue = @import("queue.zig").Queue;
|
||||||
165
src/event/storage.zig
Normal file
165
src/event/storage.zig
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const Event = @import("event.zig");
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
const Queue = @import("queue.zig").Queue;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
data: std.AutoHashMap(u64, Event),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.data = .init(alloc),
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
var it = self.data.valueIterator();
|
||||||
|
while (it.next()) |v| {
|
||||||
|
v.destroy_fn(v.ptr, self.alloc);
|
||||||
|
}
|
||||||
|
self.data.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn addMany(
|
||||||
|
self: *Self,
|
||||||
|
comptime Ts: []const type,
|
||||||
|
) !void {
|
||||||
|
inline for (Ts) |T| {
|
||||||
|
try self.add(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn add(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) !void {
|
||||||
|
// --- NEW QUEUE ---
|
||||||
|
const queue = try self.alloc.create(Queue(T));
|
||||||
|
queue.* = .init(self.alloc);
|
||||||
|
|
||||||
|
// --- NEW EVENT ---
|
||||||
|
try self.data.put(TypeID(T), .{
|
||||||
|
.ptr = queue,
|
||||||
|
|
||||||
|
.clear_fn = struct {
|
||||||
|
fn clear(ptr: *anyopaque) void {
|
||||||
|
const q: *Queue(T) = @ptrCast(@alignCast(ptr));
|
||||||
|
q.clear();
|
||||||
|
}
|
||||||
|
}.clear,
|
||||||
|
|
||||||
|
.destroy_fn = struct {
|
||||||
|
fn destroy(
|
||||||
|
ptr: *anyopaque,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) void {
|
||||||
|
const q: *Queue(T) = @ptrCast(@alignCast(ptr));
|
||||||
|
q.deinit();
|
||||||
|
alloc.destroy(q);
|
||||||
|
}
|
||||||
|
}.destroy,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn has(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) bool {
|
||||||
|
return self.data.contains(TypeID(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn sendMany(
|
||||||
|
self: *Self,
|
||||||
|
values: anytype,
|
||||||
|
) !void {
|
||||||
|
inline for (values) |value| {
|
||||||
|
try self.send(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn send(
|
||||||
|
self: *Self,
|
||||||
|
value: anytype,
|
||||||
|
) !void {
|
||||||
|
// --- METADATA ---
|
||||||
|
const T = @TypeOf(value);
|
||||||
|
const id = TypeID(T);
|
||||||
|
|
||||||
|
// --- AUTO REGISTER ---
|
||||||
|
if (!self.has(T)) {
|
||||||
|
try self.add(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- GET STORAGE ---
|
||||||
|
const storage =
|
||||||
|
self.data.getPtr(id) orelse
|
||||||
|
return error.NoEvent;
|
||||||
|
|
||||||
|
// --- CAST QUEUE ---
|
||||||
|
const queue: *Queue(T) = @ptrCast(
|
||||||
|
@alignCast(storage.ptr),
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- APPEND EVENT ---
|
||||||
|
try queue.append(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn get(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) ?*Queue(T) {
|
||||||
|
const event = self.data.getPtr(TypeID(T)) orelse return null;
|
||||||
|
return @ptrCast(@alignCast(event.ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn clear(self: *Self) void {
|
||||||
|
var it = self.data.valueIterator();
|
||||||
|
while (it.next()) |storage| {
|
||||||
|
storage.clear_fn(storage.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn current(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) ?*T {
|
||||||
|
const queue = self.get(T) orelse return null;
|
||||||
|
return queue.current();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn consume(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) ?T {
|
||||||
|
const queue = self.get(T) orelse return null;
|
||||||
|
return queue.consume();
|
||||||
|
}
|
||||||
1
src/example/root.zig
Normal file
1
src/example/root.zig
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
pub const simple = @import("simple/root.zig");
|
||||||
43
src/example/simple/component.zig
Normal file
43
src/example/simple/component.zig
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Transform = struct {
|
||||||
|
pos: @Vector(2, f32) = .{ 0, 0 },
|
||||||
|
size: @Vector(2, f32) = .{ 1, 1 },
|
||||||
|
|
||||||
|
pub fn format(
|
||||||
|
self: @This(),
|
||||||
|
writer: *std.Io.Writer,
|
||||||
|
) !void {
|
||||||
|
try writer.print("pos=({}, {}) size=({}, {})", .{
|
||||||
|
self.pos[0], self.pos[1],
|
||||||
|
self.size[0], self.size[1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Material = struct {
|
||||||
|
albedo: @Vector(4, u8) = .{ 255, 255, 255, 255 },
|
||||||
|
|
||||||
|
pub fn format(
|
||||||
|
self: @This(),
|
||||||
|
writer: *std.Io.Writer,
|
||||||
|
) std.Io.Writer.Error!void {
|
||||||
|
try writer.print("rgba({}, {}, {}, {})", .{
|
||||||
|
self.albedo[0],
|
||||||
|
self.albedo[1],
|
||||||
|
self.albedo[2],
|
||||||
|
self.albedo[3],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Player = struct {
|
||||||
|
name: []const u8,
|
||||||
|
|
||||||
|
pub fn format(
|
||||||
|
self: @This(),
|
||||||
|
writer: *std.Io.Writer,
|
||||||
|
) std.Io.Writer.Error!void {
|
||||||
|
try writer.print("\"{s}\"", .{self.name});
|
||||||
|
}
|
||||||
|
};
|
||||||
18
src/example/simple/event.zig
Normal file
18
src/example/simple/event.zig
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
pub const Login = struct {
|
||||||
|
username: []const u8,
|
||||||
|
password: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Register = struct {
|
||||||
|
username: []const u8,
|
||||||
|
password: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Connect = struct {
|
||||||
|
ip: []const u8 = "localhost",
|
||||||
|
port: u16 = 4444,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const ConnectionAccepted = struct {
|
||||||
|
id: u32,
|
||||||
|
};
|
||||||
40
src/example/simple/plugin.zig
Normal file
40
src/example/simple/plugin.zig
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const App = @import("../../app.zig");
|
||||||
|
const system = @import("system.zig");
|
||||||
|
const stage = @import("stage.zig");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub const plugin: App.plugin.Plugin = .{
|
||||||
|
.name = "Simple",
|
||||||
|
.description = "Simple plugin example",
|
||||||
|
.build = build,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn build(
|
||||||
|
self: *App.plugin.Plugin,
|
||||||
|
ctx: App.plugin.Context,
|
||||||
|
) !void {
|
||||||
|
|
||||||
|
// --- DEBUG ---
|
||||||
|
std.debug.print(
|
||||||
|
"\x1b[33mPLUGIN\x1b[0m\n" ++
|
||||||
|
\\| Name: {s}
|
||||||
|
\\| Description: {s}
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
.{
|
||||||
|
self.name,
|
||||||
|
self.description,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- ADD SYSTEMS ---
|
||||||
|
try ctx.app.schedules.addMany(stage.Init, &.{
|
||||||
|
system.test_resources,
|
||||||
|
system.test_events,
|
||||||
|
system.test_ecs,
|
||||||
|
});
|
||||||
|
}
|
||||||
2
src/example/simple/resource.zig
Normal file
2
src/example/simple/resource.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub const Server = struct { id: u32 = 0 };
|
||||||
|
pub const Client = struct { id: u32 = 0 };
|
||||||
3
src/example/simple/root.zig
Normal file
3
src/example/simple/root.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub const plugin = @import("plugin.zig").plugin;
|
||||||
|
pub const system = @import("system.zig");
|
||||||
|
pub const stage = @import("stage.zig");
|
||||||
5
src/example/simple/stage.zig
Normal file
5
src/example/simple/stage.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
pub const Init = struct {};
|
||||||
|
pub const Deinit = struct {};
|
||||||
|
pub const Inputs = struct {};
|
||||||
|
pub const Update = struct {};
|
||||||
|
pub const Draw = struct {};
|
||||||
207
src/example/simple/system.zig
Normal file
207
src/example/simple/system.zig
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const App = @import("../../app.zig");
|
||||||
|
|
||||||
|
const event = @import("event.zig");
|
||||||
|
const resource = @import("resource.zig");
|
||||||
|
const component = @import("component.zig");
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn test_resources(
|
||||||
|
ctx: App.scheduler.Context,
|
||||||
|
) !void {
|
||||||
|
// --- SET NEW ---
|
||||||
|
try ctx.app.resources.set(resource.Server{});
|
||||||
|
try ctx.app.resources.set(resource.Client{});
|
||||||
|
|
||||||
|
// --- CONTAINS DEBUG ---
|
||||||
|
std.debug.print(
|
||||||
|
"\x1b[34mRESOURCES EXIST\x1b[0m\n" ++
|
||||||
|
\\| Server: {}
|
||||||
|
\\| Client: {}
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
.{
|
||||||
|
ctx.app.resources.contains(resource.Server),
|
||||||
|
ctx.app.resources.contains(resource.Client),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- GET READONLY RESOURCES ---
|
||||||
|
_ = try ctx.app.resources.getMany(struct {
|
||||||
|
server: ?*const resource.Server,
|
||||||
|
client: ?*const resource.Client,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const User = struct {
|
||||||
|
id: u32 = 0,
|
||||||
|
username: []const u8,
|
||||||
|
password: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn test_events(
|
||||||
|
ctx: App.scheduler.Context,
|
||||||
|
) !void {
|
||||||
|
// --- DEBUG ---
|
||||||
|
std.debug.print(
|
||||||
|
"\x1b[35mEVENTS\x1b[0m\n",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- CLEAR EVENTS ---
|
||||||
|
defer ctx.app.events.clear();
|
||||||
|
|
||||||
|
// --- USER LIST ---
|
||||||
|
var users: std.ArrayList(User) = .empty;
|
||||||
|
defer users.deinit(ctx.app.alloc);
|
||||||
|
|
||||||
|
// --- ID`S ---
|
||||||
|
var next_register_id: u32 = 0;
|
||||||
|
var next_connected_id: u32 = 0;
|
||||||
|
|
||||||
|
// --- ADD ADMIN USER ---
|
||||||
|
try users.append(ctx.app.alloc, .{
|
||||||
|
.id = next_register_id,
|
||||||
|
.username = "admin",
|
||||||
|
.password = "1234",
|
||||||
|
});
|
||||||
|
|
||||||
|
{ // NEW USER
|
||||||
|
try ctx.app.events.sendMany(.{
|
||||||
|
event.Register{
|
||||||
|
.username = "abux",
|
||||||
|
.password = "qwerty",
|
||||||
|
},
|
||||||
|
event.Login{
|
||||||
|
.username = "abux",
|
||||||
|
.password = "qwerty",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- REGISTER LOOP ---
|
||||||
|
if (ctx.app.events.get(event.Register)) |queue| {
|
||||||
|
for (queue.items()) |reg| {
|
||||||
|
next_register_id += 1;
|
||||||
|
try users.append(ctx.app.alloc, .{
|
||||||
|
.id = next_register_id,
|
||||||
|
.username = reg.username,
|
||||||
|
.password = reg.password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- LOGIN LOOP ---
|
||||||
|
if (ctx.app.events.get(event.Login)) |queue| {
|
||||||
|
for (queue.items()) |login| {
|
||||||
|
for (users.items) |user| {
|
||||||
|
// --- CHECK CREDS ---
|
||||||
|
if (!std.mem.eql(u8, user.username, login.username)) continue;
|
||||||
|
if (!std.mem.eql(u8, user.password, login.password)) continue;
|
||||||
|
|
||||||
|
// --- SEND ACCEPTED CONNECTION ---
|
||||||
|
next_connected_id += 1;
|
||||||
|
try ctx.app.events.send(event.ConnectionAccepted{
|
||||||
|
.id = next_connected_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- CONNECTED ---
|
||||||
|
if (ctx.app.events.consume(event.ConnectionAccepted)) |ca| {
|
||||||
|
std.debug.print(
|
||||||
|
\\ | Connected:
|
||||||
|
\\ | ID: {}
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
.{
|
||||||
|
ca.id,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- DEBUG ---
|
||||||
|
std.debug.print(
|
||||||
|
"\x1b[31mUSERS\x1b[0m\n",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
for (users.items) |user| {
|
||||||
|
std.debug.print(
|
||||||
|
\\ | ID: {}
|
||||||
|
\\ | Username: {s}
|
||||||
|
\\ | Password: {s}
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
.{
|
||||||
|
user.id,
|
||||||
|
user.username,
|
||||||
|
user.password,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn test_ecs(
|
||||||
|
ctx: App.scheduler.Context,
|
||||||
|
) !void {
|
||||||
|
// --- SPAWN ENTITY ---
|
||||||
|
try ctx.app.world.spawn(.{
|
||||||
|
component.Player{ .name = "abux" },
|
||||||
|
component.Material{},
|
||||||
|
component.Transform{},
|
||||||
|
});
|
||||||
|
try ctx.app.world.spawn(.{
|
||||||
|
component.Material{},
|
||||||
|
component.Transform{},
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- DEBUG ---
|
||||||
|
std.debug.print(
|
||||||
|
"\x1b[35mENTITIES\x1b[0m\n",
|
||||||
|
.{},
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- QUERY ---
|
||||||
|
var query = ctx.app.world.query(struct {
|
||||||
|
player: ?*component.Player,
|
||||||
|
material: *component.Material,
|
||||||
|
transform: *component.Transform,
|
||||||
|
}, .{});
|
||||||
|
while (query.next()) |e| {
|
||||||
|
// --- UPDATE POS ---
|
||||||
|
e.transform.pos[0] += 1;
|
||||||
|
e.transform.pos[1] += 1;
|
||||||
|
|
||||||
|
// --- CHANGE PLAYER'S COLOR ---
|
||||||
|
if (e.player) |_| {
|
||||||
|
e.material.albedo = .{ 20, 200, 20, 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ENTITIES DEBUG ---
|
||||||
|
std.debug.print("\x1b[36m", .{});
|
||||||
|
std.debug.print(
|
||||||
|
\\| ID: {}
|
||||||
|
\\| Components:
|
||||||
|
\\ | Player: {?f}
|
||||||
|
\\ | Material: {f}
|
||||||
|
\\ | Transform: {f}
|
||||||
|
\\
|
||||||
|
,
|
||||||
|
.{
|
||||||
|
query.entity(),
|
||||||
|
e.player,
|
||||||
|
e.material,
|
||||||
|
e.transform,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
std.debug.print("\x1b[0m", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/plugin/context.zig
Normal file
3
src/plugin/context.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
const App = @import("../app.zig");
|
||||||
|
|
||||||
|
app: *App,
|
||||||
14
src/plugin/plugin.zig
Normal file
14
src/plugin/plugin.zig
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
const Context = @import("context.zig");
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
// --- ID ---
|
||||||
|
name: []const u8,
|
||||||
|
description: []const u8 = "",
|
||||||
|
version: [3]u8 = .{ 0, 0, 0 },
|
||||||
|
|
||||||
|
// --- CALLBACKS ---
|
||||||
|
build: *const fn (*Self, Context) anyerror!void,
|
||||||
3
src/plugin/root.zig
Normal file
3
src/plugin/root.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub const Plugin = @import("plugin.zig");
|
||||||
|
pub const Storage = @import("storage.zig");
|
||||||
|
pub const Context = @import("context.zig");
|
||||||
61
src/plugin/storage.zig
Normal file
61
src/plugin/storage.zig
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const Plugin = @import("plugin.zig");
|
||||||
|
const Context = @import("context.zig");
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
data: std.ArrayList(Plugin),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.data = .empty,
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.data.deinit(self.alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn add(
|
||||||
|
self: *Self,
|
||||||
|
plugin: Plugin,
|
||||||
|
) !void {
|
||||||
|
try self.data.append(
|
||||||
|
self.alloc,
|
||||||
|
plugin,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn addMany(
|
||||||
|
self: *Self,
|
||||||
|
plugins: []const Plugin,
|
||||||
|
) !void {
|
||||||
|
for (plugins) |plugin| {
|
||||||
|
try self.add(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn build(
|
||||||
|
self: *Self,
|
||||||
|
ctx: Context,
|
||||||
|
) !void {
|
||||||
|
for (self.data.items) |*plugin| {
|
||||||
|
try plugin.build(plugin, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/resource/resource.zig
Normal file
5
src/resource/resource.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
ptr: *anyopaque,
|
||||||
|
destroy: *const fn (*anyopaque, std.mem.Allocator) void,
|
||||||
2
src/resource/root.zig
Normal file
2
src/resource/root.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub const Resource = @import("resource.zig");
|
||||||
|
pub const Storage = @import("storage.zig");
|
||||||
170
src/resource/storage.zig
Normal file
170
src/resource/storage.zig
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const Resource = @import("resource.zig");
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
data: std.AutoHashMap(u64, Resource),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.data = .init(alloc),
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
var it = self.data.valueIterator();
|
||||||
|
while (it.next()) |v| {
|
||||||
|
v.destroy(v.ptr, self.alloc);
|
||||||
|
}
|
||||||
|
self.data.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn set(
|
||||||
|
self: *Self,
|
||||||
|
value: anytype,
|
||||||
|
) !void {
|
||||||
|
_ = try self.getOrSet(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn remove(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) void {
|
||||||
|
_ = self.data.remove(TypeID(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getOrSet(
|
||||||
|
self: *Self,
|
||||||
|
value: anytype,
|
||||||
|
) !*@TypeOf(value) {
|
||||||
|
// --- DATA ---
|
||||||
|
const T = @TypeOf(value);
|
||||||
|
const id = TypeID(T);
|
||||||
|
|
||||||
|
// --- RETURN IF ALREADY EXISTS ---
|
||||||
|
if (self.data.getPtr(id)) |res| {
|
||||||
|
const v: *T = @ptrCast(@alignCast(res.ptr));
|
||||||
|
v.* = value;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- NEW PTR ---
|
||||||
|
const ptr = try self.alloc.create(T);
|
||||||
|
errdefer self.alloc.destroy(ptr);
|
||||||
|
|
||||||
|
// --- ASSIGN VALUE ---
|
||||||
|
ptr.* = value;
|
||||||
|
|
||||||
|
// --- ADD NEW ---
|
||||||
|
try self.data.put(id, .{
|
||||||
|
.ptr = ptr,
|
||||||
|
.destroy = struct {
|
||||||
|
pub fn destroy(_ptr: *anyopaque, _alloc: std.mem.Allocator) void {
|
||||||
|
const v: *T = @ptrCast(@alignCast(_ptr));
|
||||||
|
_alloc.destroy(v);
|
||||||
|
}
|
||||||
|
}.destroy,
|
||||||
|
});
|
||||||
|
|
||||||
|
// --- RESULT ---
|
||||||
|
return @ptrCast(@alignCast(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getPtr(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) ?*T {
|
||||||
|
const res = self.data.getPtr(TypeID(T)) orelse return null;
|
||||||
|
return @ptrCast(@alignCast(res.ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getConstPtr(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) ?*const T {
|
||||||
|
const ptr = self.getPtr(T) orelse return null;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn require(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) error{ResourceNotSet}!*T {
|
||||||
|
return self.getPtr(T) orelse return error.ResourceNotSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Example
|
||||||
|
/// ```
|
||||||
|
/// const res = try app.resources.getMany(struct {
|
||||||
|
/// server: ?*const Server,
|
||||||
|
/// client: ?*const Client,
|
||||||
|
/// player: *Entity,
|
||||||
|
/// });
|
||||||
|
/// ```
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn getMany(
|
||||||
|
self: *Self,
|
||||||
|
comptime Result: type,
|
||||||
|
) error{ResourceNotSet}!Result {
|
||||||
|
// --- RESULT ---
|
||||||
|
var result: Result = undefined;
|
||||||
|
|
||||||
|
// --- GET FIELDS ---
|
||||||
|
inline for (std.meta.fields(Result)) |field| {
|
||||||
|
// --- FIELD INFO ---
|
||||||
|
const FieldType = field.type;
|
||||||
|
|
||||||
|
switch (@typeInfo(FieldType)) {
|
||||||
|
.optional => |opt| {
|
||||||
|
const ptr_info = @typeInfo(opt.child).pointer;
|
||||||
|
@field(result, field.name) = self.getPtr(ptr_info.child);
|
||||||
|
},
|
||||||
|
|
||||||
|
.pointer => |ptr| {
|
||||||
|
@field(result, field.name) =
|
||||||
|
self.getPtr(ptr.child) orelse
|
||||||
|
return error.ResourceNotSet;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => @compileError(
|
||||||
|
"getMany fields must be *T or ?*T; field '" ++ field.name ++ "' has type '" ++
|
||||||
|
@typeName(FieldType) ++ "'",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn contains(
|
||||||
|
self: *const Self,
|
||||||
|
comptime T: type,
|
||||||
|
) bool {
|
||||||
|
return self.data.contains(TypeID(T));
|
||||||
|
}
|
||||||
3
src/scheduler/context.zig
Normal file
3
src/scheduler/context.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
const App = @import("../app.zig");
|
||||||
|
|
||||||
|
app: *App,
|
||||||
3
src/scheduler/root.zig
Normal file
3
src/scheduler/root.zig
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub const System = @import("system.zig");
|
||||||
|
pub const Storage = @import("storage.zig");
|
||||||
|
pub const Context = @import("context.zig");
|
||||||
107
src/scheduler/storage.zig
Normal file
107
src/scheduler/storage.zig
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const System = @import("system.zig");
|
||||||
|
const Context = @import("context.zig");
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
data: std.ArrayList(System),
|
||||||
|
stages: std.AutoHashMap(u64, usize),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.data = .empty,
|
||||||
|
.stages = .init(alloc),
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.data.deinit(self.alloc);
|
||||||
|
self.stages.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn addStage(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) !void {
|
||||||
|
// --- GET ID ---
|
||||||
|
const stage_id = TypeID(T);
|
||||||
|
|
||||||
|
// --- INSERT ---
|
||||||
|
if (self.stages.contains(stage_id)) return;
|
||||||
|
try self.stages.put(stage_id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn add(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
execute: *const fn (Context) anyerror!void,
|
||||||
|
) !void {
|
||||||
|
// --- GET ID ---
|
||||||
|
const stage_id = TypeID(T);
|
||||||
|
|
||||||
|
// --- NEW SYSTEM ---
|
||||||
|
try self.data.append(self.alloc, .{
|
||||||
|
.stage_id = stage_id,
|
||||||
|
.execute = execute,
|
||||||
|
});
|
||||||
|
errdefer _ = self.data.swapRemove(self.data.items.len - 1);
|
||||||
|
|
||||||
|
// --- NEW STAGE ---
|
||||||
|
try self.addStage(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn addMany(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
values: []const *const fn (Context) anyerror!void,
|
||||||
|
) !void {
|
||||||
|
for (values) |exe| {
|
||||||
|
try self.add(T, exe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn run(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
ctx: Context,
|
||||||
|
) !void {
|
||||||
|
// --- GET ID ---
|
||||||
|
const stage_id = TypeID(T);
|
||||||
|
|
||||||
|
// --- RUN ---
|
||||||
|
for (self.data.items) |sys| {
|
||||||
|
if (sys.stage_id != stage_id) continue;
|
||||||
|
try sys.execute(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn runMany(
|
||||||
|
self: *Self,
|
||||||
|
comptime Ts: []const type,
|
||||||
|
ctx: Context,
|
||||||
|
) !void {
|
||||||
|
inline for (Ts) |T| {
|
||||||
|
try self.run(T, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/scheduler/system.zig
Normal file
4
src/scheduler/system.zig
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
const Context = @import("context.zig");
|
||||||
|
|
||||||
|
stage_id: u64,
|
||||||
|
execute: *const fn (ctx: Context) anyerror!void,
|
||||||
8
src/template/handle.zig
Normal file
8
src/template/handle.zig
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
pub fn Handle(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
pub const Type: type = T;
|
||||||
|
|
||||||
|
idx: u32 = 0,
|
||||||
|
gen: u32 = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
2
src/template/root.zig
Normal file
2
src/template/root.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub const TypeID = @import("type_id.zig").of;
|
||||||
|
pub const Handle = @import("handle.zig").Handle;
|
||||||
9
src/template/type_id.zig
Normal file
9
src/template/type_id.zig
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const root = @import("../app.zig");
|
||||||
|
|
||||||
|
pub fn of(comptime T: type) u64 {
|
||||||
|
return std.hash.Wyhash.hash(
|
||||||
|
root.options.seed,
|
||||||
|
@typeName(T),
|
||||||
|
);
|
||||||
|
}
|
||||||
143
src/world/archetype.zig
Normal file
143
src/world/archetype.zig
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const Signature = @import("signature.zig");
|
||||||
|
const Column = @import("column.zig");
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
signature: Signature,
|
||||||
|
type_map: std.AutoHashMap(u64, usize),
|
||||||
|
entities: std.ArrayList(u64),
|
||||||
|
columns: std.ArrayList(Column),
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(
|
||||||
|
signature: Signature,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) Self {
|
||||||
|
return .{
|
||||||
|
.signature = signature,
|
||||||
|
.type_map = .init(alloc),
|
||||||
|
.entities = .empty,
|
||||||
|
.columns = .empty,
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
for (self.columns.items) |*col| {
|
||||||
|
col.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.columns.deinit(self.alloc);
|
||||||
|
self.entities.deinit(self.alloc);
|
||||||
|
self.type_map.deinit();
|
||||||
|
|
||||||
|
self.alloc.free(self.signature.ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn addColumn(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
) !void {
|
||||||
|
const idx = self.columns.items.len;
|
||||||
|
|
||||||
|
try self.columns.append(
|
||||||
|
self.alloc,
|
||||||
|
try .init(T, self.alloc),
|
||||||
|
);
|
||||||
|
|
||||||
|
try self.type_map.put(
|
||||||
|
TypeID(T),
|
||||||
|
idx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn addColumns(
|
||||||
|
self: *Self,
|
||||||
|
components: anytype,
|
||||||
|
) !void {
|
||||||
|
inline for (components) |T| {
|
||||||
|
try self.addColumn(T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn add(
|
||||||
|
self: *Self,
|
||||||
|
entity: u64,
|
||||||
|
components: anytype,
|
||||||
|
) !void {
|
||||||
|
const fields = std.meta.fields(@TypeOf(components));
|
||||||
|
|
||||||
|
if (fields.len != self.columns.items.len) {
|
||||||
|
return error.MissingComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// const row = self.entities.items.len;
|
||||||
|
try self.entities.append(self.alloc, entity);
|
||||||
|
|
||||||
|
inline for (fields) |field| {
|
||||||
|
const component = @field(components, field.name);
|
||||||
|
|
||||||
|
const T = @TypeOf(component);
|
||||||
|
|
||||||
|
const idx = self.type_map.get(TypeID(T)) orelse {
|
||||||
|
return error.ComponentNotFound;
|
||||||
|
};
|
||||||
|
|
||||||
|
try self.columns.items[idx].append(&component);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for (self.columns.items) |*col| {
|
||||||
|
// if (col. != row + 1) {
|
||||||
|
// return error.RowDesync;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn get(
|
||||||
|
self: *Self,
|
||||||
|
comptime T: type,
|
||||||
|
row: usize,
|
||||||
|
) ?*T {
|
||||||
|
const idx = self.type_map.get(TypeID(T)) orelse return null;
|
||||||
|
const ptr = self.columns.items[idx].getPtr(row) orelse return null;
|
||||||
|
return @ptrCast(@alignCast(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn remove(
|
||||||
|
self: *Self,
|
||||||
|
row: usize,
|
||||||
|
) ?u64 {
|
||||||
|
const last_entity = self.entities.items[self.entities.items.len - 1];
|
||||||
|
|
||||||
|
_ = self.entities.swapRemove(row);
|
||||||
|
|
||||||
|
for (self.columns.items) |*col| {
|
||||||
|
col.swapRemove(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row < self.entities.items.len) {
|
||||||
|
return last_entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
151
src/world/column.zig
Normal file
151
src/world/column.zig
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
const TypedColumn = @import("column_typed.zig").TypedColumn;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
storage: *anyopaque,
|
||||||
|
|
||||||
|
type_id: u64,
|
||||||
|
|
||||||
|
elem_size: usize,
|
||||||
|
alignment: usize,
|
||||||
|
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
append_fn: *const fn (
|
||||||
|
storage: *anyopaque,
|
||||||
|
value: *const anyopaque,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) anyerror!void,
|
||||||
|
|
||||||
|
swap_remove_fn: *const fn (
|
||||||
|
storage: *anyopaque,
|
||||||
|
idx: usize,
|
||||||
|
) void,
|
||||||
|
|
||||||
|
get_ptr_fn: *const fn (
|
||||||
|
storage: *anyopaque,
|
||||||
|
idx: usize,
|
||||||
|
) ?*anyopaque,
|
||||||
|
|
||||||
|
deinit_fn: *const fn (
|
||||||
|
storage: *anyopaque,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) void,
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn init(
|
||||||
|
comptime T: type,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) !Self {
|
||||||
|
const typed = try alloc.create(TypedColumn(T));
|
||||||
|
|
||||||
|
typed.* = TypedColumn(T).init();
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.storage = typed,
|
||||||
|
|
||||||
|
.type_id = TypeID(T),
|
||||||
|
|
||||||
|
.elem_size = @sizeOf(T),
|
||||||
|
.alignment = @alignOf(T),
|
||||||
|
|
||||||
|
.alloc = alloc,
|
||||||
|
|
||||||
|
.append_fn = struct {
|
||||||
|
fn f(
|
||||||
|
storage: *anyopaque,
|
||||||
|
value: *const anyopaque,
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
) !void {
|
||||||
|
const col: *TypedColumn(T) = @ptrCast(@alignCast(storage));
|
||||||
|
try col.append(value, allocator);
|
||||||
|
}
|
||||||
|
}.f,
|
||||||
|
|
||||||
|
.swap_remove_fn = struct {
|
||||||
|
fn f(
|
||||||
|
storage: *anyopaque,
|
||||||
|
idx: usize,
|
||||||
|
) void {
|
||||||
|
const col: *TypedColumn(T) = @ptrCast(@alignCast(storage));
|
||||||
|
col.swapRemove(idx);
|
||||||
|
}
|
||||||
|
}.f,
|
||||||
|
|
||||||
|
.get_ptr_fn = struct {
|
||||||
|
fn f(
|
||||||
|
storage: *anyopaque,
|
||||||
|
idx: usize,
|
||||||
|
) ?*anyopaque {
|
||||||
|
const col: *TypedColumn(T) = @ptrCast(@alignCast(storage));
|
||||||
|
if (idx >= col.items.items.len) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return &col.items.items[idx];
|
||||||
|
}
|
||||||
|
}.f,
|
||||||
|
|
||||||
|
.deinit_fn = struct {
|
||||||
|
fn f(
|
||||||
|
storage: *anyopaque,
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
) void {
|
||||||
|
const col: *TypedColumn(T) = @ptrCast(@alignCast(storage));
|
||||||
|
col.deinit(allocator);
|
||||||
|
allocator.destroy(col);
|
||||||
|
}
|
||||||
|
}.f,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
self.deinit_fn(
|
||||||
|
self.storage,
|
||||||
|
self.alloc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn append(
|
||||||
|
self: *Self,
|
||||||
|
value: *const anyopaque,
|
||||||
|
) !void {
|
||||||
|
try self.append_fn(
|
||||||
|
self.storage,
|
||||||
|
value,
|
||||||
|
self.alloc,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn swapRemove(
|
||||||
|
self: *Self,
|
||||||
|
idx: usize,
|
||||||
|
) void {
|
||||||
|
self.swap_remove_fn(
|
||||||
|
self.storage,
|
||||||
|
idx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn getPtr(
|
||||||
|
self: *Self,
|
||||||
|
idx: usize,
|
||||||
|
) ?*anyopaque {
|
||||||
|
return self.get_ptr_fn(
|
||||||
|
self.storage,
|
||||||
|
idx,
|
||||||
|
);
|
||||||
|
}
|
||||||
61
src/world/column_typed.zig
Normal file
61
src/world/column_typed.zig
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn TypedColumn(comptime T: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
items: std.ArrayList(T),
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn init() @This() {
|
||||||
|
return .{
|
||||||
|
.items = .empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn deinit(
|
||||||
|
self: *Self,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) void {
|
||||||
|
self.items.deinit(alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn append(
|
||||||
|
self: *Self,
|
||||||
|
value: *const anyopaque,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
) !void {
|
||||||
|
const typed: *const T = @ptrCast(@alignCast(value));
|
||||||
|
try self.items.append(alloc, typed.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn swapRemove(
|
||||||
|
self: *Self,
|
||||||
|
idx: usize,
|
||||||
|
) void {
|
||||||
|
_ = self.items.swapRemove(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ------------------------------------------
|
||||||
|
/// ------------------------------------------
|
||||||
|
pub fn getPtr(
|
||||||
|
self: *Self,
|
||||||
|
idx: usize,
|
||||||
|
) *anyopaque {
|
||||||
|
return &self.items.items[idx];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
2
src/world/entity_record.zig
Normal file
2
src/world/entity_record.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
archetype_idx: usize,
|
||||||
|
row: usize,
|
||||||
140
src/world/query.zig
Normal file
140
src/world/query.zig
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const World = @import("world.zig");
|
||||||
|
const Archetype = @import("archetype.zig");
|
||||||
|
const QueryFilters = @import("query_filters.zig");
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn Query(
|
||||||
|
comptime Result: type,
|
||||||
|
comptime Filters: QueryFilters,
|
||||||
|
) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
const result_fields = std.meta.fields(Result);
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
world: *World,
|
||||||
|
arch_cursor: usize,
|
||||||
|
row_cursor: usize,
|
||||||
|
|
||||||
|
current_arch: ?*Archetype,
|
||||||
|
current_row: usize,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(world: *World) Self {
|
||||||
|
return .{
|
||||||
|
.world = world,
|
||||||
|
.arch_cursor = 0,
|
||||||
|
.row_cursor = 0,
|
||||||
|
|
||||||
|
.current_arch = null,
|
||||||
|
.current_row = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn matches(arch: *Archetype) bool {
|
||||||
|
// --- REQUIRED COMPONENTS ---
|
||||||
|
inline for (result_fields) |field| {
|
||||||
|
// TODO: Clean this shit up
|
||||||
|
const FieldT = field.type;
|
||||||
|
const is_optional = @typeInfo(FieldT) == .optional;
|
||||||
|
|
||||||
|
const PtrT = if (is_optional) @typeInfo(FieldT).optional.child else FieldT;
|
||||||
|
const T = std.meta.Child(PtrT);
|
||||||
|
|
||||||
|
if (!arch.type_map.contains(TypeID(T))) {
|
||||||
|
// --- SKIP OPTIONALS ---
|
||||||
|
if (!is_optional) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- WITH ---
|
||||||
|
inline for (Filters.with) |T| {
|
||||||
|
if (!arch.type_map.contains(TypeID(T))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- WITHOUT ---
|
||||||
|
inline for (Filters.without) |T| {
|
||||||
|
if (arch.type_map.contains(TypeID(T))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn next(self: *Self) ?Result {
|
||||||
|
while (self.arch_cursor < self.world.archetypes.items.len) {
|
||||||
|
var arch = &self.world.archetypes.items[self.arch_cursor];
|
||||||
|
|
||||||
|
// --- SKIP ARCHETYPES ---
|
||||||
|
if (!matches(arch)) {
|
||||||
|
self.arch_cursor += 1;
|
||||||
|
self.row_cursor = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- END ROWS ---
|
||||||
|
if (self.row_cursor >= arch.entities.items.len) {
|
||||||
|
self.arch_cursor += 1;
|
||||||
|
self.row_cursor = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = self.row_cursor;
|
||||||
|
|
||||||
|
self.row_cursor += 1;
|
||||||
|
self.current_arch = arch;
|
||||||
|
self.current_row = row;
|
||||||
|
|
||||||
|
var result: Result = undefined;
|
||||||
|
|
||||||
|
inline for (result_fields) |field| {
|
||||||
|
// TODO: Clean this shit up
|
||||||
|
const FieldT = field.type;
|
||||||
|
|
||||||
|
const is_optional = @typeInfo(FieldT) == .optional;
|
||||||
|
|
||||||
|
const PtrT = if (is_optional) @typeInfo(FieldT).optional.child else FieldT;
|
||||||
|
|
||||||
|
const T = std.meta.Child(PtrT);
|
||||||
|
|
||||||
|
const ptr = arch.get(T, row);
|
||||||
|
|
||||||
|
if (is_optional) {
|
||||||
|
@field(result, field.name) = ptr;
|
||||||
|
} else {
|
||||||
|
@field(result, field.name) = ptr orelse return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn entity(self: *const Self) u64 {
|
||||||
|
return self.current_arch.?.entities.items[
|
||||||
|
self.current_row
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
2
src/world/query_filters.zig
Normal file
2
src/world/query_filters.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
with: []const type = &.{},
|
||||||
|
without: []const type = &.{},
|
||||||
2
src/world/root.zig
Normal file
2
src/world/root.zig
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub const World = @import("world.zig");
|
||||||
|
pub const Archetype = @import("archetype.zig");
|
||||||
26
src/world/signature.zig
Normal file
26
src/world/signature.zig
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const root = @import("../app.zig");
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
ids: []u64,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn eql(a: Self, b: Self) bool {
|
||||||
|
return std.mem.eql(u64, a.ids, b.ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn hash(self: Self) u64 {
|
||||||
|
return std.hash.Wyhash.hash(
|
||||||
|
root.options.seed,
|
||||||
|
std.mem.sliceAsBytes(self.ids),
|
||||||
|
);
|
||||||
|
}
|
||||||
217
src/world/world.zig
Normal file
217
src/world/world.zig
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
// --- LIBS ---
|
||||||
|
const std = @import("std");
|
||||||
|
const Archetype = @import("archetype.zig");
|
||||||
|
const Signature = @import("signature.zig");
|
||||||
|
const EntityRecord = @import("entity_record.zig");
|
||||||
|
const QueryFilters = @import("query_filters.zig");
|
||||||
|
const Query = @import("query.zig").Query;
|
||||||
|
const TypeID = @import("../template/type_id.zig").of;
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
//
|
||||||
|
// FIELDS
|
||||||
|
//
|
||||||
|
|
||||||
|
archetypes: std.ArrayList(Archetype),
|
||||||
|
entity_map: std.AutoHashMap(u64, EntityRecord),
|
||||||
|
signature_map: std.AutoHashMap(u64, usize),
|
||||||
|
next_entity: u64,
|
||||||
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn init(alloc: std.mem.Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.archetypes = .empty,
|
||||||
|
.entity_map = .init(alloc),
|
||||||
|
.signature_map = .init(alloc),
|
||||||
|
.next_entity = 1,
|
||||||
|
.alloc = alloc,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn deinit(self: *Self) void {
|
||||||
|
// --- ARCHETYPES ---
|
||||||
|
for (self.archetypes.items) |*arch| {
|
||||||
|
arch.deinit();
|
||||||
|
}
|
||||||
|
self.entity_map.deinit();
|
||||||
|
self.archetypes.deinit(self.alloc);
|
||||||
|
self.signature_map.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn spawn(
|
||||||
|
self: *Self,
|
||||||
|
components: anytype,
|
||||||
|
) !void {
|
||||||
|
_ = try self.spawnEntity(components);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn despawn(
|
||||||
|
self: *Self,
|
||||||
|
entity: u64,
|
||||||
|
) void {
|
||||||
|
const record = self.entity_map.get(entity) orelse return;
|
||||||
|
if (self.archetypes.items[record.archetype_idx].remove(record.row)) |moved| {
|
||||||
|
self.entity_map.getPtr(moved).?.row = record.row;
|
||||||
|
}
|
||||||
|
_ = self.entity_map.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn spawnEntity(
|
||||||
|
self: *Self,
|
||||||
|
components: anytype,
|
||||||
|
) !u64 {
|
||||||
|
const Components = @TypeOf(components);
|
||||||
|
|
||||||
|
const fields = std.meta.fields(Components);
|
||||||
|
|
||||||
|
// --- SIGNATURE ---
|
||||||
|
var ids: [fields.len]u64 = undefined;
|
||||||
|
inline for (fields, 0..) |field, i| {
|
||||||
|
const component = @field(
|
||||||
|
components,
|
||||||
|
field.name,
|
||||||
|
);
|
||||||
|
ids[i] = TypeID(@TypeOf(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- SORT TYPE ID'S ---
|
||||||
|
std.mem.sort(
|
||||||
|
u64,
|
||||||
|
&ids,
|
||||||
|
{},
|
||||||
|
std.sort.asc(u64),
|
||||||
|
);
|
||||||
|
|
||||||
|
const signature_hash = std.hash.Wyhash.hash(0, std.mem.sliceAsBytes(&ids));
|
||||||
|
|
||||||
|
// --- FIND OR CREATE ARCHETYPE ---
|
||||||
|
const archetype_idx =
|
||||||
|
self.signature_map.get(signature_hash) orelse blk: {
|
||||||
|
const sig_ids = try self.alloc.dupe(u64, &ids);
|
||||||
|
|
||||||
|
var arch: Archetype = .init(.{ .ids = sig_ids }, self.alloc);
|
||||||
|
|
||||||
|
// --- NEW COLUMNS ---
|
||||||
|
inline for (fields) |field| {
|
||||||
|
const component = @field(components, field.name);
|
||||||
|
try arch.addColumn(@TypeOf(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ADD ARCHETYPE ---
|
||||||
|
try self.archetypes.append(
|
||||||
|
self.alloc,
|
||||||
|
arch,
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- ADD SIGNATURE ---
|
||||||
|
const idx = self.archetypes.items.len - 1;
|
||||||
|
try self.signature_map.put(
|
||||||
|
signature_hash,
|
||||||
|
idx,
|
||||||
|
);
|
||||||
|
|
||||||
|
break :blk idx;
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- NEW ENTITY ---
|
||||||
|
const entity = self.next_entity;
|
||||||
|
self.next_entity += 1;
|
||||||
|
|
||||||
|
// --- ADD ROW ---
|
||||||
|
var arch = &self.archetypes.items[archetype_idx];
|
||||||
|
|
||||||
|
const row = arch.entities.items.len;
|
||||||
|
|
||||||
|
try arch.add(
|
||||||
|
entity,
|
||||||
|
components,
|
||||||
|
);
|
||||||
|
|
||||||
|
// --- STORE ENTITY LOCATION ---
|
||||||
|
try self.entity_map.put(entity, .{
|
||||||
|
.archetype_idx = archetype_idx,
|
||||||
|
.row = row,
|
||||||
|
});
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn createEntity(self: *Self) u64 {
|
||||||
|
const entity = self.next_entity;
|
||||||
|
self.next_entity += 1;
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
fn createArchetype(
|
||||||
|
self: *Self,
|
||||||
|
comptime Components: type,
|
||||||
|
) !usize {
|
||||||
|
const fields =
|
||||||
|
std.meta.fields(Components);
|
||||||
|
|
||||||
|
var ids = try self.alloc.alloc(
|
||||||
|
u64,
|
||||||
|
fields.len,
|
||||||
|
);
|
||||||
|
|
||||||
|
inline for (fields, 0..) |field, i| {
|
||||||
|
ids[i] = TypeID(field.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const sig: Signature = .{
|
||||||
|
.ids = ids,
|
||||||
|
};
|
||||||
|
|
||||||
|
var arch: Archetype = .init(
|
||||||
|
sig,
|
||||||
|
self.alloc,
|
||||||
|
);
|
||||||
|
|
||||||
|
inline for (fields) |field| {
|
||||||
|
try arch.addColumn(
|
||||||
|
field.type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try self.archetypes.append(
|
||||||
|
self.alloc,
|
||||||
|
arch,
|
||||||
|
);
|
||||||
|
|
||||||
|
return self.archetypes.items.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// var query = world.query(struct {
|
||||||
|
/// player: ?*Player,
|
||||||
|
/// pos: *Position,
|
||||||
|
/// vel: *Velocity,
|
||||||
|
/// }, .{});
|
||||||
|
/// while (query.next()) |entity| {
|
||||||
|
/// if (entity.player) |player| {}
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// ----------------------------------------------------
|
||||||
|
pub fn query(
|
||||||
|
self: *Self,
|
||||||
|
comptime Result: type,
|
||||||
|
comptime Filters: QueryFilters,
|
||||||
|
) Query(Result, Filters) {
|
||||||
|
return .init(self);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue