diff --git a/TODO.md b/TODO.md index 3efce6d..bceba2a 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,8 @@ `nn__` for functions related to "classes", `nn_` for static functions, `nn__t` for types. +- Get rid of `nn_address` +- Make a lot more stuff const - Rework to API to be much more future-proof to reduce potential breaking changes. # Parity with Vanilla OC (only the stuff that makes sense for an emulator) @@ -13,7 +15,6 @@ - `data` component (with error correction codes and maybe synthesizing audio) - `redstone` component - `internet` component -- `computer.getDeviceInfo()`, and subsequently, component device information - `computer.beep(frequency?: number, duration?: number, volume?: number)`, frequency between 20 and 2000 Hz, duration up to 5 seconds, volume from 0 to 1. # Bugfixes @@ -39,6 +40,7 @@ # Internal changes +- use more arenas!!!!!!! - make sure OOMs are recoverable - rework some interfaces to use pre-allocated or stack-allocated memory more - use dynamic arrays for signals (and maybe components), but still keep the maximums to prevent memory hogging diff --git a/build.zig b/build.zig index 0e9c11f..9530601 100644 --- a/build.zig +++ b/build.zig @@ -27,6 +27,7 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module { "src/resource.c", "src/component.c", "src/computer.c", + "src/deviceInfo.c", "src/universe.c", "src/unicode.c", // components diff --git a/src/computer.c b/src/computer.c index e6b45bf..c7c8b94 100644 --- a/src/computer.c +++ b/src/computer.c @@ -25,6 +25,8 @@ nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_archit nn_dealloc(alloc, c, sizeof(nn_computer)); return NULL; } + // TODO: handle OOM + c->deviceInfo = nn_newDeviceInfoList(&universe->ctx, 16); c->timeOffset = nn_getTime(universe); c->supportedArchCount = 0; c->argc = 0; @@ -646,3 +648,7 @@ const char *nn_resource_nextMethodInfo(nn_computer *computer, nn_size_t id, cons } return NULL; } + +nn_deviceInfoList_t *nn_getComputerDeviceInfoList(nn_computer *computer) { + return computer->deviceInfo; +} diff --git a/src/computer.h b/src/computer.h index a939cb3..a8a5600 100644 --- a/src/computer.h +++ b/src/computer.h @@ -50,6 +50,7 @@ typedef struct nn_computer { double callBudget; nn_size_t rid; nn_resource_t resources[NN_MAX_CONCURRENT_RESOURCES]; + nn_deviceInfoList_t *deviceInfo; } nn_computer; #endif diff --git a/src/deviceInfo.c b/src/deviceInfo.c new file mode 100644 index 0000000..9a1d40e --- /dev/null +++ b/src/deviceInfo.c @@ -0,0 +1,119 @@ +#include "neonucleus.h" + +typedef struct nn_deviceInfoPair_t { + char *key; + char *value; +} nn_deviceInfoPair_t; + +typedef struct nn_deviceInfo_t { + nn_deviceInfoPair_t *pairs; + nn_size_t len; + nn_size_t capacity; + char *address; + nn_Alloc alloc; +} nn_deviceInfo_t; + +typedef struct nn_deviceInfoList_t { + nn_Context ctx; + nn_deviceInfo_t *info; + nn_size_t len; + nn_size_t cap; +} nn_deviceInfoList_t; + +nn_deviceInfoList_t *nn_newDeviceInfoList(nn_Context *ctx, nn_size_t preallocate) { + nn_Alloc *alloc = &ctx->allocator; + + nn_deviceInfoList_t *list = nn_alloc(alloc, sizeof(nn_deviceInfoList_t)); + if(list == NULL) return NULL; + list->ctx = *ctx; + list->len = 0; + list->cap = preallocate; + list->info = nn_alloc(alloc, sizeof(nn_deviceInfo_t) * list->cap); + if(list->info == NULL) { + nn_dealloc(alloc, list, sizeof(nn_deviceInfoList_t)); + return NULL; + } + return list; +} + +static void nn_deleteDeviceInfo(nn_Context *ctx, nn_deviceInfo_t *info) { + nn_Alloc *alloc = &ctx->allocator; + + nn_deallocStr(alloc, info->address); + nn_dealloc(alloc, info->pairs, sizeof(nn_deviceInfoPair_t) * info->capacity); +} + +void nn_deleteDeviceInfoList(nn_deviceInfoList_t *list) { + for(nn_size_t i = 0; i < list->len; i++) { + nn_deleteDeviceInfo(&list->ctx, &list->info[i]); + } + + nn_Alloc alloc = list->ctx.allocator; + + nn_dealloc(&alloc, list->info, sizeof(nn_deviceInfo_t) * list->cap); + nn_dealloc(&alloc, list, sizeof(nn_deviceInfoList_t)); +} + +nn_deviceInfo_t *nn_addDeviceInfo(nn_deviceInfoList_t *list, nn_address address, nn_size_t maxKeys) { + if(list->len == list->cap) { + nn_size_t neededCap = list->cap; + if(neededCap < 1) neededCap = 1; + while(neededCap < (list->len + 1)) neededCap *= 2; + + nn_deviceInfo_t *newBuf = nn_resize(&list->ctx.allocator, list->info, sizeof(nn_deviceInfo_t) * list->cap, sizeof(nn_deviceInfo_t) * neededCap); + if(newBuf == NULL) return NULL; + list->cap = neededCap; + list->info = newBuf; + } + + nn_deviceInfoPair_t *pairs = nn_alloc(&list->ctx.allocator, sizeof(nn_deviceInfoPair_t) * maxKeys); + if(pairs == NULL) return NULL; + + nn_size_t i = list->len; + list->info[i] = (nn_deviceInfo_t) { + .alloc = list->ctx.allocator, + .pairs = pairs, + .len = 0, + .address = address == NULL ? nn_randomUUID(&list->ctx) : nn_strdup(&list->ctx.allocator, address), // TODO: handle OOM + .capacity = maxKeys, + }; + list->len++; + return list->info + i; +} + +void nn_removeDeviceInfo(nn_deviceInfoList_t *list, const char *key); + +nn_bool_t nn_registerDeviceKey(nn_deviceInfo_t *deviceInfo, const char *key, const char *value) { + if(deviceInfo->len == deviceInfo->capacity) return false; + nn_size_t i = deviceInfo->len; + nn_Alloc *alloc = &deviceInfo->alloc; + // TODO: handle OOM + deviceInfo->pairs[i].key = nn_strdup(alloc, key); + deviceInfo->pairs[i].value = nn_strdup(alloc, value); + deviceInfo->len++; + return true; +} + +nn_deviceInfo_t *nn_getDeviceInfoAt(nn_deviceInfoList_t *list, nn_size_t idx) { + if(idx >= list->len) return NULL; + return &list->info[idx]; +} + +const char *nn_getDeviceInfoAddress(nn_deviceInfo_t *deviceInfo) { + return deviceInfo->address; +} + +nn_size_t nn_getDeviceCount(nn_deviceInfoList_t *list) { + return list->len; +} + +const char *nn_iterateDeviceInfoKeys(nn_deviceInfo_t *deviceInfo, nn_size_t idx, const char **value) { + if(idx >= deviceInfo->len) return NULL; + nn_deviceInfoPair_t pair = deviceInfo->pairs[idx]; + if(value != NULL) *value = pair.value; + return pair.key; +} + +nn_size_t nn_getDeviceKeyCount(nn_deviceInfo_t *deviceInfo) { + return deviceInfo->len; +} diff --git a/src/emulator.c b/src/emulator.c index 61a2cc3..cfebd8f 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -811,6 +811,21 @@ int main(int argc, char **argv) { double interval = 1.0/tps; double totalTime = 0; + nn_deviceInfoList_t *list = nn_getComputerDeviceInfoList(computer); + + nn_deviceInfo_t *theRamStick = nn_addDeviceInfo(list, NULL, 8); + nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_CLASS, NN_DEVICEINFO_CLASS_RAM); + nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_CAPACITY, "4MiB"); + nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_DESCRIPTION, "Volatile storage device"); + nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_PRODUCT, "RAM 1:5"); + nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_VENDOR, "NeoComputers Technologies L.L.C."); + + nn_deviceInfo_t *theCPU = nn_addDeviceInfo(list, NULL, 8); + nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_CLASS, NN_DEVICEINFO_CLASS_CPU); + nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_DESCRIPTION, "Processor"); + nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_PRODUCT, "Cryzen 9 6900XXL"); + nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_VENDOR, "NeoComputers Technologies L.L.C."); + while(true) { if(WindowShouldClose()) break; nn_setEnergyInfo(computer, 5000, 5000); diff --git a/src/neonucleus.h b/src/neonucleus.h index 05f6003..d2e8b36 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -409,6 +409,48 @@ void *nn_queryUserdata(nn_universe *universe, const char *name); void nn_storeUserdata(nn_universe *universe, const char *name, void *data); double nn_getTime(nn_universe *universe); +// Device info + +typedef struct nn_deviceInfoList_t nn_deviceInfoList_t; +typedef struct nn_deviceInfo_t nn_deviceInfo_t; + +// Common / standard keys +#define NN_DEVICEINFO_KEY_CLASS "class" +#define NN_DEVICEINFO_KEY_VENDOR "vendor" +#define NN_DEVICEINFO_KEY_PRODUCT "product" +#define NN_DEVICEINFO_KEY_CAPACITY "capacity" +#define NN_DEVICEINFO_KEY_CLOCK "clock" +#define NN_DEVICEINFO_KEY_DESCRIPTION "description" + +// Common / standard values +#define NN_DEVICEINFO_CLASS_INPUT "input" +#define NN_DEVICEINFO_CLASS_RAM "memory" +#define NN_DEVICEINFO_CLASS_ROM "memory" // not a mistake, they both use memory +#define NN_DEVICEINFO_CLASS_CPU "processor" // also used by data card +#define NN_DEVICEINFO_CLASS_DATA "processor" // also used by data card +#define NN_DEVICEINFO_CLASS_GPU "display" // not a mistake, it and screen have the same class +#define NN_DEVICEINFO_CLASS_SCREEN "display" +#define NN_DEVICEINFO_CLASS_COMPUTER "system" +#define NN_DEVICEINFO_CLASS_STORAGE "volume" +#define NN_DEVICEINFO_CLASS_INTERNET "communication" +#define NN_DEVICEINFO_CLASS_REDSTONE "communication" // why do they use the same one? idfk +#define NN_DEVICEINFO_CLASS_MODEM "network" +#define NN_DEVICEINFO_CLASS_TUNNEL "network" +#define NN_DEVICEINFO_CLASS_GENERIC "generic" + +nn_deviceInfoList_t *nn_newDeviceInfoList(nn_Context *ctx, nn_size_t preallocate); +void nn_deleteDeviceInfoList(nn_deviceInfoList_t *deviceInfoList); +nn_deviceInfo_t *nn_addDeviceInfo(nn_deviceInfoList_t *list, nn_address address, nn_size_t maxKeys); +void nn_removeDeviceInfo(nn_deviceInfoList_t *list, const char *key); +nn_bool_t nn_registerDeviceKey(nn_deviceInfo_t *deviceInfo, const char *key, const char *value); +nn_deviceInfo_t *nn_getDeviceInfoAt(nn_deviceInfoList_t *list, nn_size_t idx); +nn_size_t nn_getDeviceCount(nn_deviceInfoList_t *list); +const char *nn_getDeviceInfoAddress(nn_deviceInfo_t *deviceInfo); +const char *nn_iterateDeviceInfoKeys(nn_deviceInfo_t *deviceInfo, nn_size_t idx, const char **value); +nn_size_t nn_getDeviceKeyCount(nn_deviceInfo_t *deviceInfo); + +// Computer running states + nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_architecture *arch, void *userdata, nn_size_t memoryLimit, nn_size_t componentLimit); nn_universe *nn_getUniverse(nn_computer *computer); int nn_tickComputer(nn_computer *computer); @@ -436,6 +478,7 @@ void nn_callCost(nn_computer *computer, double cost); double nn_getCallCost(nn_computer *computer); nn_bool_t nn_isOverworked(nn_computer *computer); void nn_triggerIndirect(nn_computer *computer); +nn_deviceInfoList_t *nn_getComputerDeviceInfoList(nn_computer *computer); /* The memory returned can be freed with nn_dealloc() */ char *nn_serializeProgram(nn_computer *computer, nn_Alloc *alloc, nn_size_t *len); @@ -1068,12 +1111,32 @@ void nn_hologram_fill(nn_hologram *hologram, int x, int z, int minY, int maxY, i void nn_hologram_copy(nn_hologram *hologram, int x, int z, int sx, int sz, int tx, int tz); float nn_hologram_getScale(nn_hologram *hologram); void nn_hologram_setScale(nn_hologram *hologram, float value); -void nn_hologram_getTranslation(nn_hologram *hologram, int *x, int *y, int *z); -void nn_hologram_setTranslation(nn_hologram *hologram, int x, int y, int z); +void nn_hologram_getTranslation(nn_hologram *hologram, double *x, double *y, double *z); +void nn_hologram_setTranslation(nn_hologram *hologram, double x, double y, double z); int nn_hologram_maxDepth(nn_hologram *hologram); int nn_hologram_getPaletteColor(nn_hologram *hologram, int index); int nn_hologram_setPaletteColor(nn_hologram *hologram, int index, int value); +typedef struct nn_externalComputerTable_t { + void *userdata; + void (*deinit)(void *userdata); + + nn_bool_t (*start)(void *userdata, nn_computer *requester, nn_errorbuf_t err); + nn_bool_t (*stop)(void *userdata, nn_computer *requester, nn_errorbuf_t err); + nn_bool_t (*isRunning)(void *userdata, nn_computer *requester, nn_errorbuf_t err); + void (*beep)(void *userdata, nn_computer *requester, double freq, double duration, double volume, nn_errorbuf_t err); + void (*crash)(void *userdata, nn_computer *requester, nn_errorbuf_t err); + nn_architecture *(*getArchitecture)(void *userdata, nn_computer *requester, nn_errorbuf_t err); + nn_bool_t (*isRobot)(void *userdata, nn_computer *requester, nn_errorbuf_t err); +} nn_externalComputerTable_t; + +typedef struct nn_externalComputer_t nn_externalComputer_t; + +// An external computer is a computer component. +// It may refer to the current computer (counter-intuitively) +// It may exist when the computer it is refering too has no running state (aka is powered off) +nn_externalComputer_t *nn_newExternalComputer(nn_Context *ctx, nn_externalComputerTable_t table); + #ifdef __cplusplus // c++ sucks } #endif diff --git a/src/sandbox.lua b/src/sandbox.lua index d709c53..fdd18e5 100644 --- a/src/sandbox.lua +++ b/src/sandbox.lua @@ -236,9 +236,7 @@ local libcomputer = { until computer.uptime() >= deadline end, beep = computer.beep, - getDeviceInfo = function() - return {} -- yup - end, + getDeviceInfo = computer.getDeviceInfo, getProgramLocations = function() return {} -- yup end, diff --git a/src/testLuaArch.c b/src/testLuaArch.c index 654c7ff..993405b 100644 --- a/src/testLuaArch.c +++ b/src/testLuaArch.c @@ -339,6 +339,36 @@ static int testLuaArch_computer_setState(lua_State *L) { return 1; } +static int testLuaArch_computer_getDeviceInfo(lua_State *L) { + nn_computer *c = testLuaArch_getComputer(L); + + nn_deviceInfoList_t *list = nn_getComputerDeviceInfoList(c); + nn_size_t deviceCount = nn_getDeviceCount(list); + + lua_createtable(L, 0, deviceCount); + int infoTable = lua_gettop(L); + + for(nn_size_t i = 0; i < deviceCount; i++) { + nn_deviceInfo_t *info = nn_getDeviceInfoAt(list, i); + lua_createtable(L, 0, 16); + int deviceTable = lua_gettop(L); + + nn_size_t j = 0; + while(true) { + const char *value = NULL; + const char *key = nn_iterateDeviceInfoKeys(info, j, &value); + j++; + if(key == NULL) break; + lua_pushstring(L, value); + lua_setfield(L, deviceTable, key); + } + + lua_setfield(L, infoTable, nn_getDeviceInfoAddress(info)); + } + + return 1; +} + static int testLuaArch_component_list(lua_State *L) { nn_computer *c = testLuaArch_getComputer(L); lua_createtable(L, 0, 10); @@ -598,6 +628,8 @@ void testLuaArch_loadEnv(lua_State *L) { lua_setfield(L, computer, "getState"); lua_pushcfunction(L, testLuaArch_computer_setState); lua_setfield(L, computer, "setState"); + lua_pushcfunction(L, testLuaArch_computer_getDeviceInfo); + lua_setfield(L, computer, "getDeviceInfo"); lua_setglobal(L, "computer"); lua_createtable(L, 0, 10);