Backlog/engine/core/src/script/ComponentRef.zig

259 lines
10 KiB
Zig

// lwdt can't have meta tables so I'm going to create component references
//
// the way this works is
// - entities are created as POD types
// - entities can get components added to them via entity:addComponent
// - this returns a ComponentReference
// - you can also get components from an entity via enity:get()
// - this also returns a ComponentReference
//
// - ComponentReferences allow you to modify data on a component or call functions on them
//
//
// How the registration works
//
// - define component
// - ecs.zig defineComponent
// - ComponentRef.zig - ReferenceType
// - addComponentRegistration - script.zig
// - ComponentRef - ReferenceType.registerType
// this represents the lua side of the object
pub fn ComponentReferenceType(comptime T: type) type {
return struct {
ptr: *T = undefined,
// used for resolving deltas.
handle: core.ObjectHandle = undefined,
stateCount: u32 = 0,
containerRef: ecs.EcsContainerRef = undefined,
pub const MetatableName = T.ComponentName;
// argc = 1,
// 1. a componentRegistration userdata
// can only be called from entity.luaAddComponent
pub fn luaNew(state: lua.LuaState, handle: core.ObjectHandle, ptr: ?*anyopaque) void {
const ud = state.newZigUserdata(@This()) catch return;
const containerRef = ecs.getTypeContainer(T);
ud.* = .{
.handle = handle,
.containerRef = containerRef,
.stateCount = 0,
};
// std.debug.print("luaNew ComponentReferenceType: {x}\n", .{@intFromPtr(ud)});
// std.debug.print("luaNew ContainerRef: {x}\n", .{@intFromPtr(containerRef.ptr)});
// std.debug.print("luaNew " ++ @typeName(T) ++ " ud.ptr = {x}\n", .{@intFromPtr(ptr)});
if (ptr) |p| {
ud.ptr = @ptrCast(@alignCast(p));
ud.stateCount = containerRef.vtable.getStateCount(containerRef.ptr);
}
if (@hasDecl(@TypeOf(T.BaseContainer.*), "IsMultiset")) {
// ... there are several things that need to be reworked here...
ud.ptr = @ptrCast(@alignCast(@as(*anyopaque, @ptrCast(&ud.handle))));
// std.debug.print("luaNew " ++ @typeName(T) ++ " v = {any}\n", .{ud.ptr.getPosition()});
}
}
pub fn resolve(self: *@This()) void {
const ref = self.containerRef;
// std.debug.print("self: {x}\n", .{@intFromPtr(self)});
// std.debug.print("ptr: {x}\n", .{@intFromPtr(ref.ptr)});
if (@hasDecl(@TypeOf(T.BaseContainer.*), "IsMultiset")) {
//
}
if (ref.vtable.getStateCount(ref.ptr) != self.stateCount) {
self.ptr = @ptrCast(@alignCast(ref.vtable.get(ref.ptr, self.handle)));
}
}
pub fn get(self: *@This()) *T {
self.resolve();
return self.ptr;
}
pub fn luaToString(state: lua.LuaState) i32 {
// oh god... this isn't good
// I think i've been treating this component ref as the user type
// huge failure of type resolution
if (state.toUserdata(@This(), 1)) |self| {
self.resolve();
Buffer.clearRetainingCapacity();
var writer = Buffer.writer();
writer.print("{s}{{", .{MetatableName}) catch return 0;
inline for (std.meta.fields(T), 0..) |field, i| {
if (i == 0) {
writer.print(" {s} = ", .{field.name}) catch return 0;
} else {
writer.print(", {s} = ", .{field.name}) catch return 0;
}
switch (field.type) {
f32, i32, u32, f64, i64, u64 => {
writer.print("{d}", .{@field(self.ptr, field.name)}) catch return 0;
},
[]const u8 => {
writer.print("\"{s}\"", .{@field(self.ptr, field.name)}) catch return 0;
},
else => {
writer.print("<unknown type {s}>", .{@typeName(field.type)}) catch return 0;
},
}
}
writer.print(" }}" ++ "\x00", .{}) catch return 0;
state.pop(1);
state.pushString(Buffer.items) catch return 0;
return 1;
}
return 0;
}
pub fn luaIndex(state: lua.LuaState) i32 {
if (state.toUserdata(@This(), 1)) |self| {
if (state.isString(2)) {
_ = self;
const argument = state.toString(2);
// core.engine_log(@typeName(@This()) ++ " got indexed. 0x{x}", .{self.handle.index});
// check metatable
if (state.getMetafield(1, @ptrCast(argument))) {
return 1;
}
}
}
return 0;
}
fn makeTypeTable() lua.LibSpec {
const methods = blk: {
comptime var m: lua.LibSpec = &.{};
m = m ++ .{lua.luaL_Reg{ .name = "__index", .func = lua.CWrap(luaIndex) }};
m = m ++ .{lua.luaL_Reg{ .name = "__tostring", .func = lua.CWrap(luaToString) }};
inline for (T.ScriptExports) |name| {
m = m ++ .{lua.luaL_Reg{ .name = @as([*c]const u8, @ptrCast(name)), .func = ComponentFuncWrapper(@field(T, name), T) }};
}
break :blk m ++ .{lua.luaL_Reg{ .name = null, .func = null }};
};
return methods;
}
pub fn registerType(state: *lua.LuaState) !void {
core.engine_log("creating lua metatable {s}", .{MetatableName});
const methods = comptime makeTypeTable();
try state.newMetatable(@ptrCast(MetatableName));
try state.setFuncs(methods, 0);
}
};
}
pub fn ComponentFuncWrapper(comptime baseFunc: anytype, comptime baseType: type) lua.LuaCFunc {
return lua.CWrap(FuncWrapper(baseFunc, baseType).wrapper);
}
pub fn FuncWrapper(comptime baseFunc: anytype, comptime baseType: type) type {
return struct {
pub fn wrapper(state: lua.LuaState) i32 {
const Args = std.meta.ArgsTuple(@TypeOf(baseFunc));
var args: Args = undefined;
inline for (std.meta.fields(Args), 0..) |field, index| {
// std.debug.print("typename = {s}\n", .{@typeName(field.type)});
switch (field.type) {
f32 => {
args[index] = @as(f32, @floatCast(state.toNumber(index + 1)));
},
f64 => {
args[index] = state.toNumber(index + 1);
},
i32 => {
args[index] = @intFromFloat(state.toNumber(index + 1));
},
[]const u8 => {
args[index] = state.toString(index + 1);
},
// I'll be honest how the hell does this work?
//
// the pointer being passed in here isn't the actual resulting type...
// it's the reference type
*baseType => {
const ref = state.toUserdata(ComponentReferenceType(baseType), index + 1).?;
ref.resolve();
args[index] = ref.ptr;
},
baseType => {
const ref = state.toUserdata(ComponentReferenceType(baseType), index + 1).?;
ref.resolve();
args[index] = ref.ptr.*;
},
else => {
// if(isComponentType(field.type)) {
// const ref = state.toUserdata(field.type, index + 1).?;
// ref.resolve();
// args[index] = ref.ptr.*;
// continue;
// }
//
lua.debugPrints(true);
args[index] = (state.toUserdata(field.type, index + 1) orelse {
std.debug.print("argument error in index: {d}\n", .{index});
state.emitError("something's weird with this argument\n");
@panic("lmao");
}).*;
lua.debugPrints(false);
},
}
}
state.pop(@intCast(args.len));
//const rv = @call(.always_inline, baseFunc, args);
const rv = @call(.auto, baseFunc, args);
switch (@TypeOf(rv)) {
i32, u32, i64, u64 => {
state.pushNumber(@floatFromInt(rv));
},
f32, f64 => {
state.pushNumber(@floatCast(rv));
},
void => {
return 0;
},
else => {
const ud = state.newZigUserdata(@TypeOf(rv)) catch @panic("not implemented");
ud.* = rv;
},
}
return 1;
}
};
}
var Buffer: std.ArrayList(u8) = undefined;
pub fn setupFormatBuffer(allocator: std.mem.Allocator) !void {
Buffer = std.ArrayList(u8).init(allocator);
}
pub fn shutdownFormatBuffer() void {
Buffer.deinit();
}
const std = @import("std");
const core = @import("../core.zig");
const ecs = @import("../ecs.zig");
const lua = @import("lua");
const pod = lua.pod;
const scene = @import("../scene.zig");