const std = @import("std"); const logging = @import("logging.zig"); const input = @import("input.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 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, UnknownError, OutOfMemory, }; pub const EngineObjectVTable = struct { typeName: []const u8, typeSize: usize, typeAlign: usize, init_func: *const fn (std.mem.Allocator) EngineDataEventError!*anyopaque, 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, pub fn from(comptime TargetType: type) EngineObjectVTable { const wrappedInit = struct { const funcFind: @TypeOf(@field(TargetType, "init")) = @field(TargetType, "init"); pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque { const newObject = funcFind(allocator) catch return error.BadInit; return @as(*anyopaque, @ptrCast(newObject)); } }; var self = EngineObjectVTable{ .typeName = @typeName(TargetType), .typeSize = @sizeOf(TargetType), .typeAlign = @alignOf(TargetType), .init_func = wrappedInit.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; } return self; } }; pub const EngineObjectRef = InterfaceRef(EngineObjectVTable);