Compare commits

...

9 Commits

Author SHA1 Message Date
IonutParau
087fd1e9b3 more TODO 2025-07-29 00:13:19 +02:00
IonutParau
82f62f5158 potential future change 2025-07-29 00:04:48 +02:00
IonutParau
b6d1131353 fixed a critical bug 2025-07-28 23:15:42 +02:00
IonutParau
bf473f5c4c minor build changes 2025-07-28 22:41:12 +02:00
IonutParau
255df4d352 PIC 2.0 2025-07-28 22:07:06 +02:00
IonutParau
48faf57d84 made stuff PIC 2025-07-28 22:05:18 +02:00
IonutParau
3f3e18dbe8 progress 2025-07-28 20:20:50 +02:00
IonutParau
cd02f31368 tunnels 2025-07-28 20:20:34 +02:00
IonutParau
b7ddf9c34e minor changes to the serialization process 2025-07-28 19:03:22 +02:00
9 changed files with 205 additions and 39 deletions

View File

@ -10,7 +10,6 @@
- `hologram` component
- `computer` component
- `tunnel` component
- `data` component (with error correction codes and maybe synthesizing audio)
- `redstone` component
- `internet` component
@ -44,4 +43,7 @@
- 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
- setup an extensive testing system to find bugs easier
- optimize the codebase by using globals instead of universe userdata
- use compiler hints to let the optimizer make the code even faster
- (maybe) rework a bunch of internal structures to use tagged unions over vtables (such as components). This may make certain APIs unnecessary or slightly
different. This can improve performance at the cost of making the codebase more complex

View File

@ -52,6 +52,7 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
if(strict) "-Werror" else "",
"-std=gnu23",
"-Wno-keyword-macro", // cuz bools
"-fPIE",
},
});
@ -61,6 +62,9 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
.files = &.{
"src/tinycthread.c",
},
.flags = &.{
"-fPIE",
},
});
}
@ -148,11 +152,15 @@ pub fn build(b: *std.Build) !void {
const engineStatic = b.addStaticLibrary(.{
.name = "neonucleus",
.root_module = engineMod,
.pic = true,
.code_model = .default,
});
const engineShared = b.addSharedLibrary(.{
.name = if(os == .windows) "neonucleusdll" else "neonucleus",
.root_module = engineMod,
.pic = true,
.code_model = .default,
});
const engineStep = b.step("engine", "Builds the engine as a static library");

View File

@ -1,7 +1,59 @@
#include "../neonucleus.h"
typedef struct nn_loopTunnel_t {
nn_Context ctx;
nn_debugLoopbackNetworkOpts opts;
char wakeup[NN_MAX_WAKEUPMSG];
nn_size_t wakeupLen;
} nn_loopTunnel_t;
static void nni_debugTunnel_deinit(nn_loopTunnel_t *t) {
nn_Alloc a = t->ctx.allocator;
nn_deallocStr(&a, t->opts.address);
nn_dealloc(&a, t, sizeof(nn_loopTunnel_t));
}
static nn_size_t nni_debugTunnel_getChannel(nn_loopTunnel_t *t, char *buf, nn_errorbuf_t err) {
nn_strcpy(buf, "loopback");
return 8;
}
static nn_size_t nni_debugTunnel_getWakeMessage(nn_loopTunnel_t *t, char *buf, nn_errorbuf_t err) {
nn_memcpy(buf, t->wakeup, t->wakeupLen);
return t->wakeupLen;
}
static nn_size_t nni_debugTunnel_setWakeMessage(nn_loopTunnel_t *t, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err) {
if(buflen > NN_MAX_CHANNEL_SIZE) buflen = NN_MAX_CHANNEL_SIZE;
nn_memcpy(t->wakeup, buf, buflen);
t->wakeupLen = buflen;
return buflen;
}
static void nni_debugTunnel_send(nn_loopTunnel_t *t, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err) {
nn_pushNetworkMessage(t->opts.computer, t->opts.address, "loopback", NN_TUNNEL_PORT, 0, values, valueCount);
}
nn_tunnel *nn_debugLoopbackTunnel(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control) {
nn_tunnelTable table = {};
nn_Alloc *alloc = &context->allocator;
nn_loopTunnel_t *t = nn_alloc(alloc, sizeof(nn_loopTunnel_t));
t->ctx = *context;
t->opts = opts;
t->opts.address = nn_strdup(alloc, t->opts.address);
t->wakeupLen = 0;
nn_tunnelTable table = {
.userdata = t,
.deinit = (void *)nni_debugTunnel_deinit,
.maxValues = opts.maxValues,
.maxPacketSize = opts.maxPacketSize,
.getChannel = (void *)nni_debugTunnel_getChannel,
.getWakeMessage = (void *)nni_debugTunnel_getWakeMessage,
.setWakeMessage = (void *)nni_debugTunnel_setWakeMessage,
.send = (void *)nni_debugTunnel_send,
};
return nn_newTunnel(context, table, control);
}

View File

@ -42,9 +42,114 @@ void nn_tunnel_destroy(void *_, nn_component *component, nn_tunnel *tunnel) {
nn_destroyTunnel(tunnel);
}
static void nni_tunnel_maxPacketSize(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, tunnel->table.maxPacketSize);
}
static void nni_tunnel_maxValues(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_return_integer(computer, tunnel->table.maxValues);
}
static void nni_tunnel_getChannel(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
char buf[NN_MAX_CHANNEL_SIZE];
nn_lock(&tunnel->ctx, tunnel->lock);
nn_size_t len = tunnel->table.getChannel(tunnel->table.userdata, buf, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, len);
}
static void nni_tunnel_getWakeMessage(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_errorbuf_t err = "";
char buf[NN_MAX_CHANNEL_SIZE];
nn_lock(&tunnel->ctx, tunnel->lock);
nn_size_t len = tunnel->table.getWakeMessage(tunnel->table.userdata, buf, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, len);
}
static void nni_tunnel_setWakeMessage(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_size_t buflen;
const char *buf = nn_toString(nn_getArgument(computer, 0), &buflen);
if(buf == NULL) {
nn_setCError(computer, "invalid wake message");
return;
}
if(buflen > NN_MAX_WAKEUPMSG) {
buflen = NN_MAX_WAKEUPMSG;
}
nn_bool_t fuzzy = nn_toBoolean(nn_getArgument(computer, 1)); // nil is false
nn_errorbuf_t err = "";
nn_lock(&tunnel->ctx, tunnel->lock);
buflen = tunnel->table.setWakeMessage(tunnel->table.userdata, buf, buflen, fuzzy, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_string(computer, buf, buflen);
}
static void nni_tunnel_send(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) {
nn_value vals[tunnel->table.maxValues];
nn_size_t valLen = nn_getArgumentCount(computer);
if(valLen > tunnel->table.maxValues) {
nn_setCError(computer, "too many values");
return;
}
for(nn_size_t i = 0; i < valLen; i++) {
vals[i] = nn_getArgument(computer, i);
}
nn_size_t bytesSent = nn_measurePacketSize(vals, valLen);
if(bytesSent > tunnel->table.maxPacketSize) {
nn_setCError(computer, "packet too big");
return;
}
nn_simulateBufferedIndirect(component, bytesSent, tunnel->ctrl.packetBytesPerTick);
double d = (double)bytesSent / tunnel->table.maxPacketSize;
nn_addHeat(computer, d * tunnel->ctrl.heatPerFullPacket);
nn_removeEnergy(computer, d * tunnel->ctrl.energyPerFullPacket);
nn_errorbuf_t err = "";
nn_lock(&tunnel->ctx, tunnel->lock);
tunnel->table.send(tunnel->table.userdata, vals, valLen, err);
nn_unlock(&tunnel->ctx, tunnel->lock);
if(!nn_error_isEmpty(err)) {
nn_setError(computer, err);
return;
}
nn_return_boolean(computer, true);
}
void nn_loadTunnelTable(nn_universe *universe) {
nn_componentTable *tunnelTable = nn_newComponentTable(nn_getAllocator(universe), "tunnel", NULL, NULL, (nn_componentDestructor *)nn_tunnel_destroy);
nn_storeUserdata(universe, "NN:TUNNEL", tunnelTable);
nn_defineMethod(tunnelTable, "maxPacketSize", (nn_componentMethod *)nni_tunnel_maxPacketSize, "maxPacketSize(): integer - Returns the maximum size of a packet");
nn_defineMethod(tunnelTable, "maxValues", (nn_componentMethod *)nni_tunnel_maxValues, "maxValues(): integer - the maximum number of values which can be sent in a packet");
nn_defineMethod(tunnelTable, "getChannel", (nn_componentMethod *)nni_tunnel_getChannel, "getChannel(): string - Gets the ID of the communications channel of the tunnel. Under normal conditions, there are only 2 cards in the same universe which share this.");
nn_defineMethod(tunnelTable, "getWakeMessage", (nn_componentMethod *)nni_tunnel_getWakeMessage, "getWakeMessage(): string - Returns the current wake message");
nn_defineMethod(tunnelTable, "setWakeMessage", (nn_componentMethod *)nni_tunnel_setWakeMessage, "setWakeMessage(msg: string[, fuzzy: boolean]): string - Sets the new wake message");
nn_defineMethod(tunnelTable, "send", (nn_componentMethod *)nni_tunnel_send, "send(...): boolean - Sends a tunnel message. Returns whether it was sent.");
}
nn_component *nn_addTunnel(nn_computer *computer, nn_address address, int slot, nn_tunnel *tunnel) {

View File

@ -20,43 +20,43 @@ static void nni_veeprom_deinit(nn_veeprom *veeprom) {
nn_dealloc(&a, veeprom, sizeof(nn_veeprom));
}
static void nni_veeprom_getLabel(nn_veeprom *veeprom, char *buf, nn_size_t *buflen) {
static void nni_veeprom_getLabel(nn_veeprom *veeprom, char *buf, nn_size_t *buflen, nn_errorbuf_t err) {
nn_memcpy(buf, veeprom->label, veeprom->labelLen);
*buflen = veeprom->labelLen;
}
static nn_size_t nni_veeprom_setLabel(nn_veeprom *veeprom, const char *buf, nn_size_t buflen) {
static nn_size_t nni_veeprom_setLabel(nn_veeprom *veeprom, const char *buf, nn_size_t buflen, nn_errorbuf_t err) {
if(buflen > NN_LABEL_SIZE) buflen = NN_LABEL_SIZE;
nn_memcpy(veeprom->label, buf, buflen);
veeprom->labelLen = buflen;
return buflen;
}
static nn_size_t nni_veeprom_get(nn_veeprom *veeprom, char *buf) {
static nn_size_t nni_veeprom_get(nn_veeprom *veeprom, char *buf, nn_errorbuf_t err) {
nn_memcpy(buf, veeprom->code, veeprom->codeLen);
return veeprom->codeLen;
}
static void nni_veeprom_set(nn_veeprom *veeprom, const char *buf, nn_size_t len) {
static void nni_veeprom_set(nn_veeprom *veeprom, const char *buf, nn_size_t len, nn_errorbuf_t err) {
nn_memcpy(veeprom->code, buf, len);
veeprom->codeLen = len;
}
static nn_size_t nni_veeprom_getData(nn_veeprom *veeprom, char *buf) {
static nn_size_t nni_veeprom_getData(nn_veeprom *veeprom, char *buf, nn_errorbuf_t err) {
nn_memcpy(buf, veeprom->data, veeprom->dataLen);
return veeprom->dataLen;
}
static void nni_veeprom_setData(nn_veeprom *veeprom, const char *buf, nn_size_t len) {
static void nni_veeprom_setData(nn_veeprom *veeprom, const char *buf, nn_size_t len, nn_errorbuf_t err) {
nn_memcpy(veeprom->data, buf, len);
veeprom->dataLen = len;
}
static nn_bool_t nni_veeprom_isReadonly(nn_veeprom *eeprom) {
static nn_bool_t nni_veeprom_isReadonly(nn_veeprom *eeprom, nn_errorbuf_t err) {
return eeprom->isReadOnly;
}
static void nni_veeprom_makeReadonly(nn_veeprom *eeprom) {
static void nni_veeprom_makeReadonly(nn_veeprom *eeprom, nn_errorbuf_t err) {
eeprom->isReadOnly = true;
}

View File

@ -473,20 +473,20 @@ nn_size_t nn_getReturnCount(nn_computer *computer) {
return computer->retc;
}
char *nn_serializeProgram(nn_computer *computer, nn_size_t *len) {
return computer->arch->serialize(computer, computer->archState, computer->arch->userdata, len);
char *nn_serializeProgram(nn_computer *computer, nn_Alloc *alloc, nn_size_t *len) {
return computer->arch->serialize(computer, alloc, computer->archState, computer->arch->userdata, len);
}
void nn_deserializeProgram(nn_computer *computer, const char *memory, nn_size_t len) {
computer->arch->deserialize(computer, memory, len, computer->archState, computer->arch->userdata);
}
void nn_lockComputer(nn_computer *computer) {
nn_lock(&computer->universe->ctx, computer->lock);
nn_Context *nn_getComputerContext(nn_computer *computer) {
return &computer->universe->ctx;
}
void nn_unlockComputer(nn_computer *computer) {
nn_unlock(&computer->universe->ctx, computer->lock);
nn_guard *nn_getComputerLock(nn_computer *computer) {
return computer->lock;
}
void nn_return_nil(nn_computer *computer) {

View File

@ -811,8 +811,6 @@ int main(int argc, char **argv) {
double interval = 1.0/tps;
double totalTime = 0;
SetTargetFPS(144);
while(true) {
if(WindowShouldClose()) break;
nn_setEnergyInfo(computer, 5000, 5000);

View File

@ -140,18 +140,6 @@ typedef struct nn_universe nn_universe;
typedef struct nn_computer nn_computer;
typedef struct nn_component nn_component;
typedef struct nn_componentTable nn_componentTable;
typedef struct nn_architecture {
void *userdata;
const char *archName;
void *(*setup)(nn_computer *computer, void *userdata);
void (*teardown)(nn_computer *computer, void *state, void *userdata);
nn_size_t (*getMemoryUsage)(nn_computer *computer, void *state, void *userdata);
void (*tick)(nn_computer *computer, void *state, void *userdata);
/* Pointer returned should be allocated with nn_malloc or nn_realloc, so it can be freed with nn_free */
char *(*serialize)(nn_computer *computer, void *state, void *userdata, nn_size_t *len);
void (*deserialize)(nn_computer *computer, const char *data, nn_size_t len, void *state, void *userdata);
} nn_architecture;
typedef char *nn_address;
// A non-zero malloc is a null ptr, with a 0 oldSize, but a non-0 newSize.
// A zero malloc is never called, the proc address itself is returned, which is ignored when freeing.
@ -164,6 +152,19 @@ typedef struct nn_Alloc {
nn_AllocProc *proc;
} nn_Alloc;
typedef struct nn_architecture {
void *userdata;
const char *archName;
void *(*setup)(nn_computer *computer, void *userdata);
void (*teardown)(nn_computer *computer, void *state, void *userdata);
nn_size_t (*getMemoryUsage)(nn_computer *computer, void *state, void *userdata);
void (*tick)(nn_computer *computer, void *state, void *userdata);
/* Pointer returned should be allocated with nn_malloc or nn_realloc, so it can be freed with nn_free */
char *(*serialize)(nn_computer *computer, nn_Alloc *alloc, void *state, void *userdata, nn_size_t *len);
void (*deserialize)(nn_computer *computer, const char *data, nn_size_t len, void *state, void *userdata);
} nn_architecture;
typedef char *nn_address;
#define NN_LOCK_DEFAULT 0
#define NN_LOCK_IMMEDIATE 1
@ -436,12 +437,12 @@ double nn_getCallCost(nn_computer *computer);
nn_bool_t nn_isOverworked(nn_computer *computer);
void nn_triggerIndirect(nn_computer *computer);
/* The memory returned can be freed with nn_free() */
char *nn_serializeProgram(nn_computer *computer, nn_size_t *len);
/* The memory returned can be freed with nn_dealloc() */
char *nn_serializeProgram(nn_computer *computer, nn_Alloc *alloc, nn_size_t *len);
void nn_deserializeProgram(nn_computer *computer, const char *memory, nn_size_t len);
void nn_lockComputer(nn_computer *computer);
void nn_unlockComputer(nn_computer *computer);
nn_Context *nn_getComputerContext(nn_computer *computer);
nn_guard *nn_getComputerLock(nn_computer *computer);
/// This means the computer has not yet started.
#define NN_STATE_SETUP 0
@ -1016,10 +1017,10 @@ typedef struct nn_tunnelTable {
nn_size_t maxValues;
nn_size_t maxPacketSize;
nn_bool_t (*send)(void *userdata, nn_value *values, nn_size_t valueCount);
void (*getChannel)(void *userdata, char *buf, nn_size_t *buflen);
void (*getWakeMessage)(void *userdata, char *buf, nn_size_t *buflen);
nn_size_t (*setWakeMessage)(void *userdata, const char *buf, nn_size_t buflen, nn_bool_t fuzzy);
void (*send)(void *userdata, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err);
nn_size_t (*getChannel)(void *userdata, char *buf, nn_errorbuf_t err);
nn_size_t (*getWakeMessage)(void *userdata, char *buf, nn_errorbuf_t err);
nn_size_t (*setWakeMessage)(void *userdata, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err);
} nn_tunnelTable;
typedef struct nn_tunnel nn_tunnel;

View File

@ -704,7 +704,7 @@ size_t testLuaArch_getMemoryUsage(nn_computer *computer, testLuaArch *arch, void
return arch->memoryUsed;
}
char *testLuaArch_serialize(nn_computer *computer, testLuaArch *arch, void *_, size_t *len) {
char *testLuaArch_serialize(nn_computer *computer, nn_Alloc *alloc, testLuaArch *arch, void *_, size_t *len) {
*len = 0;
return NULL;
}