259 lines
10 KiB
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");
|