You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
13 KiB
437 lines
13 KiB
const std = @import("std");
|
|
const root = @import("root");
|
|
|
|
pub const c = @import("luajit").c;
|
|
|
|
const util = root.util;
|
|
|
|
pub const Func = fn (lua: ?*c.lua_State) callconv(.C) c_int;
|
|
|
|
const slice_types = .{
|
|
f32,
|
|
usize,
|
|
root.model.Product,
|
|
root.model.Product.Quantity,
|
|
};
|
|
|
|
const map_types = .{
|
|
root.model.Task.ProductMap,
|
|
};
|
|
|
|
var load_alloc: ?std.mem.Allocator = null;
|
|
|
|
pub fn init(
|
|
alloc: std.mem.Allocator,
|
|
config: root.Config.Start,
|
|
simulation: *root.Simulation,
|
|
) !*c.lua_State {
|
|
load_alloc = alloc;
|
|
|
|
const lua = c.luaL_newstate().?;
|
|
_ = c.luaopen_base(lua);
|
|
_ = c.luaopen_table(lua);
|
|
_ = c.luaopen_io(lua);
|
|
_ = c.luaopen_string(lua);
|
|
_ = c.luaopen_math(lua);
|
|
|
|
const cstr_filename = try alloc.dupeZ(u8, config.script);
|
|
defer alloc.free(cstr_filename);
|
|
|
|
if (c.luaL_loadfile(lua, cstr_filename.ptr) != 0) {
|
|
const error_str = load([]const u8, lua);
|
|
defer load_alloc.?.free(error_str);
|
|
|
|
std.log.err("lua error: {s}", .{error_str});
|
|
return error.LuaError;
|
|
}
|
|
|
|
initTables(simulation, lua);
|
|
initAPI(simulation, lua);
|
|
|
|
if (c.lua_pcall(lua, 0, c.LUA_MULTRET, 0) != 0) {
|
|
const error_str = load([]const u8, lua);
|
|
defer load_alloc.?.free(error_str);
|
|
|
|
std.log.err("lua error: {s}", .{error_str});
|
|
return error.LuaError;
|
|
}
|
|
|
|
return lua;
|
|
}
|
|
|
|
pub fn deinit(_: std.mem.Allocator, lua: *c.lua_State) !void {
|
|
//try call(void, alloc, lua, "printResults", .{});
|
|
load_alloc = null;
|
|
c.lua_close(lua);
|
|
}
|
|
|
|
pub fn update(
|
|
alloc: std.mem.Allocator,
|
|
_: root.Config.Run,
|
|
lua: *c.lua_State,
|
|
) !void {
|
|
const api = root.Simulation.interface;
|
|
const tuples = .{
|
|
.{ api.ResourcesOrder, "assignResources", api.addResources },
|
|
.{ api.WageOrder, "assignWages", api.setWages },
|
|
.{ api.PriceOrder, "assignPrices", api.setPrices },
|
|
};
|
|
inline for (tuples) |tuple| {
|
|
const Type = tuple.@"0";
|
|
const funcName = tuple.@"1";
|
|
const apiFunc = tuple.@"2";
|
|
const data = try call([]Type, alloc, lua, funcName, .{});
|
|
defer util.free(alloc, data);
|
|
|
|
apiFunc(data);
|
|
}
|
|
}
|
|
|
|
pub fn call(
|
|
comptime Type: type,
|
|
alloc: std.mem.Allocator,
|
|
lua: *c.lua_State,
|
|
name: []const u8,
|
|
args: anytype,
|
|
) !Type {
|
|
const cstr = try alloc.dupeZ(u8, name);
|
|
defer alloc.free(cstr);
|
|
|
|
c.lua_getglobal(lua, cstr.ptr);
|
|
|
|
for (args) |arg| {
|
|
push(lua, arg);
|
|
}
|
|
|
|
const err = c.lua_pcall(lua, args.len, 1, 0);
|
|
if (err != 0) {
|
|
const msg = load([]const u8, lua);
|
|
defer load_alloc.?.free(msg);
|
|
|
|
std.log.info("error running {s}: {s}", .{ name, msg });
|
|
}
|
|
|
|
if (Type != void) {
|
|
return load(Type, lua);
|
|
}
|
|
}
|
|
|
|
fn initTables(_: *root.Simulation, lua: *c.lua_State) void {
|
|
inline for (slice_types) |Type| {
|
|
addSliceMetatable(Type, lua);
|
|
}
|
|
inline for (map_types) |Type| {
|
|
addMapMetatable(Type, lua);
|
|
}
|
|
}
|
|
|
|
fn initAPI(simulation: *root.Simulation, lua: *c.lua_State) void {
|
|
const api = root.Simulation.interface;
|
|
api.simulation = simulation;
|
|
|
|
c.lua_newtable(lua);
|
|
|
|
setField(lua, "products", simulation.products);
|
|
//setField(lua, "agents", simulation.agents.keys());
|
|
//setField(lua, "tasks", simulation.tasks.keys());
|
|
|
|
registerFunc(lua, "getDay", api.getDay);
|
|
registerFunc(lua, "getAvgLabor", api.getAvgLabor);
|
|
registerFunc(lua, "getTasks", api.getTasks);
|
|
registerFunc(lua, "getAgents", api.getAgents);
|
|
registerFunc(lua, "getTaskProduct", api.getTaskProduct);
|
|
registerFunc(lua, "getInventory", api.getInventory);
|
|
registerFunc(lua, "getProduced", api.getProduced);
|
|
registerFunc(lua, "getConsumed", api.getConsumed);
|
|
registerFunc(lua, "getConsumedByAgents", api.getConsumedByAgents);
|
|
registerFunc(lua, "getConsumedByTasks", api.getConsumedByTasks);
|
|
registerFunc(lua, "getAgentTask", api.getAgentTask);
|
|
registerFunc(lua, "getTaskAgents", api.getTaskAgents);
|
|
registerFunc(lua, "getAgentConsumption", api.getAgentConsumption);
|
|
registerFunc(lua, "getAgentProduction", api.getAgentProduction);
|
|
registerFunc(lua, "getAgentSavings", api.getAgentSavings);
|
|
registerFunc(lua, "getTaskInventory", api.getTaskInventory);
|
|
registerFunc(lua, "getTaskUnspent", api.getTaskUnspent);
|
|
registerFunc(lua, "getAverageHealth", api.getAverageHealth);
|
|
registerFunc(lua, "getAverageHappiness", api.getAverageHappiness);
|
|
|
|
c.lua_setglobal(lua, "econ");
|
|
}
|
|
|
|
fn registerFunc(
|
|
lua: *c.lua_State,
|
|
comptime name: []const u8,
|
|
comptime func: anytype,
|
|
) void {
|
|
const wfunc = wrap(func);
|
|
setFunctionField(lua, name, wfunc);
|
|
}
|
|
|
|
pub fn wrap(comptime func: anytype) Func {
|
|
return struct {
|
|
const Type = @TypeOf(func);
|
|
const info = @typeInfo(Type).Fn;
|
|
const Tuple = std.meta.ArgsTuple(Type);
|
|
const fields = std.meta.fields(Tuple);
|
|
const len = fields.len;
|
|
const return_type = info.return_type;
|
|
pub fn f(opt: ?*c.lua_State) callconv(.C) c_int {
|
|
const lua = opt.?;
|
|
var args: Tuple = undefined;
|
|
inline for (fields, 0..) |field, i| {
|
|
c.lua_pushvalue(lua, -@as(c_int, @intCast(len - i)));
|
|
@field(args, field.name) = load(field.type, lua);
|
|
c.lua_pop(lua, 1);
|
|
}
|
|
if (return_type != null and return_type.? != void) {
|
|
const ret = @call(.auto, func, args);
|
|
push(lua, ret);
|
|
return 1;
|
|
} else {
|
|
@call(.auto, func, args);
|
|
return 0;
|
|
}
|
|
}
|
|
}.f;
|
|
}
|
|
|
|
pub fn load(comptime Type: type, lua: *c.lua_State) Type {
|
|
//@compileLog("load " ++ @typeName(Type));
|
|
//std.log.info("loading {s}", .{@typeName(Type)});
|
|
const alloc = load_alloc.?;
|
|
const value: Type = switch (@typeInfo(Type)) {
|
|
.Bool => c.lua_toboolean(lua, -1) != 0,
|
|
.Int => @as(Type, @intFromFloat(c.lua_tonumber(lua, -1))),
|
|
.Float => @as(Type, @floatCast(c.lua_tonumber(lua, -1))),
|
|
.Optional => |info| if (c.lua_isnil(lua, -1))
|
|
null
|
|
else
|
|
load(info.child, lua),
|
|
.Struct => loadStruct(Type, lua),
|
|
.Pointer => |info| switch (info.size) {
|
|
.Slice => if (info.is_const and info.child == u8) blk: {
|
|
var len: usize = 0;
|
|
const ptr = c.lua_tolstring(lua, -1, &len);
|
|
var slice: [:0]const u8 = undefined;
|
|
slice.ptr = ptr;
|
|
slice.len = len;
|
|
break :blk alloc.dupe(u8, slice) catch
|
|
unreachable;
|
|
} else loadSlice(info.child, alloc, lua) catch
|
|
unreachable,
|
|
else => @compileError("unsupported type"),
|
|
},
|
|
else => @compileError("unsupported type"),
|
|
};
|
|
if (comptime @typeInfo(Type) != .Optional) {
|
|
c.lua_pop(lua, 1);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
fn loadStruct(comptime Type: type, lua: *c.lua_State) Type {
|
|
var value: Type = undefined; //TODO: init defaults
|
|
inline for (std.meta.fields(Type)) |field| {
|
|
const name = field.name;
|
|
@field(value, name) = getField(field.type, lua, name);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
fn getField(comptime Type: type, lua: *c.lua_State, name: []const u8) Type {
|
|
push(lua, name);
|
|
c.lua_gettable(lua, -2);
|
|
return load(Type, lua);
|
|
}
|
|
|
|
fn loadSlice(
|
|
comptime Type: type,
|
|
alloc: std.mem.Allocator,
|
|
lua: *c.lua_State,
|
|
) ![]Type {
|
|
const len = c.lua_objlen(lua, -1);
|
|
const slice = try alloc.alloc(Type, len);
|
|
for (slice, 0..) |*item, i| {
|
|
item.* = getIndex(Type, lua, i + 1);
|
|
}
|
|
return slice;
|
|
}
|
|
|
|
fn getIndex(comptime Type: type, lua: *c.lua_State, index: usize) Type {
|
|
push(lua, index);
|
|
c.lua_gettable(lua, -2);
|
|
return load(Type, lua);
|
|
}
|
|
|
|
pub fn push(lua: *c.lua_State, value: anytype) void {
|
|
const Type = @TypeOf(value);
|
|
if (comptime in(Type, map_types)) {
|
|
pushMap(Type, lua, value);
|
|
return;
|
|
} else {
|
|
switch (@typeInfo(Type)) {
|
|
.Bool => c.lua_pushboolean(lua, @intFromBool(value)),
|
|
.Int => c.lua_pushnumber(lua, @as(f64, @floatFromInt(value))),
|
|
.Float => c.lua_pushnumber(lua, @as(f64, @floatCast(value))),
|
|
.Optional => if (value) |v| push(lua, v) else c.lua_pushnil(lua),
|
|
.Array => |info| pushSliceAsTable(info.child, lua, &value),
|
|
.Struct => pushStruct(Type, lua, value),
|
|
.Pointer => |info| switch (info.size) {
|
|
.Slice => if (info.is_const and info.child == u8) {
|
|
c.lua_pushlstring(lua, value.ptr, value.len);
|
|
} else {
|
|
if (in(info.child, slice_types)) {
|
|
pushSlice(info.child, lua, value);
|
|
} else {
|
|
pushSliceAsTable(info.child, lua, value);
|
|
}
|
|
},
|
|
.One => push(lua, value.*),
|
|
else => @compileError("unsupported type"),
|
|
},
|
|
else => @compileError("unsupported type"),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn pushStruct(comptime Type: type, lua: *c.lua_State, value: Type) void {
|
|
c.lua_newtable(lua);
|
|
inline for (std.meta.fields(Type)) |field| {
|
|
const name = field.name;
|
|
setField(lua, name, @field(value, name));
|
|
}
|
|
}
|
|
|
|
fn pushMap(comptime Type: type, lua: *c.lua_State, map: Type) void {
|
|
const Payload = Type;
|
|
const n = @sizeOf(Payload);
|
|
const ptr = cast(*Payload, c.lua_newuserdata(lua, n));
|
|
c.luaL_getmetatable(lua, mapMetatableName(Type));
|
|
_ = c.lua_setmetatable(lua, -2);
|
|
ptr.* = map;
|
|
}
|
|
|
|
fn pushSlice(comptime Type: type, lua: *c.lua_State, slice: []Type) void {
|
|
const Payload = []Type;
|
|
const n = @sizeOf(Payload);
|
|
const ptr = cast(*Payload, c.lua_newuserdata(lua, n));
|
|
c.luaL_getmetatable(lua, sliceMetatableName(Type));
|
|
_ = c.lua_setmetatable(lua, -2);
|
|
ptr.* = slice;
|
|
}
|
|
|
|
fn pushSliceAsTable(
|
|
comptime Type: type,
|
|
lua: *c.lua_State,
|
|
value: []Type,
|
|
) void {
|
|
c.lua_newtable(lua);
|
|
for (value, 0..) |item, i| {
|
|
push(lua, i);
|
|
push(lua, item);
|
|
c.lua_settable(lua, -3);
|
|
}
|
|
//setField(lua, "size", value.len);
|
|
}
|
|
|
|
fn setField(lua: *c.lua_State, index: []const u8, value: anytype) void {
|
|
push(lua, index);
|
|
push(lua, value);
|
|
c.lua_settable(lua, -3);
|
|
}
|
|
|
|
fn setFunctionField(
|
|
lua: *c.lua_State,
|
|
index: []const u8,
|
|
value: *const Func,
|
|
) void {
|
|
push(lua, index);
|
|
c.lua_pushcfunction(lua, value);
|
|
c.lua_settable(lua, -3);
|
|
}
|
|
|
|
fn addMapMetatable(comptime Type: type, lua: *c.lua_State) void {
|
|
const Payload = Type;
|
|
const getItem = struct {
|
|
pub fn f(opt_l: ?*c.lua_State) callconv(.C) c_int {
|
|
const l = opt_l.?;
|
|
const opt = cast(?*Payload, c.lua_touserdata(l, 1));
|
|
const index = c.luaL_checkint(l, 2);
|
|
const ptr = opt.?;
|
|
push(l, ptr.get(@as(usize, @intCast(index))));
|
|
return 1;
|
|
}
|
|
}.f;
|
|
const getSize = struct {
|
|
pub fn f(opt_l: ?*c.lua_State) callconv(.C) c_int {
|
|
const l = opt_l.?;
|
|
const opt = cast(?*Payload, c.lua_touserdata(l, 1));
|
|
const ptr = opt.?;
|
|
push(l, ptr.count());
|
|
return 1;
|
|
}
|
|
}.f;
|
|
const index_str: []const u8 = "__index";
|
|
|
|
_ = c.luaL_newmetatable(lua, mapMetatableName(Type));
|
|
setFunctionField(lua, "get", getItem);
|
|
setFunctionField(lua, "size", getSize);
|
|
push(lua, index_str);
|
|
c.lua_pushvalue(lua, -2);
|
|
c.lua_settable(lua, -3);
|
|
c.lua_pop(lua, 1);
|
|
}
|
|
|
|
fn mapMetatableName(comptime Type: type) [*c]const u8 {
|
|
return "econ_map" ++ @typeName(Type);
|
|
}
|
|
|
|
fn addSliceMetatable(comptime Type: type, lua: *c.lua_State) void {
|
|
const Payload = []Type;
|
|
const getItem = struct {
|
|
pub fn f(opt_l: ?*c.lua_State) callconv(.C) c_int {
|
|
const l = opt_l.?;
|
|
const opt = cast(?*Payload, c.lua_touserdata(l, 1));
|
|
const index = c.luaL_checkint(l, 2);
|
|
const ptr = opt.?;
|
|
push(l, ptr.*[@as(usize, @intCast(index))]);
|
|
return 1;
|
|
}
|
|
}.f;
|
|
const getSize = struct {
|
|
pub fn f(opt_l: ?*c.lua_State) callconv(.C) c_int {
|
|
const l = opt_l.?;
|
|
const opt = cast(?*Payload, c.lua_touserdata(l, 1));
|
|
const ptr = opt.?;
|
|
push(l, ptr.*.len);
|
|
return 1;
|
|
}
|
|
}.f;
|
|
const index_str: []const u8 = "__index";
|
|
|
|
_ = c.luaL_newmetatable(lua, sliceMetatableName(Type));
|
|
setFunctionField(lua, "get", getItem);
|
|
setFunctionField(lua, "size", getSize);
|
|
push(lua, index_str);
|
|
c.lua_pushvalue(lua, -2);
|
|
c.lua_settable(lua, -3);
|
|
c.lua_pop(lua, 1);
|
|
}
|
|
|
|
fn sliceMetatableName(comptime Type: type) [*c]const u8 {
|
|
return "econ_slice" ++ @typeName(Type);
|
|
}
|
|
|
|
fn cast(comptime Type: type, ptr: anytype) Type {
|
|
return @as(Type, @ptrFromInt(@intFromPtr(ptr)));
|
|
}
|
|
|
|
fn in(comptime Type: type, comptime tuple: anytype) bool {
|
|
var found: bool = false;
|
|
inline for (tuple) |Item| {
|
|
if (Item == Type) {
|
|
found = true;
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|