diff --git a/Makefile b/Makefile index 57e9582..f223356 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ CC=cc LD=$(CC) AR=ar RANLIB=ranlib +WARN=-Wall -Werror -Wno-format-truncation ifeq ($(MODE), release) OPT=-Oz @@ -20,6 +21,9 @@ SANITIZE=undefined,address DEBUG=-ggdb endif +NN_STD=gnu99 +EMU_STD=c23 + NNFLAGS= SANITIZE_FLAGS= @@ -28,7 +32,7 @@ ifdef SANITIZE SANITIZE_FLAGS += -fsanitize=$(SANITIZE) endif -CFLAGS=-fPIC $(OPT) $(SANITIZE_FLAGS) $(DEBUG) $(NNFLAGS) +CFLAGS=-fPIC $(OPT) $(SANITIZE_FLAGS) $(DEBUG) $(NNFLAGS) $(WARN) LDFLAGS=$(OPT) $(DEBUG) $(SANITIZE_FLAGS) @@ -44,21 +48,21 @@ SRC_DIR=src all: bin lib dynlib $(BUILD_DIR)/neonucleus.o: $(SRC_DIR)/neonucleus.c $(SRC_DIR)/neonucleus.h - $(CC) -o $(BUILD_DIR)/neonucleus.o -c $(SRC_DIR)/neonucleus.c $(CFLAGS) + $(CC) -o $(BUILD_DIR)/neonucleus.o -c $(SRC_DIR)/neonucleus.c $(CFLAGS) -std=$(NN_STD) $(BUILD_DIR)/ncomplib.o: $(SRC_DIR)/ncomplib.c $(SRC_DIR)/ncomplib.h - $(CC) -o $(BUILD_DIR)/ncomplib.o -c $(SRC_DIR)/ncomplib.c $(CFLAGS) + $(CC) -o $(BUILD_DIR)/ncomplib.o -c $(SRC_DIR)/ncomplib.c $(CFLAGS) -std=$(NN_STD) nn: $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o $(BUILD_DIR)/luaarch.o: $(SRC_DIR)/luaarch.c $(SRC_DIR)/machine.lua - $(CC) -o $(BUILD_DIR)/luaarch.o -c $(SRC_DIR)/luaarch.c $(CFLAGS) $(INCLUA) + $(CC) -o $(BUILD_DIR)/luaarch.o -c $(SRC_DIR)/luaarch.c $(CFLAGS) $(INCLUA) -std=$(EMU_STD) $(BUILD_DIR)/glyphcache.o: $(SRC_DIR)/glyphcache.c $(SRC_DIR)/glyphcache.h - $(CC) -o $(BUILD_DIR)/glyphcache.o -c $(SRC_DIR)/glyphcache.c $(CFLAGS) + $(CC) -o $(BUILD_DIR)/glyphcache.o -c $(SRC_DIR)/glyphcache.c $(CFLAGS) -std=$(EMU_STD) $(BUILD_DIR)/main.o: $(SRC_DIR)/main.c $(SRC_DIR)/minBIOS.lua - $(CC) -o $(BUILD_DIR)/main.o -c $(SRC_DIR)/main.c $(CFLAGS) $(INCLUA) + $(CC) -o $(BUILD_DIR)/main.o -c $(SRC_DIR)/main.c $(CFLAGS) $(INCLUA) -std=$(EMU_STD) bin: nn $(BUILD_DIR)/main.o $(BUILD_DIR)/luaarch.o $(BUILD_DIR)/glyphcache.o $(LD) $(LDFLAGS) -o $(BIN) $(BUILD_DIR)/neonucleus.o $(BUILD_DIR)/ncomplib.o $(BUILD_DIR)/main.o $(BUILD_DIR)/glyphcache.o $(BUILD_DIR)/luaarch.o $(LINKLIBC) $(LINKLIBM) $(LINKRAYLIB) $(LINKLUA) diff --git a/src/luaarch.c b/src/luaarch.c index f8d7498..9c836c1 100644 --- a/src/luaarch.c +++ b/src/luaarch.c @@ -751,7 +751,7 @@ static void luaArch_loadEnv(lua_State *L) { lua_setfield(L, unicode, "len"); lua_pushcfunction(L, luaArch_unicode_sub); lua_setfield(L, unicode, "sub"); - lua_pushcfunction(L, luaArch_unicode_len); + lua_pushcfunction(L, luaArch_unicode_wlen); lua_setfield(L, unicode, "wlen"); lua_pushcfunction(L, luaArch_unicode_wtrunc); lua_setfield(L, unicode, "wtrunc"); diff --git a/src/machine.lua b/src/machine.lua index e732893..8b1321c 100644 --- a/src/machine.lua +++ b/src/machine.lua @@ -70,6 +70,24 @@ end local syncedMethodStats +local function unsandboxValue(val) + if type(val) == "table" then + local meta = getmetatable(val) + if meta then + if type(meta.__userdata) == "userdata" then + return meta.__userdata + end + end + local nt = {} + for sk, sv in pairs(val) do + local k, v = unsandboxValue(sk), unsandboxValue(sv) + if k ~= nil then nt[k] = v end + end + return nt + end + return val +end + local function sandboxValue(val) if type(val) == "table" then local nt = {} @@ -87,6 +105,7 @@ local function sandboxValue(val) __gc = function() userdata.free(val) end, + __userdata = val, } local wrapped = {type = "userdata", userdata = val} @@ -108,7 +127,9 @@ local function sandboxValue(val) end local function realInvoke(address, method, ...) - local t = {pcall(cinvoke, address, method, ...)} + local args = {...} + for i=1,#args do args[i] = unsandboxValue(args[i]) end + local t = {pcall(cinvoke, address, method, table.unpack(args))} if not _SYNCED and not os.getenv("NN_FAST") then if computer.energy() <= 0 then sysyield() end -- out of power if computer.isOverused() then sysyield() end -- overused @@ -231,6 +252,7 @@ function computer.pullSignal(timeout) while true do if computer.uptime() >= deadline then return end local t = {computer.popSignal()} + for i=1,#t do t[i] = unsandboxValue(t[i]) end if #t == 0 then sysyield() else @@ -465,7 +487,18 @@ sandbox = { isRobot = computer.isRobot, maxEnergy = computer.maxEnergy, pullSignal = computer.pullSignal, - pushSignal = computer.pushSignal, + pushSignal = function(name, ...) + checkArg(1, name, "string") + local t = {...} + for i=1,#t do + local v = unsandboxValue(t[i]) + if type(v) == "userdata" then + return nil, "userdata is forbidden" + end + t[i] = v + end + return computer.pushSignal(name, table.unpack(t)) + end, removeUser = computer.removeUser, setArchitecture = computer.setArchitecture, shutdown = computer.shutdown, @@ -516,9 +549,6 @@ sandbox = { }, utf8 = utf8, - - -- unsafe, don't care - userdata = userdata, } sandbox._G = sandbox diff --git a/src/main.c b/src/main.c index 022eb82..3ac50a1 100644 --- a/src/main.c +++ b/src/main.c @@ -191,8 +191,9 @@ static nn_Exit ne_modemBullshit(nn_ModemRequest *req) { if(req->action == NN_MODEM_SEND) { req->send.strengthSent = req->modem->maxRange; - printf("Transmission from %s to %s (port %zu) of %zu bytes (%zu values)\n", req->localAddress, req->send.address == NULL ? "*" : req->send.address, req->send.port, req->send.contents->buflen, req->send.contents->valueCount); - return nn_pushModemMessage(C, req->localAddress, nn_getComputerAddress(C), req->send.port, 0, req->send.contents); + const char *dest = req->send.address == NULL ? "*" : req->send.address; + printf("Transmission from %s to %s (port %zu) of %zu bytes (%zu values)\n", req->localAddress, dest, req->send.port, req->send.contents->buflen, req->send.contents->valueCount); + return nn_pushModemMessage(C, req->localAddress, dest, req->send.port, 0, req->send.contents); } if(req->action == NN_MODEM_GETWAKEMESSAGE) { @@ -209,6 +210,36 @@ static nn_Exit ne_modemBullshit(nn_ModemRequest *req) { return NN_EBADCALL; } +static nn_Exit ne_tunnelBullshit(nn_TunnelRequest *req) { + nn_Computer *C = req->computer; + + if(req->action == NN_TUNNEL_DROP) { + return NN_OK; + } + + if(req->action == NN_TUNNEL_GETCHANNEL) { + return nn_pushstring(C, "creative"); + } + + if(req->action == NN_TUNNEL_SEND) { + printf("Transmission from tunnel %s of %zu bytes (%zu values)\n", req->localAddress, req->toSend->buflen, req->toSend->valueCount); + return nn_pushModemMessage(C, req->localAddress, nn_getComputerAddress(C), NN_TUNNEL_PORT, 0, req->toSend); + } + + if(req->action == NN_TUNNEL_GETWAKEMESSAGE) { + req->getWake.len = 0; + req->getWake.isFuzzy = false; + return NN_OK; + } + + if(req->action == NN_TUNNEL_SETWAKEMESSAGE) { + return NN_OK; + } + + if(C) nn_setError(C, "ne: modem method not implemented"); + return NN_EBADCALL; +} + static unsigned char ne_processColorPart(unsigned char channel, double brightness) { double n = (double)channel / 255; n *= brightness; @@ -556,6 +587,7 @@ int main(int argc, char **argv) { 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 *tunnel = nn_createTunnel(u, NULL, &nn_defaultTunnel, NULL, ne_tunnelBullshit); char *eepromCode = (char *)minBIOS; size_t eepromSize = strlen(minBIOS); @@ -709,6 +741,7 @@ int main(int argc, char **argv) { nn_mountComponent(c, testFlash, 5, false); nn_mountComponent(c, dataCard, 6, false); nn_mountComponent(c, modem, 7, false); + nn_mountComponent(c, tunnel, 8, false); int ltx = 0, lty = 0; double scrollBuf = 0; double tickTime = 0; @@ -887,7 +920,6 @@ int main(int argc, char **argv) { if(noIdle) nn_resetIdleTime(c); // OC computers consume 0.5W when running, 0.05W when running but idle double normalPowerUsage = 0.5, idlePowerUsage = 0.05; - bool isIdle = false; nn_Exit e = nn_tick(c); tickTime = GetTime() - tickNow; if(tickTime < tickDelay) { @@ -947,6 +979,7 @@ cleanup:; nn_dropComponent(keyboard); nn_dropComponent(dataCard); nn_dropComponent(modem); + nn_dropComponent(tunnel); // rip the universe nn_destroyUniverse(u); ncl_destroyGlyphCache(gc); diff --git a/src/ncomplib.c b/src/ncomplib.c index 404d2cb..ff6de16 100644 --- a/src/ncomplib.c +++ b/src/ncomplib.c @@ -518,7 +518,7 @@ typedef struct ncl_ScreenPixel { int realBg; } ncl_ScreenPixel; -typedef struct ncl_ScreenState { +struct ncl_ScreenState { nn_Context *ctx; nn_Lock *lock; nn_ScreenConfig conf; @@ -534,7 +534,7 @@ typedef struct ncl_ScreenState { size_t keyboardCount; double brightness; char *keyboards[NCL_MAX_KEYBOARD]; -} ncl_ScreenState; +}; typedef struct nn_VRAMBuf { int width; diff --git a/src/neonucleus.c b/src/neonucleus.c index 0224986..2d8e67d 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -70,10 +70,9 @@ bool nn_decRef(nn_refc_t *refc, size_t n) { } #endif -typedef struct nn_Lock nn_Lock; - // the special includes #ifndef NN_BAREMETAL + #include #include #include @@ -91,6 +90,7 @@ typedef struct nn_Lock nn_Lock; #endif #ifdef NN_POSIX +#include #include #include #endif @@ -894,7 +894,7 @@ typedef struct nn_MethodEntry { unsigned int idx; } nn_MethodEntry; -typedef struct nn_Component { +struct nn_Component { nn_refc_t refc; nn_Universe *universe; char *address; @@ -906,7 +906,7 @@ typedef struct nn_Component { nn_Arena methodArena; nn_HashMap methodsMap; size_t methodCount; -} nn_Component; +}; static size_t nn_methodHash(nn_HashAction act, void *_slot, void *_ent) { nn_MethodEntry *slot = _slot; @@ -937,14 +937,14 @@ static const nn_HashContext nn_methodHasher = { .handler = (nn_HashHandler *)nn_methodHash, }; -typedef struct nn_Universe { +struct nn_Universe { nn_Context ctx; void *userdata; // 0 for unbounded size_t memoryLimit; // 0 for unbounded size_t storageLimit; -} nn_Universe; +}; typedef struct nn_ComponentEntry { const char *address; @@ -1069,7 +1069,7 @@ typedef struct nn_Userdata { char *compAddress; } nn_Userdata; -typedef struct nn_Computer { +struct nn_Computer { nn_ComputerState state; nn_Universe *universe; nn_Environment env; @@ -1098,7 +1098,7 @@ typedef struct nn_Computer { nn_Signal signals[NN_MAX_SIGNALS]; char *users[NN_MAX_USERS]; nn_Userdata uservals[NN_MAX_USERDATA]; -} nn_Computer; +}; nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata) { nn_Universe *u = nn_alloc(ctx, sizeof(nn_Universe)); @@ -1240,13 +1240,6 @@ void nn_unlockComputer(nn_Computer *computer) { static void nn_dropValue(nn_Value val); -static nn_ComponentEntry *nn_getInternalComponent(nn_Computer *computer, const char *address) { - nn_ComponentEntry lookingFor = { - .address = address, - }; - return nn_hashGet(&computer->components, &lookingFor); -} - nn_Exit nn_startComputer(nn_Computer *computer) { if(nn_isComputerOn(computer)) { nn_stopComputer(computer); @@ -7052,7 +7045,7 @@ static nn_Exit nn_modemHandler(nn_ComponentRequest *req) { nn_setError(C, "invalid port"); return NN_EBADCALL; } - size_t valcount = nn_getstacksize(C) - 1; + size_t valcount = nn_getstacksize(C) - 2; if(valcount > state->modem.maxValues) return NN_EBADCALL; int cost = nn_countValueCost(C, valcount); if(cost < 0) { @@ -7201,3 +7194,156 @@ nn_Tunnel nn_defaultTunnel = { .basePacketCost = 100, .fullPacketCost = 256, }; + +typedef enum nn_TunnelNum { + NN_TUNNELNUM_MAXPACKETSIZE, + NN_TUNNELNUM_MAXVALUES, + + NN_TUNNELNUM_GETCHANNEL, + NN_TUNNELNUM_SEND, + + NN_TUNNELNUM_GETWAKE, + NN_TUNNELNUM_SETWAKE, + + NN_TUNNELNUM_COUNT, +} nn_TunnelNum; + +typedef struct nn_TunnelState { + nn_Context *ctx; + nn_Tunnel tunnel; + nn_TunnelHandler *handler; +} nn_TunnelState; + +static nn_Exit nn_tunnelHandler(nn_ComponentRequest *req) { + if(req->action == NN_COMP_USERDATA) return NN_OK; + nn_Context *ctx = req->ctx; + nn_TunnelState *state = req->classState; + nn_Computer *C = req->computer; + + nn_TunnelNum method = req->methodIdx; + + if(req->action == NN_COMP_CHECKMETHOD) return NN_OK; + + nn_TunnelRequest treq; + treq.ctx = ctx; + treq.computer = C; + treq.state = req->state; + treq.tunnel = &state->tunnel; + treq.localAddress = req->compAddress; + nn_Exit e; + + if(req->action == NN_COMP_DROP) { + treq.action = NN_TUNNEL_DROP; + state->handler(&treq); + nn_free(ctx, state, sizeof(*state)); + return NN_OK; + } + + if(method == NN_TUNNELNUM_MAXPACKETSIZE) { + req->returnCount = 1; + return nn_pushinteger(C, state->tunnel.maxPacketSize); + } + if(method == NN_TUNNELNUM_MAXVALUES) { + req->returnCount = 1; + return nn_pushinteger(C, state->tunnel.maxValues); + } + + if(method == NN_TUNNELNUM_GETCHANNEL) { + treq.action = NN_TUNNEL_GETCHANNEL; + req->returnCount = 1; + return state->handler(&treq); + } + if(method == NN_TUNNELNUM_SEND) { + size_t valcount = nn_getstacksize(C); + if(valcount > state->tunnel.maxValues) return NN_EBADCALL; + int cost = nn_countValueCost(C, valcount); + if(cost < 0) { + nn_setError(C, "invalid contents"); + return NN_EBADCALL; + } + if(cost > state->tunnel.maxPacketSize) return NN_ELIMIT; + nn_EncodedNetworkContents data; + e = nn_encodeNetworkContents(C, &data, valcount); + if(e) return e; + treq.action = NN_TUNNEL_SEND; + treq.toSend = &data; + e = state->handler(&treq); + nn_dropNetworkContents(&data); + if(!e) { + nn_removeEnergy(C, state->tunnel.basePacketCost + state->tunnel.fullPacketCost * cost / state->tunnel.maxPacketSize); + req->returnCount = 1; + e = nn_pushbool(C, true); + } + return e; + } + + if(method == NN_TUNNELNUM_GETWAKE) { + char buf[NN_MAX_WAKEUPMSG]; + treq.action = NN_TUNNEL_GETWAKEMESSAGE; + treq.getWake.buf = buf; + treq.getWake.len = NN_MAX_WAKEUPMSG; + e = state->handler(&treq); + if(e) return e; + + req->returnCount = 2; + e = treq.getWake.len == 0 ? nn_pushnull(C) : nn_pushlstring(C, buf, treq.getWake.len); + if(e) return e; + return nn_pushbool(C, treq.getWake.isFuzzy); + } + + if(method == NN_TUNNELNUM_SETWAKE) { + e = nn_defaultstring(C, 0, ""); + if(e) return e; + if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL; + e = nn_defaultboolean(C, 1, false); + if(e) return e; + if(nn_checkboolean(C, 1, "bad argument #2 (boolean expected)")) return NN_EBADCALL; + treq.action = NN_TUNNEL_SETWAKEMESSAGE; + treq.setWake.buf = nn_tolstring(C, 0, &treq.setWake.len); + if(treq.setWake.len > NN_MAX_WAKEUPMSG) return NN_ELIMIT; + treq.setWake.isFuzzy = nn_toboolean(C, 1); + e = state->handler(&treq); + if(e) return e; + + req->returnCount = 1; + return nn_pushbool(C, true); + } + + if(C) nn_setError(C, "tunnel: not implemented yet"); + return NN_EBADCALL; +} + +nn_Component *nn_createTunnel(nn_Universe *universe, const char *address, const nn_Tunnel *tunnel, void *state, nn_TunnelHandler *handler) { + nn_Component *c = nn_createComponent( + universe, address, "tunnel"); + if(c == NULL) return NULL; + + const nn_Method methods[NN_TUNNELNUM_COUNT] = { + [NN_TUNNELNUM_MAXPACKETSIZE] = {"maxPacketSize", "function(): integer - Returns the maximum logical packet size", NN_DIRECT}, + [NN_TUNNELNUM_MAXVALUES] = {"maxValues", "function(): integer - Returns the maximum amount of values", NN_DIRECT}, + + [NN_TUNNELNUM_GETCHANNEL] = {"getChannel", "function(): string - Get the ID of the channel", NN_DIRECT}, + [NN_TUNNELNUM_SEND] = {"send", "function(...): boolean - Send a packet", NN_INDIRECT}, + + [NN_TUNNELNUM_GETWAKE] = {"getWakeMessage", "function(): string?, boolean - Returns the wake message, if any, and whether it is fuzzy", NN_DIRECT}, + [NN_TUNNELNUM_SETWAKE] = {"setWakeMessage", "function(message: string?, fuzzy: boolean) - Changes the wake-up message of the modem", NN_INDIRECT}, + }; + + nn_Exit e = nn_setComponentMethodsArray( + c, methods, NN_TUNNELNUM_COUNT); + if(e) { nn_dropComponent(c); return NULL; } + + nn_Context *ctx = &universe->ctx; + nn_TunnelState *cls = nn_alloc(ctx, sizeof(*cls)); + if(cls == NULL) { + nn_dropComponent(c); + return NULL; + } + cls->ctx = ctx; + cls->tunnel = *tunnel; + cls->handler = handler; + nn_setComponentState(c, state); + nn_setComponentClassState(c, cls); + nn_setComponentHandler(c, nn_tunnelHandler); + return c; +} diff --git a/src/neonucleus.h b/src/neonucleus.h index 2066772..3f21604 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -108,9 +108,9 @@ void *_alloca(size_t); // maximum size of an address. // This only matters in places where an address is returned through a component, as it is the amount of space to allocate for the response. // Past this there would be a truncation which would invalidate the address. -// However, 256 is unrealistically long, as UUIDv4 only needs 36. +// However, 64 is unrealistically long, as UUIDv4 only needs 36. // Please, do not go above this. -#define NN_MAX_ADDRESS 256 +#define NN_MAX_ADDRESS 64 // the port used by tunnel cards. This port is invalid for modems. #define NN_TUNNEL_PORT 0 // maximum amount of users a computer can have @@ -1042,6 +1042,258 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount); nn_Exit nn_transferErrorFrom(nn_Exit exit, nn_Computer *from, nn_Computer *to); +// Signal helpers + +// common mouse buttons, not an exhaustive list +#define NN_BUTTON_LEFT 0 +#define NN_BUTTON_RIGHT 1 +#define NN_BUTTON_MIDDLE 2 + +// OC keycodes +// taken from https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua +#define NN_KEY_UNKNOWN 0 +#define NN_KEY_1 0x02 +#define NN_KEY_2 0x03 +#define NN_KEY_3 0x04 +#define NN_KEY_4 0x05 +#define NN_KEY_5 0x06 +#define NN_KEY_6 0x07 +#define NN_KEY_7 0x08 +#define NN_KEY_8 0x09 +#define NN_KEY_9 0x0A +#define NN_KEY_0 0x0B +#define NN_KEY_A 0x1E +#define NN_KEY_B 0x30 +#define NN_KEY_C 0x2E +#define NN_KEY_D 0x20 +#define NN_KEY_E 0x12 +#define NN_KEY_F 0x21 +#define NN_KEY_G 0x22 +#define NN_KEY_H 0x23 +#define NN_KEY_I 0x17 +#define NN_KEY_J 0x24 +#define NN_KEY_K 0x25 +#define NN_KEY_L 0x26 +#define NN_KEY_M 0x32 +#define NN_KEY_N 0x31 +#define NN_KEY_O 0x18 +#define NN_KEY_P 0x19 +#define NN_KEY_Q 0x10 +#define NN_KEY_R 0x13 +#define NN_KEY_S 0x1F +#define NN_KEY_T 0x14 +#define NN_KEY_U 0x16 +#define NN_KEY_V 0x2F +#define NN_KEY_W 0x11 +#define NN_KEY_X 0x2D +#define NN_KEY_Y 0x15 +#define NN_KEY_Z 0x2C + +#define NN_KEY_APOSTROPHE 0x28 +#define NN_KEY_AT 0x91 +#define NN_KEY_BACK 0x0E +#define NN_KEY_BACKSLASH 0x2B +// caps-lock +#define NN_KEY_CAPITAL 0x3A +#define NN_KEY_COLON 0x92 +#define NN_KEY_COMMA 0x33 +#define NN_KEY_ENTER 0x1C +#define NN_KEY_EQUALS 0x0D +// accent grave +#define NN_KEY_GRAVE 0x29 +#define NN_KEY_LBRACKET 0x1A +#define NN_KEY_LCONTROL 0x1D +// left alt +#define NN_KEY_LMENU 0x38 +#define NN_KEY_LSHIFT 0x2A +#define NN_KEY_MINUS 0x0C +#define NN_KEY_NUMLOCK 0x45 +#define NN_KEY_PAUSE 0xC5 +#define NN_KEY_PERIOD 0x34 +#define NN_KEY_RBRACKET 0x1B +#define NN_KEY_RCONTROL 0x9D +// right alt +#define NN_KEY_RMENU 0xB8 +#define NN_KEY_RSHIFT 0x36 +// scroll lock +#define NN_KEY_SCROLL 0x46 +#define NN_KEY_SEMICOLON 0x27 +#define NN_KEY_SLASH 0x35 +#define NN_KEY_SPACE 0x39 +#define NN_KEY_STOP 0x95 +#define NN_KEY_TAB 0x0F +#define NN_KEY_UNDERLINE 0x93 + +#define NN_KEY_UP 0xC8 +#define NN_KEY_DOWN 0xD0 +#define NN_KEY_LEFT 0xCB +#define NN_KEY_RIGHT 0xCD +#define NN_KEY_HOME 0xC7 +#define NN_KEY_END 0xCF +#define NN_KEY_PAGEUP 0xC9 +#define NN_KEY_PAGEDOWN 0xD1 +#define NN_KEY_INSERT 0xD2 +#define NN_KEY_DELETE 0xD3 + +#define NN_KEY_F1 0x3B +#define NN_KEY_F2 0x3C +#define NN_KEY_F3 0x3D +#define NN_KEY_F4 0x3E +#define NN_KEY_F5 0x3F +#define NN_KEY_F6 0x40 +#define NN_KEY_F7 0x41 +#define NN_KEY_F8 0x42 +#define NN_KEY_F9 0x43 +#define NN_KEY_F10 0x44 +#define NN_KEY_F11 0x57 +#define NN_KEY_F12 0x58 +#define NN_KEY_F13 0x64 +#define NN_KEY_F14 0x65 +#define NN_KEY_F15 0x66 +#define NN_KEY_F16 0x67 +#define NN_KEY_F17 0x68 +#define NN_KEY_F18 0x69 +#define NN_KEY_F19 0x71 + +#define NN_KEY_KANA 0x70 +#define NN_KEY_KANJI 0x94 +#define NN_KEY_CONVERT 0x79 +#define NN_KEY_NOCONVERT 0x7B +#define NN_KEY_YEN 0x7D +#define NN_KEY_CIRCUMFLEX 0x90 +#define NN_KEY_AX 0x96 + +#define NN_KEY_NUMPAD0 0x52 +#define NN_KEY_NUMPAD1 0x4F +#define NN_KEY_NUMPAD2 0x50 +#define NN_KEY_NUMPAD3 0x51 +#define NN_KEY_NUMPAD4 0x4B +#define NN_KEY_NUMPAD5 0x4C +#define NN_KEY_NUMPAD6 0x4D +#define NN_KEY_NUMPAD7 0x47 +#define NN_KEY_NUMPAD8 0x48 +#define NN_KEY_NUMPAD9 0x49 +#define NN_KEY_NUMPADMUL 0x37 +#define NN_KEY_NUMPADDIV 0xB5 +#define NN_KEY_NUMPADSUB 0x4A +#define NN_KEY_NUMPADADD 0x4E +#define NN_KEY_NUMPADDECIMAL 0x53 +#define NN_KEY_NUMPADCOMMA 0xB3 +#define NN_KEY_NUMPADENTER 0x9C +#define NN_KEY_NUMPADEQUALS 0x8D + +// the bottom side, can also be downwards / negative y +#define NN_SIDE_BOTTOM 0 +// the top side, can also be upwards / positive y +#define NN_SIDE_TOP 1 +// backwards, can also be north / negative z +#define NN_SIDE_BACK 2 +// forwards, can also be south / positive z +#define NN_SIDE_FRONT 3 +// to the right, can also be west / negative x +#define NN_SIDE_RIGHT 4 +// to the left, can also be east / positive x +#define NN_SIDE_LEFT 5 +// absolutely no clue +#define NN_SIDE_UNKNOWN 6 + +// pushes a screen_resized signal +nn_Exit nn_pushScreenResized(nn_Computer *computer, const char *screenAddress, int newWidth, int newHeight); +// pushes a touch signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushTouch(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); +// pushes a drag signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushDrag(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); +// pushes a drop signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); +// pushes a scroll signal +// A positive direction usually means up, a negative one usually means down. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player); +// pushes a walk signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player); + +// pushes a key_down event +// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. +// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. +// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushKeyDown(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); +// pushes a key_up event +// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. +// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. +// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushKeyUp(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); +// pushes a clipboard event +// clipboard should be a NULL-terminated string. +// NN does no truncation of the contents, but it is best to limit it. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, const char *player); +// pushes a clipboard event +// len is the length of the clipboard. +// NN does no truncation of the contents, but it is best to limit it. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushLClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, size_t len, const char *player); + +// pushes a redstone_changed signal. +// side is the side of the device the redstone changed on +// oldValue is the old value +// newValue is the new value, must not be equal to oldValue +// color is the color of the redstone signal +// if color < 0, it is set to null +nn_Exit nn_pushRedstoneChanged(nn_Computer *computer, const char *redstoneAddress, int side, int oldValue, int newValue, int color); + +// pushes a motion signal. +// Do note that it is meant to only be sent if the entity has a direct line of sight and if |(relX, relY, relZ)| >= sensitivity. +// This signal does *not* check if the motion sensor actually is sensitive enough to detect it, so make sure to check it yourself. +// relX, relY and relZ are the relative postion in 3D Cartesian space. +// entityName can be NULL if the entity has no name. +nn_Exit nn_pushMotion(nn_Computer *computer, double relX, double relY, double relZ, const char *entityName); + +// A buffer with encoded values +typedef struct nn_EncodedNetworkContents { + nn_Context *ctx; + char *buf; + size_t buflen; + size_t valueCount; +} nn_EncodedNetworkContents; + +// applies basic encoding to a network message. This encoding has a header, and thus should remain backwards-compatible. +// The encoding serves 2 purposes: +// 1. Prevent shared memory between computers. Values do not use atomic reference counting, and thus this could lead to UAF or memory leaks. +// 2. Simplify implementing packet queues, which should be used in relays. The lack of access to raw values would force implementers to use +// an encoding anyways. +// This only encodes the contents, not the sender, hops, or other metadata which may be needed in the queue. +// This does not pop the values, in case you need them afterwards. If you don't just call nn_popn(). +// The encoding is architecture-dependent, so be careful with storing it on-disk. +// Do note that the architecture-dependent parts are sizeof(double), sizeof(size_t) and endianness. +// The encoding is simple: +// - 0x00 for null +// - 0x01 for true +// - 0x02 for false +// - 0x03 + for a number +// - 0x04 + + for a string +// - 0x05 + for resource +// - 0x06 + + for a table +nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, size_t valueCount); +// Allocates a copy of [buf] and stores it in contents. +// This is useful for copying network contents, either from storage or from another buffer. +nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount); +void nn_dropNetworkContents(nn_EncodedNetworkContents *contents); +// Pushes the encoded contents onto the stack. +// This does not drop the network contents. +nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents); + +// push a modem_message, can be queued by both modems and tunnels. +// This does not check if the modem has that port open, so make sure to check it yourself. +// It does not check if the distance is within the modem's range, if it is wireless, and thus does not send it. +// Note that relays should change the sender. +nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents); + // EEPROM class // reads and writes are always 1/1 @@ -1923,14 +2175,6 @@ typedef struct nn_Modem { double costPerStrength; } nn_Modem; -// A buffer with encoded values -typedef struct nn_EncodedNetworkContents { - nn_Context *ctx; - char *buf; - size_t buflen; - size_t valueCount; -} nn_EncodedNetworkContents; - extern nn_Modem nn_defaultWiredModem; extern nn_Modem nn_defaultWirelessModems[2]; @@ -2039,7 +2283,7 @@ typedef struct nn_TunnelRequest { void *state; const nn_Tunnel *tunnel; const char *localAddress; - nn_ModemAction action; + nn_TunnelAction action; union { // for send const nn_EncodedNetworkContents *toSend; @@ -2097,250 +2341,6 @@ int nn_mapDepth(int color, int depth); // Valid depths are 1, 2, 3, 4, 8, 16 and 24. const char *nn_depthName(int depth); -// Signal helpers - -// common mouse buttons, not an exhaustive list -#define NN_BUTTON_LEFT 0 -#define NN_BUTTON_RIGHT 1 -#define NN_BUTTON_MIDDLE 2 - -// OC keycodes -// taken from https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua -#define NN_KEY_UNKNOWN 0 -#define NN_KEY_1 0x02 -#define NN_KEY_2 0x03 -#define NN_KEY_3 0x04 -#define NN_KEY_4 0x05 -#define NN_KEY_5 0x06 -#define NN_KEY_6 0x07 -#define NN_KEY_7 0x08 -#define NN_KEY_8 0x09 -#define NN_KEY_9 0x0A -#define NN_KEY_0 0x0B -#define NN_KEY_A 0x1E -#define NN_KEY_B 0x30 -#define NN_KEY_C 0x2E -#define NN_KEY_D 0x20 -#define NN_KEY_E 0x12 -#define NN_KEY_F 0x21 -#define NN_KEY_G 0x22 -#define NN_KEY_H 0x23 -#define NN_KEY_I 0x17 -#define NN_KEY_J 0x24 -#define NN_KEY_K 0x25 -#define NN_KEY_L 0x26 -#define NN_KEY_M 0x32 -#define NN_KEY_N 0x31 -#define NN_KEY_O 0x18 -#define NN_KEY_P 0x19 -#define NN_KEY_Q 0x10 -#define NN_KEY_R 0x13 -#define NN_KEY_S 0x1F -#define NN_KEY_T 0x14 -#define NN_KEY_U 0x16 -#define NN_KEY_V 0x2F -#define NN_KEY_W 0x11 -#define NN_KEY_X 0x2D -#define NN_KEY_Y 0x15 -#define NN_KEY_Z 0x2C - -#define NN_KEY_APOSTROPHE 0x28 -#define NN_KEY_AT 0x91 -#define NN_KEY_BACK 0x0E -#define NN_KEY_BACKSLASH 0x2B -// caps-lock -#define NN_KEY_CAPITAL 0x3A -#define NN_KEY_COLON 0x92 -#define NN_KEY_COMMA 0x33 -#define NN_KEY_ENTER 0x1C -#define NN_KEY_EQUALS 0x0D -// accent grave -#define NN_KEY_GRAVE 0x29 -#define NN_KEY_LBRACKET 0x1A -#define NN_KEY_LCONTROL 0x1D -// left alt -#define NN_KEY_LMENU 0x38 -#define NN_KEY_LSHIFT 0x2A -#define NN_KEY_MINUS 0x0C -#define NN_KEY_NUMLOCK 0x45 -#define NN_KEY_PAUSE 0xC5 -#define NN_KEY_PERIOD 0x34 -#define NN_KEY_RBRACKET 0x1B -#define NN_KEY_RCONTROL 0x9D -// right alt -#define NN_KEY_RMENU 0xB8 -#define NN_KEY_RSHIFT 0x36 -// scroll lock -#define NN_KEY_SCROLL 0x46 -#define NN_KEY_SEMICOLON 0x27 -#define NN_KEY_SLASH 0x35 -#define NN_KEY_SPACE 0x39 -#define NN_KEY_STOP 0x95 -#define NN_KEY_TAB 0x0F -#define NN_KEY_UNDERLINE 0x93 - -#define NN_KEY_UP 0xC8 -#define NN_KEY_DOWN 0xD0 -#define NN_KEY_LEFT 0xCB -#define NN_KEY_RIGHT 0xCD -#define NN_KEY_HOME 0xC7 -#define NN_KEY_END 0xCF -#define NN_KEY_PAGEUP 0xC9 -#define NN_KEY_PAGEDOWN 0xD1 -#define NN_KEY_INSERT 0xD2 -#define NN_KEY_DELETE 0xD3 - -#define NN_KEY_F1 0x3B -#define NN_KEY_F2 0x3C -#define NN_KEY_F3 0x3D -#define NN_KEY_F4 0x3E -#define NN_KEY_F5 0x3F -#define NN_KEY_F6 0x40 -#define NN_KEY_F7 0x41 -#define NN_KEY_F8 0x42 -#define NN_KEY_F9 0x43 -#define NN_KEY_F10 0x44 -#define NN_KEY_F11 0x57 -#define NN_KEY_F12 0x58 -#define NN_KEY_F13 0x64 -#define NN_KEY_F14 0x65 -#define NN_KEY_F15 0x66 -#define NN_KEY_F16 0x67 -#define NN_KEY_F17 0x68 -#define NN_KEY_F18 0x69 -#define NN_KEY_F19 0x71 - -#define NN_KEY_KANA 0x70 -#define NN_KEY_KANJI 0x94 -#define NN_KEY_CONVERT 0x79 -#define NN_KEY_NOCONVERT 0x7B -#define NN_KEY_YEN 0x7D -#define NN_KEY_CIRCUMFLEX 0x90 -#define NN_KEY_AX 0x96 - -#define NN_KEY_NUMPAD0 0x52 -#define NN_KEY_NUMPAD1 0x4F -#define NN_KEY_NUMPAD2 0x50 -#define NN_KEY_NUMPAD3 0x51 -#define NN_KEY_NUMPAD4 0x4B -#define NN_KEY_NUMPAD5 0x4C -#define NN_KEY_NUMPAD6 0x4D -#define NN_KEY_NUMPAD7 0x47 -#define NN_KEY_NUMPAD8 0x48 -#define NN_KEY_NUMPAD9 0x49 -#define NN_KEY_NUMPADMUL 0x37 -#define NN_KEY_NUMPADDIV 0xB5 -#define NN_KEY_NUMPADSUB 0x4A -#define NN_KEY_NUMPADADD 0x4E -#define NN_KEY_NUMPADDECIMAL 0x53 -#define NN_KEY_NUMPADCOMMA 0xB3 -#define NN_KEY_NUMPADENTER 0x9C -#define NN_KEY_NUMPADEQUALS 0x8D - -// the bottom side, can also be downwards / negative y -#define NN_SIDE_BOTTOM 0 -// the top side, can also be upwards / positive y -#define NN_SIDE_TOP 1 -// backwards, can also be north / negative z -#define NN_SIDE_BACK 2 -// forwards, can also be south / positive z -#define NN_SIDE_FRONT 3 -// to the right, can also be west / negative x -#define NN_SIDE_RIGHT 4 -// to the left, can also be east / positive x -#define NN_SIDE_LEFT 5 -// absolutely no clue -#define NN_SIDE_UNKNOWN 6 - -// pushes a screen_resized signal -nn_Exit nn_pushScreenResized(nn_Computer *computer, const char *screenAddress, int newWidth, int newHeight); -// pushes a touch signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushTouch(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); -// pushes a drag signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushDrag(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); -// pushes a drop signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); -// pushes a scroll signal -// A positive direction usually means up, a negative one usually means down. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player); -// pushes a walk signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player); - -// pushes a key_down event -// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. -// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. -// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushKeyDown(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); -// pushes a key_up event -// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. -// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. -// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushKeyUp(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); -// pushes a clipboard event -// clipboard should be a NULL-terminated string. -// NN does no truncation of the contents, but it is best to limit it. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, const char *player); -// pushes a clipboard event -// len is the length of the clipboard. -// NN does no truncation of the contents, but it is best to limit it. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushLClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, size_t len, const char *player); - -// pushes a redstone_changed signal. -// side is the side of the device the redstone changed on -// oldValue is the old value -// newValue is the new value, must not be equal to oldValue -// color is the color of the redstone signal -// if color < 0, it is set to null -nn_Exit nn_pushRedstoneChanged(nn_Computer *computer, const char *redstoneAddress, int side, int oldValue, int newValue, int color); - -// pushes a motion signal. -// Do note that it is meant to only be sent if the entity has a direct line of sight and if |(relX, relY, relZ)| >= sensitivity. -// This signal does *not* check if the motion sensor actually is sensitive enough to detect it, so make sure to check it yourself. -// relX, relY and relZ are the relative postion in 3D Cartesian space. -// entityName can be NULL if the entity has no name. -nn_Exit nn_pushMotion(nn_Computer *computer, double relX, double relY, double relZ, const char *entityName); - -// applies basic encoding to a network message. This encoding has a header, and thus should remain backwards-compatible. -// The encoding serves 2 purposes: -// 1. Prevent shared memory between computers. Values do not use atomic reference counting, and thus this could lead to UAF or memory leaks. -// 2. Simplify implementing packet queues, which should be used in relays. The lack of access to raw values would force implementers to use -// an encoding anyways. -// This only encodes the contents, not the sender, hops, or other metadata which may be needed in the queue. -// This does not pop the values, in case you need them afterwards. If you don't just call nn_popn(). -// The encoding is architecture-dependent, so be careful with storing it on-disk. -// Do note that the architecture-dependent parts are sizeof(double), sizeof(size_t) and endianness. -// The encoding is simple: -// - 0x00 for null -// - 0x01 for true -// - 0x02 for false -// - 0x03 + for a number -// - 0x04 + + for a string -// - 0x05 + for resource -// - 0x06 + + for a table -nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, size_t valueCount); -// Allocates a copy of [buf] and stores it in contents. -// This is useful for copying network contents, either from storage or from another buffer. -nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount); -void nn_dropNetworkContents(nn_EncodedNetworkContents *contents); -// Pushes the encoded contents onto the stack. -// This does not drop the network contents. -nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents); - -// push a modem_message, can be queued by both modems and tunnels. -// This does not check if the modem has that port open, so make sure to check it yourself. -// It does not check if the distance is within the modem's range, if it is wireless, and thus does not send it. -// Note that relays should change the sender. -nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents); - #ifdef __cplusplus } #endif