413 lines
13 KiB
Zig
413 lines
13 KiB
Zig
const std = @import("std");
|
|
const logging = @import("logging.zig");
|
|
const p2 = @import("p2");
|
|
const engine_logs = logging.engine_logs;
|
|
|
|
const ObjectHandle = p2.ObjectHandle;
|
|
const Name = p2.Name;
|
|
const MakeName = p2.MakeName;
|
|
|
|
const ArrayList = std.ArrayList;
|
|
const AutoHashMap = std.AutoHashMap;
|
|
|
|
const ArrayListUnmanaged = std.ArrayListUnmanaged;
|
|
const AutoHashMapUnmanaged = std.AutoHashMapUnmanaged;
|
|
|
|
pub fn InterfaceRef(comptime Vtable: type) type {
|
|
return struct {
|
|
ptr: *anyopaque,
|
|
vtable: *const Vtable,
|
|
};
|
|
}
|
|
|
|
pub fn InterfaceRef2(comptime Vtable: type) type {
|
|
return struct {
|
|
ptr: *anyopaque,
|
|
vtable: *Vtable,
|
|
};
|
|
}
|
|
|
|
pub fn MakeTypeName(comptime TargetType: type) Name {
|
|
const hashedName = comptime std.fmt.comptimePrint("{s}_{d}", .{ @typeName(TargetType), @sizeOf(TargetType) });
|
|
|
|
return MakeName(hashedName);
|
|
}
|
|
|
|
// todo.. engineObject is not the right word for this
|
|
|
|
pub const EngineDataEventError = error{
|
|
UnknownStatePanic,
|
|
BadInit,
|
|
JobsAbortShutdown,
|
|
UnknownError,
|
|
OutOfMemory,
|
|
};
|
|
|
|
const FuncInfo = struct {
|
|
name: []const u8,
|
|
ptr: *const anyopaque,
|
|
};
|
|
|
|
pub const FieldInfo = struct {
|
|
name: []const u8,
|
|
size: u32,
|
|
offset: u32,
|
|
alignment: u32,
|
|
};
|
|
|
|
const core = @import("core.zig");
|
|
|
|
pub fn updateEngineVTable(comptime T: type) void {
|
|
const ref = core.getEngineObjectRef(T).?;
|
|
const vtable: *EngineObjectVTable = @constCast(ref.vtable);
|
|
const version = vtable.version;
|
|
|
|
if (ref.vtable.fieldList) |fieldList| {
|
|
core.PatchStruct(T, @ptrCast(@alignCast(ref.ptr)), fieldList);
|
|
}
|
|
|
|
vtable.* = T.NeonObjectTable;
|
|
vtable.version = version + 1;
|
|
}
|
|
|
|
pub fn PatchStruct(comptime T: type, p: *T, old: []const FieldInfo) void {
|
|
const t = @typeInfo(T).@"struct";
|
|
|
|
var new: T = .{};
|
|
|
|
inline for (t.fields) |field| {
|
|
var oldField: ?FieldInfo = null;
|
|
|
|
for (old) |oldFieldSearch| {
|
|
if (std.mem.eql(u8, field.name, oldFieldSearch.name)) {
|
|
oldField = oldFieldSearch;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (oldField) |of| {
|
|
var src: []const u8 = undefined;
|
|
src.ptr = @ptrCast(p);
|
|
src.ptr += of.offset;
|
|
src.len = @sizeOf(@TypeOf(@field(p, field.name)));
|
|
|
|
var dest: []u8 = undefined;
|
|
dest.ptr = @ptrCast(&@field(new, field.name));
|
|
dest.len = @sizeOf(@TypeOf(@field(new, field.name)));
|
|
|
|
// std.debug.print(
|
|
// "patching field {s} {d} bytes {d} -> {d} {s}\n",
|
|
// .{
|
|
// field.name,
|
|
// src.len,
|
|
// of.offset,
|
|
// @offsetOf(T, field.name),
|
|
// if (of.offset == @offsetOf(T, field.name)) "" else "change!",
|
|
// },
|
|
// );
|
|
|
|
std.mem.copyForwards(u8, dest, src);
|
|
} else {
|
|
std.debug.print("new field added {s}", .{field.name});
|
|
}
|
|
}
|
|
|
|
p.* = new;
|
|
}
|
|
|
|
pub fn EngineObjectDelegate(comptime T: type) type {
|
|
return struct {
|
|
func: ?T = null,
|
|
target: *anyopaque,
|
|
|
|
functionName: []const u8,
|
|
version: i32 = -1,
|
|
vtable: *EngineObjectVTable,
|
|
|
|
pub fn make(ptr: anytype, comptime funcName: []const u8) @This() {
|
|
const TargetType = @typeInfo(@TypeOf(ptr)).pointer.child;
|
|
const func = @field(TargetType, funcName);
|
|
|
|
return .{
|
|
.func = func,
|
|
.functionName = funcName,
|
|
.target = ptr,
|
|
.vtable = &TargetType.NeonObjectTable,
|
|
};
|
|
}
|
|
|
|
pub fn call(self: *@This(), args: anytype) void {
|
|
//if (!core.BuildOption("static_build")) { todo; disable this vtable check in non-static builds
|
|
if (self.vtable.version != self.version) {
|
|
self.version = self.vtable.version;
|
|
if (self.vtable.findFunc(self.functionName)) |p| {
|
|
self.func = @ptrCast(@alignCast(p));
|
|
} else {
|
|
core.engine_log("unable to find function {s} in {s}", .{ self.functionName, self.vtable.typeName });
|
|
self.func = null;
|
|
}
|
|
}
|
|
//}
|
|
|
|
if (self.func) |func| {
|
|
func(self.target, args);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub const EngineObjectVTable = struct {
|
|
typeName: []const u8,
|
|
typeSize: usize,
|
|
typeAlign: usize,
|
|
|
|
singletonName: ?[]const u8 = null,
|
|
|
|
// new init_function passes in an already created object
|
|
init_func: *const fn (*anyopaque, std.mem.Allocator, bool) EngineDataEventError!void,
|
|
tick_func: ?*const fn (*anyopaque, f64) void = null,
|
|
engineDraw_func: ?*const fn (*anyopaque, f64) void = null,
|
|
preTick_func: ?*const fn (*anyopaque, f64) EngineDataEventError!void = null,
|
|
deinit_func: ?*const fn (*anyopaque) void = null,
|
|
postInit_func: ?*const fn (*anyopaque) EngineDataEventError!void = null,
|
|
processEvents: ?*const fn (*anyopaque, u64) EngineDataEventError!void = null,
|
|
exitSignal_func: ?*const fn (*anyopaque) EngineDataEventError!void = null,
|
|
readyToExit_func: ?*const fn (*anyopaque) bool = null,
|
|
|
|
prepare_func: ?*const fn (*anyopaque) EngineDataEventError!void = null,
|
|
fieldListHash: ?usize = null,
|
|
fieldList: ?[]const FieldInfo = null,
|
|
slackSize: ?usize = null,
|
|
|
|
funcList: ?[]const FuncInfo = null,
|
|
version: i32 = 0,
|
|
|
|
pub fn findFunc(self: *const @This(), name: []const u8) ?*const anyopaque {
|
|
if (self.funcList) |flist| {
|
|
for (flist) |fi| {
|
|
// core.engine_log("checking func {s} vs {s}.", .{ fi.name, name });
|
|
if (std.mem.eql(u8, fi.name, name)) {
|
|
return fi.ptr;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
pub fn addFunctionsList(self: *@This(), comptime TargetType: type) void {
|
|
const ti = @typeInfo(TargetType);
|
|
|
|
const funcList = blk: {
|
|
comptime var f: []const FuncInfo = &.{};
|
|
inline for (ti.@"struct".decls) |decl| {
|
|
if (std.mem.eql(u8, decl.name, "NeonObjectTable")) {
|
|
continue;
|
|
}
|
|
|
|
const t = @typeInfo(@TypeOf(@field(TargetType, decl.name)));
|
|
|
|
switch (t) {
|
|
.@"fn" => {
|
|
// @compileLog(decl.name);
|
|
f = f ++ .{FuncInfo{
|
|
.name = decl.name,
|
|
.ptr = @ptrCast(@alignCast(&@field(TargetType, decl.name))),
|
|
}};
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
break :blk f;
|
|
};
|
|
|
|
self.funcList = funcList;
|
|
}
|
|
|
|
fn fieldInfoCompare(_: void, a: FieldInfo, b: FieldInfo) bool {
|
|
return a.offset < b.offset;
|
|
}
|
|
|
|
pub fn addFieldList(self: *@This(), comptime TargetType: type) void {
|
|
if (!@hasDecl(TargetType, "Slack")) {
|
|
return;
|
|
}
|
|
|
|
self.slackSize = @sizeOf(TargetType.Slack);
|
|
const typeinfo = @typeInfo(TargetType).@"struct";
|
|
const fieldList = blk: {
|
|
comptime var f: []const FieldInfo = &.{};
|
|
inline for (typeinfo.fields) |field| {
|
|
f = f ++ .{FieldInfo{
|
|
.name = field.name,
|
|
.offset = @intCast(@offsetOf(TargetType, field.name)),
|
|
.size = @intCast(@sizeOf(field.type)),
|
|
.alignment = @alignOf(field.type),
|
|
}};
|
|
}
|
|
|
|
break :blk f;
|
|
};
|
|
|
|
self.fieldList = fieldList;
|
|
}
|
|
|
|
pub fn from(comptime TargetType: type, comptime engineObjectName: ?[]const u8) EngineObjectVTable {
|
|
var self = EngineObjectVTable{
|
|
.typeName = @typeName(TargetType),
|
|
.typeSize = @sizeOf(TargetType),
|
|
.typeAlign = @alignOf(TargetType),
|
|
.init_func = undefined,
|
|
};
|
|
self.addFieldList(TargetType);
|
|
self.addFunctionsList(TargetType);
|
|
|
|
if (engineObjectName) |eon| {
|
|
self.singletonName = eon;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "init")) {
|
|
const wrappedInit = struct {
|
|
pub fn func(p: *anyopaque, allocator: std.mem.Allocator, first: bool) EngineDataEventError!void {
|
|
const newObject: *TargetType = @ptrCast(@alignCast(p)); // funcFind(allocator) catch return error.BadInit;
|
|
newObject.init(allocator, first) catch return error.BadInit;
|
|
// return @as(*anyopaque, @ptrCast(newObject));
|
|
}
|
|
};
|
|
|
|
self.init_func = wrappedInit.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "create")) {
|
|
const wrappedInit = struct {
|
|
pub fn func(p: *anyopaque, allocator: std.mem.Allocator, first: bool) EngineDataEventError!void {
|
|
const newObject: *TargetType = @ptrCast(@alignCast(p)); // funcFind(allocator) catch return error.BadInit;
|
|
newObject.create(allocator, first) catch return error.BadInit;
|
|
// return @as(*anyopaque, @ptrCast(newObject));
|
|
}
|
|
};
|
|
|
|
self.init_func = wrappedInit.func;
|
|
}
|
|
|
|
// function used for objects created before engine loop starts;
|
|
// this is a defferred startup
|
|
//
|
|
// will run normally during create if in systems thread
|
|
//
|
|
// or else will run
|
|
if (@hasDecl(TargetType, "prepare")) {
|
|
const wrap = struct {
|
|
pub fn func(pointer: *anyopaque) EngineDataEventError!void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
ptr.prepare() catch return error.BadInit;
|
|
}
|
|
};
|
|
|
|
self.prepare_func = wrap.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "postInit")) {
|
|
const wrappedPostInit = struct {
|
|
pub fn func(pointer: *anyopaque) EngineDataEventError!void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
try ptr.postInit();
|
|
}
|
|
};
|
|
|
|
self.postInit_func = wrappedPostInit.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "preTick")) {
|
|
const Wrapped = struct {
|
|
pub fn func(pointer: *anyopaque, deltaTime: f64) EngineDataEventError!void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
ptr.preTick(deltaTime) catch return error.UnknownStatePanic;
|
|
}
|
|
};
|
|
|
|
self.preTick_func = Wrapped.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "engineDraw")) {
|
|
const Wrapped = struct {
|
|
pub fn func(pointer: *anyopaque, deltaTime: f64) void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
ptr.engineDraw(deltaTime);
|
|
}
|
|
};
|
|
|
|
self.engineDraw_func = Wrapped.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "tick")) {
|
|
const Wrapped = struct {
|
|
pub fn func(pointer: *anyopaque, deltaTime: f64) void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
ptr.tick(deltaTime);
|
|
}
|
|
};
|
|
|
|
self.tick_func = Wrapped.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "processEvents")) {
|
|
const wrappedProcessEvents = struct {
|
|
pub fn func(pointer: *anyopaque, frameNumber: u64) EngineDataEventError!void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
try ptr.processEvents(frameNumber);
|
|
}
|
|
};
|
|
|
|
self.processEvents = wrappedProcessEvents.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "onExitSignal")) {
|
|
if (!@hasDecl(TargetType, "readyToExit")) {
|
|
@compileError("engine object implemented onExitSignal but did not implement fn readyToExit() bool");
|
|
}
|
|
|
|
const wrappedProcessEvents = struct {
|
|
pub fn onExitSignal(pointer: *anyopaque) EngineDataEventError!void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
try ptr.onExitSignal();
|
|
}
|
|
|
|
pub fn readyToExit(pointer: *anyopaque) bool {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
return ptr.readyToExit();
|
|
}
|
|
};
|
|
|
|
self.exitSignal_func = wrappedProcessEvents.onExitSignal;
|
|
self.readyToExit_func = wrappedProcessEvents.readyToExit;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "deinit")) {
|
|
const wrappedDeinit = struct {
|
|
pub fn func(pointer: *anyopaque) void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
ptr.deinit();
|
|
}
|
|
};
|
|
|
|
self.deinit_func = wrappedDeinit.func;
|
|
}
|
|
|
|
if (@hasDecl(TargetType, "destroy")) {
|
|
const wrappedDeinit = struct {
|
|
pub fn func(pointer: *anyopaque) void {
|
|
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
|
|
ptr.destroy();
|
|
}
|
|
};
|
|
|
|
self.deinit_func = wrappedDeinit.func;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
};
|
|
|
|
pub const EngineObjectRef = InterfaceRef2(EngineObjectVTable);
|