Compare commits

...

11 Commits

9 changed files with 902 additions and 404 deletions

22
TODO.md
View File

@@ -1,15 +1,3 @@
# To improve the API
- finish tmpfs (rework the whole thing)
# To re-evaluate
- Make the hashmap growing/shrinking to save on memory.
- Exposing the internal hashmap implementation.
- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them
to be made more portable.
- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code.
# Vanilla components needed # Vanilla components needed
Not everything OC has (as a few of them are really MC-centered) but most of it. Not everything OC has (as a few of them are really MC-centered) but most of it.
@@ -44,7 +32,7 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
- `iron_noteblock` component - `iron_noteblock` component
- `colorful_lamp` component - `colorful_lamp` component
# To make it good (after API is stable) # To make it good
- make more stuff const if it can be. Gotta help out the optimizer - make more stuff const if it can be. Gotta help out the optimizer
- put a bunch of internal-only functions as fastcall - put a bunch of internal-only functions as fastcall
@@ -226,3 +214,11 @@ TODO: interface
## OLED/IPU ## OLED/IPU
TODO: interface TODO: interface
# To re-evaluate
- Make the hashmap growing/shrinking to save on memory.
- Exposing the internal hashmap implementation.
- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them
to be made more portable.
- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code.

View File

@@ -20,7 +20,6 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
.sanitize_c = if (strict) .full else null, .sanitize_c = if (strict) .full else null,
}); });
dataMod.addCSourceFiles(.{ dataMod.addCSourceFiles(.{
.files = &[_][]const u8{ .files = &[_][]const u8{
"src/neonucleus.c", "src/neonucleus.c",
@@ -89,11 +88,7 @@ fn compileTheRightLua(b: *std.Build, target: std.Build.ResolvedTarget, version:
// its a static library because COFF is a pile of shit // its a static library because COFF is a pile of shit
const c = b.addLibrary(.{ const c = b.addLibrary(.{
.name = "lua", .name = "lua",
.root_module = b.addModule("luamod", .{ .root_module = b.addModule("luamod", .{ .link_libc = true, .optimize = .ReleaseFast, .target = target, .pic = true }),
.link_libc = true,
.optimize = .ReleaseFast,
.target = target,
}),
.linkage = .static, .linkage = .static,
}); });

View File

@@ -124,6 +124,27 @@ static void luaArch_nnToLua(luaArch *arch, lua_State *L, size_t nnIdx) {
} }
static int luaArch_computer_beep(lua_State *L) { static int luaArch_computer_beep(lua_State *L) {
if(lua_type(L, 1) == LUA_TSTRING) {
nn_MorseBeep beep = {.frequency = 1000, .beepDuration = 200, .volume = 1};
beep.pattern = lua_tostring(L, 1);
if(lua_isnumber(L, 2)) {
beep.frequency = lua_tonumber(L, 2);
}
if(lua_isnumber(L, 3)) {
beep.beepDuration = lua_tonumber(L, 3);
}
if(lua_isnumber(L, 4)) {
beep.volume = lua_tonumber(L, 4);
}
if(beep.frequency < 20) beep.frequency = 20;
if(beep.beepDuration < 0) beep.beepDuration = 0;
if(beep.volume < 0) beep.volume = 0;
if(beep.frequency > 20000) beep.frequency = 20000;
if(beep.beepDuration > 5) beep.beepDuration = 5;
if(beep.volume > 1) beep.volume = 1;
nn_beepComputerMorse(luaArch_from(L)->computer, beep);
return 0;
}
nn_Beep beep = {.frequency = 1000, .duration = 1, .volume = 1}; nn_Beep beep = {.frequency = 1000, .duration = 1, .volume = 1};
if(lua_isnumber(L, 1)) { if(lua_isnumber(L, 1)) {
beep.frequency = lua_tonumber(L, 1); beep.frequency = lua_tonumber(L, 1);

View File

@@ -136,9 +136,9 @@ local function realInvoke(address, method, ...)
if computer.isIdle() then sysyield() end -- machine idle if computer.isIdle() then sysyield() end -- machine idle
end end
if os.getenv("NN_INVDBG") and component.type(address) == os.getenv("NN_INVDBG") then if component.type(address) == os.getenv("NN_INVDBG") or string.find(os.getenv("NN_METDBG") or "", method, nil, true) then
print("invoked", address, method, ...) print("invoked", address, method, ...)
print("got", table.unpack(t)) print(string.format("got %d values", #t), table.unpack(t))
end end
for i=1,#t do t[i] = sandboxValue(t[i]) end for i=1,#t do t[i] = sandboxValue(t[i]) end

View File

@@ -500,6 +500,7 @@ typedef struct ne_memSand {
char *buf; char *buf;
size_t used; size_t used;
size_t cap; size_t cap;
size_t active;
} ne_memSand; } ne_memSand;
void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize) { void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize) {
@@ -509,12 +510,16 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
newSize = ne_alignAlloc(newSize, NN_ALLOC_ALIGN); newSize = ne_alignAlloc(newSize, NN_ALLOC_ALIGN);
// never free // never free
if(newSize == 0) return NULL; if(newSize == 0) {
sand->active -= oldSize;
return NULL;
}
if(memory == NULL) { if(memory == NULL) {
if(sand->cap - sand->used < newSize) return NULL; if(sand->cap - sand->used < newSize) return NULL;
// alloc new // alloc new
void *mem = sand->buf + sand->used; void *mem = sand->buf + sand->used;
sand->used += newSize; sand->used += newSize;
sand->active += newSize;
return mem; return mem;
} }
// realloc // realloc
@@ -522,6 +527,7 @@ void *ne_sandbox_alloc(void *state, void *memory, size_t oldSize, size_t newSize
if(sand->cap - sand->used < newSize) return NULL; if(sand->cap - sand->used < newSize) return NULL;
void *mem = sand->buf + sand->used; void *mem = sand->buf + sand->used;
sand->used += newSize; sand->used += newSize;
sand->active += newSize;
memcpy(mem, memory, oldSize); memcpy(mem, memory, oldSize);
return mem; return mem;
} }
@@ -536,6 +542,10 @@ void ne_env(nn_EnvironmentRequest *req) {
printf("beep: %f Hz %fs %.02f%%\n", req->beep.frequency, req->beep.duration, req->beep.volume*100); printf("beep: %f Hz %fs %.02f%%\n", req->beep.frequency, req->beep.duration, req->beep.volume*100);
return; return;
} }
if(req->action == NN_ENV_BEEPMORSE) {
printf("morse beep: %s, %f Hz %fs %.02f%%\n", req->morseBeep.pattern, req->morseBeep.frequency, req->morseBeep.beepDuration, req->morseBeep.volume*100);
return;
}
if(req->action == NN_ENV_DRAWENERGY) { if(req->action == NN_ENV_DRAWENERGY) {
accumulatedEnergyCost += req->energy; accumulatedEnergyCost += req->energy;
totalEnergyLoss += req->energy; totalEnergyLoss += req->energy;
@@ -546,6 +556,25 @@ void ne_env(nn_EnvironmentRequest *req) {
} }
} }
nn_Exit ne_inetBullshit(nn_InternetRequest *req) {
// welcome to hell
nn_Computer *C = req->computer;
// TODO: make this shi connect to the real internet
// preferrably use libcurl, openssl and a basic blocking socket layer
if(req->action == NN_INTERNET_CONNECT) {
req->connection->state = ne_inetBullshit;
return NN_OK;
}
if(req->action == NN_INTERNET_CLOSE) {
return NN_OK;
}
if(C) nn_setError(C, "inet bullshit: not supported");
return NN_EBADCALL;
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
const char *player = getenv("USER"); const char *player = getenv("USER");
@@ -566,6 +595,7 @@ int main(int argc, char **argv) {
ne_memSand sand; ne_memSand sand;
sand.buf = NULL; sand.buf = NULL;
sand.active = 0;
if(sandboxMem) { if(sandboxMem) {
// 1 MiB pre-allocated to prevent erasing the free-list // 1 MiB pre-allocated to prevent erasing the free-list
@@ -599,6 +629,7 @@ int main(int argc, char **argv) {
nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit); nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit);
nn_Component *modem = nn_createModem(u, NULL, &nn_defaultWirelessModems[1], NULL, ne_modemBullshit); nn_Component *modem = nn_createModem(u, NULL, &nn_defaultWirelessModems[1], NULL, ne_modemBullshit);
nn_Component *tunnel = nn_createTunnel(u, NULL, &nn_defaultTunnel, NULL, ne_tunnelBullshit); nn_Component *tunnel = nn_createTunnel(u, NULL, &nn_defaultTunnel, NULL, ne_tunnelBullshit);
nn_Component *inet = nn_createInternet(u, NULL, &nn_defaultInternetCard, NULL, ne_inetBullshit);
char *eepromCode = (char *)minBIOS; char *eepromCode = (char *)minBIOS;
size_t eepromSize = strlen(minBIOS); size_t eepromSize = strlen(minBIOS);
@@ -626,10 +657,8 @@ int main(int argc, char **argv) {
char mainfspath[NN_MAX_PATH]; char mainfspath[NN_MAX_PATH];
snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir); snprintf(mainfspath, NN_MAX_PATH, "data/%s", mainDir);
nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &nn_defaultFilesystems[tier-1], true); nn_Component *managedfs = ncl_createFilesystem(u, NULL, mainfspath, &nn_defaultFilesystems[tier-1], true);
//nn_Component *tmpfs = ncl_createTmpFS(u, NULL, &nn_defaultTmpFS, NCL_FILECOST_DEFAULT, false); nn_Component *tmpfs = ncl_createTmpFS(u, NULL, &nn_defaultTmpFS, NCL_FILECOST_DEFAULT, false);
nn_Component *tmpfs = ncl_createFilesystem(u, NULL, "/tmp", &nn_defaultFilesystems[3], false);
nn_Component *testingfs = ncl_createFilesystem(u, NULL, "aux", &nn_defaultFilesystems[3], false); nn_Component *testingfs = ncl_createFilesystem(u, NULL, "aux", &nn_defaultFilesystems[3], false);
// until TmpFS works
nn_Component *debugfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false); nn_Component *debugfs = ncl_createTmpFS(u, NULL, &nn_defaultFilesystems[3], NCL_FILECOST_DEFAULT, false);
const char * const testDriveData = const char * const testDriveData =
@@ -700,7 +729,7 @@ int main(int argc, char **argv) {
ncl_mountKeyboard(scrstate, "mainKB"); ncl_mountKeyboard(scrstate, "mainKB");
// we assume server basically // we assume server basically
nn_Computer *c = nn_createComputer(u, NULL, NULL, ramTotal, nn_defaultComponentLimits[tier-1] * 4, 256); nn_Computer * volatile c = nn_createComputer(u, NULL, NULL, ramTotal, nn_defaultComponentLimits[tier-1] * 4, 256);
nn_Environment cEnv = { nn_Environment cEnv = {
.userdata = NULL, .userdata = NULL,
.handler = ne_env, .handler = ne_env,
@@ -746,6 +775,7 @@ int main(int argc, char **argv) {
nn_mountComponent(c, modem, 7, false); nn_mountComponent(c, modem, 7, false);
nn_mountComponent(c, tunnel, 8, false); nn_mountComponent(c, tunnel, 8, false);
nn_mountComponent(c, debugfs, 9, false); nn_mountComponent(c, debugfs, 9, false);
nn_mountComponent(c, inet, 10, false);
int ltx = 0, lty = 0; int ltx = 0, lty = 0;
double scrollBuf = 0; double scrollBuf = 0;
double tickTime = 0; double tickTime = 0;
@@ -985,11 +1015,15 @@ cleanup:;
nn_dropComponent(modem); nn_dropComponent(modem);
nn_dropComponent(tunnel); nn_dropComponent(tunnel);
nn_dropComponent(debugfs); nn_dropComponent(debugfs);
nn_dropComponent(inet);
// rip the universe // rip the universe
nn_destroyUniverse(u); nn_destroyUniverse(u);
ncl_destroyGlyphCache(gc); ncl_destroyGlyphCache(gc);
if(eepromPath != NULL) free(eepromCode); if(eepromPath != NULL) free(eepromCode);
CloseWindow(); CloseWindow();
free(sand.buf); free(sand.buf);
if(sand.buf != NULL) {
printf("Leaked: %zu bytes\n", sand.active);
}
return 0; return 0;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -258,35 +258,29 @@ typedef struct ncl_ComponentStat {
// specific properties // specific properties
union { union {
struct { struct {
const nn_EEPROM *conf;
size_t codeUsed; size_t codeUsed;
size_t dataUsed; size_t dataUsed;
} eeprom; } eeprom;
struct { struct {
const nn_Filesystem *conf;
size_t spaceUsed; size_t spaceUsed;
size_t realDiskUsage; size_t realDiskUsage;
size_t filesOpen; size_t filesOpen;
const char *path; const char *path;
} fs; } fs;
struct { struct {
const nn_Drive *conf;
size_t lastSector; size_t lastSector;
} drive; } drive;
struct { struct {
const nn_NandFlash *conf;
size_t currentWriteCount; size_t currentWriteCount;
double wearlevel; double wearlevel;
} flash; } flash;
struct { struct {
const nn_GPU *conf;
size_t vramFree; size_t vramFree;
size_t bufferCount; size_t bufferCount;
// can be NULL if there is none // can be NULL if there is none
const char *boundScreen; const char *boundScreen;
} gpu; } gpu;
struct { struct {
const nn_ScreenConfig *conf;
ncl_ScreenState *state; ncl_ScreenState *state;
ncl_ScreenFlags flags; ncl_ScreenFlags flags;
int viewportWidth; int viewportWidth;

View File

@@ -1074,6 +1074,7 @@ struct nn_Computer {
nn_Universe *universe; nn_Universe *universe;
nn_Environment env; nn_Environment env;
nn_Lock *lock; nn_Lock *lock;
nn_refc_t refc;
void *userdata; void *userdata;
char *address; char *address;
char *tmpaddress; char *tmpaddress;
@@ -1176,6 +1177,7 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
c->state = NN_BOOTUP; c->state = NN_BOOTUP;
c->universe = universe; c->universe = universe;
c->userdata = userdata; c->userdata = userdata;
c->refc = 1;
c->lock = nn_createLock(ctx); c->lock = nn_createLock(ctx);
if(c->lock == NULL) { if(c->lock == NULL) {
@@ -1233,6 +1235,14 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
return c; return c;
} }
void nn_retainComputer(nn_Computer *computer) {
nn_retainComputerN(computer, 1);
}
void nn_retainComputerN(nn_Computer *computer, size_t n) {
nn_incRef(&computer->refc, n);
}
void nn_lockComputer(nn_Computer *computer) { void nn_lockComputer(nn_Computer *computer) {
nn_lock(&computer->universe->ctx, computer->lock); nn_lock(&computer->universe->ctx, computer->lock);
} }
@@ -1430,7 +1440,22 @@ void nn_beepComputer(nn_Computer *computer, nn_Beep beep) {
computer->env.handler(&req); computer->env.handler(&req);
} }
void nn_beepComputerMorse(nn_Computer *computer, nn_MorseBeep beep) {
if(beep.beepDuration < 0) beep.beepDuration = 0;
nn_EnvironmentRequest req;
req.userdata = computer->env.userdata;
req.computer = computer;
req.action = NN_ENV_BEEPMORSE;
req.morseBeep = beep;
computer->env.handler(&req);
}
void nn_destroyComputer(nn_Computer *computer) { void nn_destroyComputer(nn_Computer *computer) {
nn_destroyComputerN(computer, 1);
}
void nn_destroyComputerN(nn_Computer *computer, size_t n) {
if(!nn_decRef(&computer->refc, n)) return;
nn_Context *ctx = &computer->universe->ctx; nn_Context *ctx = &computer->universe->ctx;
nn_stopComputer(computer); nn_stopComputer(computer);
@@ -3022,6 +3047,7 @@ const nn_Filesystem nn_defaultTmpFS = NN_INIT(nn_Filesystem) {
.readsPerTick = 15, .readsPerTick = 15,
.writesPerTick = 6, .writesPerTick = 6,
.dataEnergyCost = 0.1 / NN_MiB, .dataEnergyCost = 0.1 / NN_MiB,
.maxReadSize = 2048,
}; };
const nn_Drive nn_defaultDrives[4] = { const nn_Drive nn_defaultDrives[4] = {
@@ -4473,6 +4499,7 @@ static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
nn_Context *ctx = req->ctx; nn_Context *ctx = req->ctx;
nn_FSState *state = req->classState; nn_FSState *state = req->classState;
nn_FSRequest freq; nn_FSRequest freq;
freq.action = NN_FS_DROP;
freq.ctx = req->ctx; freq.ctx = req->ctx;
freq.computer = req->computer; freq.computer = req->computer;
freq.state = req->state; freq.state = req->state;
@@ -7403,5 +7430,274 @@ nn_Component *nn_createTunnel(nn_Universe *universe, const char *address, const
nn_InternetCard nn_defaultInternetCard = { nn_InternetCard nn_defaultInternetCard = {
.protocolsSupported = NN_INET_ALL, .protocolsSupported = NN_INET_ALL,
.maxReadSize = 64 * NN_KiB,
.transmissionEnergyCost = 0, .transmissionEnergyCost = 0,
}; };
typedef enum nn_InternetNum {
NN_INETNUM_MAXREADSIZE,
// is*Enabled
NN_INETNUM_ISHTTP,
NN_INETNUM_ISTCP,
NN_INETNUM_ISTLS,
NN_INETNUM_ISUDP,
NN_INETNUM_ISWEBSOCK,
// TCP/TLS connection
NN_INETNUM_CONNECT,
// WebSocket/UDP connection
NN_INETNUM_CHANNEL,
// HTTP/HTTPS request
NN_INETNUM_REQUEST,
NN_INETNUM_COUNT,
} nn_InternetNum;
typedef struct nn_InternetState {
nn_Context *ctx;
nn_InternetCard inet;
nn_InternetHandler *handler;
} nn_InternetState;
static nn_Exit nn_inetCloseConn(nn_InternetRequest req, nn_InternetHandler *handler, nn_InternetConnection *conn) {
if(conn == NULL) return NN_OK;
if(conn->state != NULL) {
req.connection = conn;
req.action = NN_INTERNET_CLOSE;
nn_Exit e = handler(&req);
if(e) return e;
}
conn->state = NULL;
return NN_OK;
}
// Pushes a userdata that makes the connection, to save on brain power.
static nn_Exit nn_inetMakeConn(nn_InternetRequest req, nn_InternetHandler *handler, nn_InternetProtocol proto) {
if(req.action != NN_INTERNET_CONNECT) return NN_EBADSTATE;
nn_Context *ctx = req.ctx;
nn_InternetConnection *conn = NULL;
nn_Exit e = NN_OK;
conn = nn_alloc(ctx, sizeof(*conn));
if(conn == NULL) {
e = NN_ENOMEM;
goto fail;
}
conn->protocol = proto;
conn->port = req.connect.port;
conn->state = NULL;
nn_randomUUID(ctx, conn->id);
req.connection = conn;
e = handler(&req);
if(e) goto fail;
int idx = nn_allocUserdata(req.computer, conn, req.localAddress);
if(idx < 0) {
e = NN_ELIMIT;
goto fail;
}
e = nn_pushuserdata(req.computer, idx);
if(e) goto fail;
return NN_OK;
fail:
nn_inetCloseConn(req, handler, conn);
nn_free(ctx, conn, sizeof(*conn));
return e;
}
static nn_Exit nn_inetHandler(nn_ComponentRequest *req) {
nn_Context *ctx = req->ctx;
nn_InternetState *state = req->classState;
nn_Computer *C = req->computer;
nn_Exit e;
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
nn_InternetRequest ireq;
ireq.ctx = ctx;
ireq.computer = C;
ireq.state = req->state;
ireq.inet = &state->inet;
ireq.localAddress = req->compAddress;
ireq.connection = NULL;
if(req->action == NN_COMP_DROP) {
ireq.action = NN_INTERNET_DROP;
state->handler(&ireq);
nn_free(ctx, state, sizeof(*state));
return NN_OK;
}
if(req->action == NN_COMP_USERDATA) {
nn_UserdataRequest *ureq = req->user;
nn_InternetConnection *conn = ureq->state;
if(conn->state == NULL) return NN_EBADSTATE;
if(ureq->action == NN_USER_DROP) {
nn_inetCloseConn(ireq, state->handler, conn);
nn_free(ctx, conn, sizeof(*conn));
return NN_OK;
}
if(ureq->action == NN_USER_GETMETHOD) {
size_t idx = ureq->getmethod.idx;
nn_Method *method = ureq->getmethod.method;
if(idx == 0) {
method->name = "finishConnect";
method->doc = "function(): boolean - Returns whether the connection finished. Can also return an error if the connection failed, or no error if still pending";
method->flags = NN_DIRECT;
} else if(idx == 1) {
method->name = "id";
method->doc = "function(): string - Returns the id of the connection";
method->flags = NN_DIRECT;
} else if(idx == 2) {
method->name = "close";
method->doc = "function() - Closes the connection";
method->flags = NN_DIRECT;
} else if(idx == 3) {
method->name = "read";
method->doc = "function(n?: number): string? - Reads data from the connection. Returns the data, or nil if EoF";
method->flags = NN_DIRECT;
} else if(idx == 4) {
method->name = conn->protocol == NN_INET_HTTP ? NULL : "write";
method->doc = "function(data: string): integer - Writes data to the connection. Returns how much data was successfully written";
method->flags = NN_DIRECT;
} else {
ureq->getmethod.method = NULL;
}
return NN_OK;
}
if(ureq->action == NN_USER_INVOKE) {
const char *method = ureq->invoke.method;
if(nn_strcmp(method, "id") == 0) {
ureq->invoke.returnCount = 1;
return nn_pushlstring(C, conn->id, 36);
}
if(nn_strcmp(method, "close") == 0) {
e = nn_inetCloseConn(ireq, state->handler, conn);
if(e) return e;
ureq->invoke.returnCount = 1;
return nn_pushbool(C, true);
}
}
if(C) nn_setError(C, "internet: not implemented yet");
return NN_EBADCALL;
}
nn_InternetNum method = req->methodIdx;
if(method == NN_INETNUM_MAXREADSIZE) {
req->returnCount = 1;
return nn_pushinteger(C, state->inet.maxReadSize);
}
if(method == NN_INETNUM_ISHTTP) {
req->returnCount = 1;
return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_HTTP) != 0);
}
if(method == NN_INETNUM_ISTCP) {
req->returnCount = 1;
return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_TCP) != 0);
}
if(method == NN_INETNUM_ISTLS) {
req->returnCount = 1;
return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_TLS) != 0);
}
if(method == NN_INETNUM_ISUDP) {
req->returnCount = 1;
return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_UDP) != 0);
}
if(method == NN_INETNUM_ISWEBSOCK) {
req->returnCount = 1;
return nn_pushbool(C, (state->inet.protocolsSupported & NN_INET_WEBSOCKET) != 0);
}
if(method == NN_INETNUM_REQUEST) {
if((state->inet.protocolsSupported & NN_INET_HTTP) == 0) {
nn_setError(C, "protocol disabled");
return NN_EBADCALL;
}
if(nn_checkstring(C, 0, "bad argument #1 (string expected")) return NN_EBADCALL;
e = nn_defaultstring(C, 1, "");
if(e) return e;
if(nn_checkstring(C, 1, "bad argument #2 (string expected")) return NN_EBADCALL;
e = nn_defaulttable(C, 2);
if(e) return e;
if(nn_checktable(C, 2, "bad argument #3 (table expected)")) return NN_EBADCALL;
ireq.action = NN_INTERNET_CONNECT;
ireq.connect.url = nn_tostring(C, 0);
ireq.connect.http.postdata = nn_tolstring(C, 1, &ireq.connect.http.postdatalen);
size_t headerlen = 0;
e = nn_dumptable(C, 2, &headerlen);
if(e) return e;
ireq.connect.http.headerlen = headerlen;
nn_HTTPHeader *headers = nn_alloc(ctx, sizeof(nn_HTTPHeader) * headerlen);
if(headers == NULL) return NN_ENOMEM;
for(size_t i = 3; i < 3 + headerlen * 2; i += 2) {
if(!nn_isstring(C, i) || !nn_isstring(C, i+1)) {
nn_free(ctx, headers, sizeof(nn_HTTPHeader) * headerlen);
nn_setError(C, "malformed headers");
return NN_EBADCALL;
}
headers[i].name = nn_tostring(C, i);
headers[i].value = nn_tostring(C, i+1);
}
req->returnCount = 1;
e = nn_inetMakeConn(ireq, state->handler, NN_INET_HTTP);
nn_free(ctx, headers, sizeof(nn_HTTPHeader) * headerlen);
return e;
}
if(C) nn_setError(C, "internet: not implemented yet");
return NN_EBADCALL;
}
nn_Component *nn_createInternet(nn_Universe *universe, const char *address, const nn_InternetCard *inet, void *state, nn_InternetHandler *handler) {
nn_Component *c = nn_createComponent(
universe, address, "internet");
if(c == NULL) return NULL;
const nn_Method methods[NN_INETNUM_COUNT] = {
[NN_INETNUM_MAXREADSIZE] = {"maxReadSize", "function(): integer - Returns the maximum size of a read", NN_DIRECT},
[NN_INETNUM_ISHTTP] = {"isHttpEnabled", "function(): boolean - Returns whether HTTP/HTTPS support is enabled", NN_DIRECT},
[NN_INETNUM_ISTCP] = {"isTcpEnabled", "function(): boolean - Returns whether TCP support is enabled", NN_DIRECT},
[NN_INETNUM_ISTLS] = {"isTlsEnabled", "function(): boolean - Returns whether TLS support is enabled", NN_DIRECT},
[NN_INETNUM_ISWEBSOCK] = {"isWebsocketEnabled", "function(): boolean - Returns whether WebSocket / WSS support is enabled", NN_DIRECT},
[NN_INETNUM_ISUDP] = {"isUdpEnabled", "function(): boolean - Returns whether UDP support is enabled", NN_DIRECT},
[NN_INETNUM_CONNECT] = {"connect", "function(address: string, port: number = -1, protocol: string = 'tcp'): userdata - Opens a socket connection and returns a handle to it. Valid protocols are tcp and tls", NN_INDIRECT},
[NN_INETNUM_CHANNEL] = {"channel", "function(address: string, port: number = -1, protocol: string = 'websocket', subprotocols?: string[]): userdata - Opens a packet channel and reutrns a handle to it. Valid protocols are websocket and udp", NN_INDIRECT},
[NN_INETNUM_REQUEST] = {"request", "function(url: string, portData?: string, headers?: table): userdata - Sends an HTTP/HTTPS requests, returns a handle to it", NN_INDIRECT},
};
nn_Exit e = nn_setComponentMethodsArray(
c, methods, NN_INETNUM_COUNT);
if(e) { nn_dropComponent(c); return NULL; }
nn_Context *ctx = &universe->ctx;
nn_InternetState *cls = nn_alloc(ctx, sizeof(*cls));
if(cls == NULL) {
nn_dropComponent(c);
return NULL;
}
cls->ctx = ctx;
cls->inet = *inet;
cls->handler = handler;
nn_setComponentState(c, state);
nn_setComponentClassState(c, cls);
nn_setComponentHandler(c, nn_inetHandler);
return c;
}

View File

@@ -393,17 +393,36 @@ typedef struct nn_Architecture {
extern size_t nn_ramSizes[8]; extern size_t nn_ramSizes[8];
typedef struct nn_Beep { typedef struct nn_Beep {
// frequenc, in Hz
double frequency; double frequency;
// duration, in seconds
double duration; double duration;
// 0 is mute, 1 is 100%
double volume; double volume;
} nn_Beep; } nn_Beep;
// Morse beep, like a normal beep except it follows a morse code pattern.
// . is a short sound
// - is a long sound, 2x in length
// a space is a pause, 2x in length and dead silent.
// Every sound, including actual pauses, have a 50ms pause between them.
typedef struct nn_MorseBeep {
const char *pattern;
// frequency, in Hz
double frequency;
// duration of a ., in seconds
double beepDuration;
// 0 is mute, 1 is 100%
double volume;
} nn_MorseBeep;
typedef enum nn_EnvironmentAction { typedef enum nn_EnvironmentAction {
NN_ENV_DRAWENERGY, NN_ENV_DRAWENERGY,
NN_ENV_POWERON, NN_ENV_POWERON,
NN_ENV_POWEROFF, NN_ENV_POWEROFF,
NN_ENV_CRASHED, NN_ENV_CRASHED,
NN_ENV_BEEP, NN_ENV_BEEP,
NN_ENV_BEEPMORSE,
} nn_EnvironmentAction; } nn_EnvironmentAction;
typedef struct nn_EnvironmentRequest { typedef struct nn_EnvironmentRequest {
@@ -415,6 +434,8 @@ typedef struct nn_EnvironmentRequest {
double energy; double energy;
// for BEEP, information about the beep // for BEEP, information about the beep
nn_Beep beep; nn_Beep beep;
// for BEEPMORSE, information about the beep
nn_MorseBeep morseBeep;
}; };
} nn_EnvironmentRequest; } nn_EnvironmentRequest;
@@ -435,8 +456,11 @@ typedef struct nn_Environment {
// maxComponents and maxDevices determine how many components can be connected simultaneously to this computer, and how much device info can be // maxComponents and maxDevices determine how many components can be connected simultaneously to this computer, and how much device info can be
// registered on this computer. // registered on this computer.
nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices); nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices);
void nn_retainComputer(nn_Computer *computer);
void nn_retainComputerN(nn_Computer *computer, size_t n);
// Destroys the state, effectively shutting down the computer. // Destroys the state, effectively shutting down the computer.
void nn_destroyComputer(nn_Computer *computer); void nn_destroyComputer(nn_Computer *computer);
void nn_destroyComputerN(nn_Computer *computer, size_t n);
void nn_lockComputer(nn_Computer *computer); void nn_lockComputer(nn_Computer *computer);
void nn_unlockComputer(nn_Computer *computer); void nn_unlockComputer(nn_Computer *computer);
// stops the computer if an architecture state is already present, // stops the computer if an architecture state is already present,
@@ -517,6 +541,7 @@ void nn_clearCommonDeviceInfo(nn_CommonDeviceInfo *info);
bool nn_removeDeviceInfo(nn_Computer *computer, const char *addr); bool nn_removeDeviceInfo(nn_Computer *computer, const char *addr);
void nn_beepComputer(nn_Computer *computer, nn_Beep beep); void nn_beepComputer(nn_Computer *computer, nn_Beep beep);
void nn_beepComputerMorse(nn_Computer *computer, nn_MorseBeep beep);
// get the userdata pointer // get the userdata pointer
void *nn_getComputerUserdata(nn_Computer *computer); void *nn_getComputerUserdata(nn_Computer *computer);
@@ -2329,12 +2354,94 @@ typedef enum nn_InternetProtocol {
typedef struct nn_InternetCard { typedef struct nn_InternetCard {
// bitwise OR multiple of them // bitwise OR multiple of them
unsigned char protocolsSupported; unsigned char protocolsSupported;
unsigned int maxReadSize;
// per-byte cost of a write // per-byte cost of a write
double transmissionEnergyCost; double transmissionEnergyCost;
} nn_InternetCard; } nn_InternetCard;
extern nn_InternetCard nn_defaultInternetCard; extern nn_InternetCard nn_defaultInternetCard;
typedef struct nn_InternetConnection {
nn_InternetProtocol protocol;
int port;
void *state;
nn_uuid id;
} nn_InternetConnection;
typedef struct nn_HTTPHeader {
const char *name;
const char *value;
} nn_HTTPHeader;
typedef enum nn_InternetAction {
NN_INTERNET_DROP,
// start a connection, remember to check connection->protocol to know which type of connection to establish
NN_INTERNET_CONNECT,
// Ensure a connection is established, error if not.
// It is assumed this operation will return whether it is currently connected,
// and should be called repeatedly until it works
NN_INTERNET_FINISHCONNECT,
// close a connection
NN_INTERNET_CLOSE,
// read from a connection. Set read.buf to NULL to mark EoF. read.len is the capacity, but also set how much was read in it.
NN_INTERNET_READ,
// write to a connection. HTTP connections do not have this method
NN_INTERNET_WRITE,
// get an HTTP response. Should push the response code, message (ie. OK for 200) and table
NN_INTERNET_RESPONSE,
} nn_INternetAction;
typedef struct nn_InternetRequest {
nn_Context *ctx;
nn_Computer *computer;
void *state;
const nn_InternetCard *inet;
const char *localAddress;
nn_InternetConnection *connection;
nn_INternetAction action;
union {
// does a socket connection, for any of the supported protocols
struct {
// URL of connection
const char *url;
// port. Useless for HTTP connections, as they should use 80 for HTTP and 443 for HTTPS
unsigned short port;
union {
struct {
// HTTP specific
size_t postdatalen;
const char *postdata;
const nn_HTTPHeader *headers;
size_t headerlen;
} http;
struct {
const char * const * subprotocols;
size_t subprotocolcount;
} websockets;
};
} connect;
struct {
const char *buf;
size_t len;
} write;
struct {
char *buf;
size_t len;
} read;
bool isConnected;
};
} nn_InternetRequest;
typedef nn_Exit (nn_InternetHandler)(nn_InternetRequest *req);
nn_Component *nn_createInternet(nn_Universe *universe, const char *address, const nn_InternetCard *inet, void *state, nn_InternetHandler *handler);
// Colors and palettes. // Colors and palettes.
// Do note that the // Do note that the