diff --git a/src/main.c b/src/main.c index 45495f2..bb23c92 100644 --- a/src/main.c +++ b/src/main.c @@ -473,17 +473,17 @@ int main(int argc, char **argv) { //nn_setTmpAddress(c, nn_getComponentAddress(tmpfs)); - nn_mountComponent(c, wrappedC, 255); - nn_mountComponent(c, screen, -1); - nn_mountComponent(c, ocelotCard, -1); - //nn_mountComponent(c, tmpfs, -1); - nn_mountComponent(c, keyboard, -1); - nn_mountComponent(c, eepromCard, 0); - nn_mountComponent(c, managedfs, 1); - nn_mountComponent(c, gpuCard, 2); - nn_mountComponent(c, testingfs, 3); - nn_mountComponent(c, testDrive, 4); - nn_mountComponent(c, testFlash, 5); + nn_mountComponent(c, wrappedC, 255, false); + nn_mountComponent(c, screen, -1, false); + nn_mountComponent(c, ocelotCard, -1, false); + //nn_mountComponent(c, tmpfs, -1, false); + nn_mountComponent(c, keyboard, -1, false); + nn_mountComponent(c, eepromCard, 0, false); + nn_mountComponent(c, managedfs, 1, false); + nn_mountComponent(c, gpuCard, 2, false); + nn_mountComponent(c, testingfs, 3, false); + nn_mountComponent(c, testDrive, 4, false); + nn_mountComponent(c, testFlash, 5, false); while(true) { if(WindowShouldClose()) break; diff --git a/src/neonucleus.c b/src/neonucleus.c index df8b08c..ee59ab4 100644 --- a/src/neonucleus.c +++ b/src/neonucleus.c @@ -1858,7 +1858,7 @@ static nn_Exit nn_pushComponentRemoved(nn_Computer *c, const char *address, cons return nn_pushSignal(c, 3); } -nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot) { +nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot, bool silent) { if(nn_getComponent(c, comp->address) != NULL) return NN_EBADSTATE; nn_ComponentEntry ent = { @@ -1870,20 +1870,20 @@ nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot) { if(!nn_hashPut(&c->components, &ent)) return NN_ELIMIT; nn_retainComponent(comp); nn_signalComponent(comp, c, NN_CSIGMOUNTED); - if(c->state == NN_RUNNING) { + if(c->state == NN_RUNNING && !silent) { return nn_pushComponentAdded(c, comp->address, comp->type); } return NN_OK; } -nn_Exit nn_unmountComponent(nn_Computer *c, const char *address) { +nn_Exit nn_unmountComponent(nn_Computer *c, const char *address, bool silent) { nn_Component *comp = nn_getComponent(c, address); if(comp == NULL) return NN_OK; nn_ComponentEntry lookingFor = {.address = address}; nn_hashRemove(&c->components, &lookingFor); nn_Exit e = NN_OK; - if(c->state == NN_RUNNING) { + if(c->state == NN_RUNNING && !silent) { e = nn_pushComponentRemoved(c, address, comp->type); } nn_signalComponent(comp, c, NN_CSIGUNMOUNTED); @@ -1891,6 +1891,24 @@ nn_Exit nn_unmountComponent(nn_Computer *c, const char *address) { return e; } +nn_Exit nn_swapComponents(nn_Computer *c, nn_Component *previous, nn_Component *next, int slot) { + bool silent = false; + if(previous && next) { + // means for reasons beyond our understanding the config changed + silent = nn_strcmp(previous->address, next->address) == 0; + } + nn_Exit e; + if(previous != NULL) { + e = nn_unmountComponent(c, previous->address, silent); + if(e) return e; + } + if(next != NULL) { + e = nn_mountComponent(c, next, slot, silent); + if(e) return e; + } + return NN_OK; +} + static nn_ComponentEntry *nn_getComponentEntry(nn_Computer *c, const char *address) { nn_ComponentEntry ent = { .address = address, diff --git a/src/neonucleus.h b/src/neonucleus.h index 7bdcc88..26a410e 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -673,13 +673,14 @@ const char *nn_getComponentTypeID(nn_Component *c); const char *nn_getComponentAddress(nn_Component *c); // Adds a component to the computer on a given slot. -// This will also queue a component_added signal if the computer is in a running state. +// This will also queue a component_added signal if the computer is in a running state, unless silent is true. // If the component already is mounted, an error is returned. -nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot); +nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot, bool silent); // Removes a component from the computer. -// This will also queue a component_removed signal if the computer is in a running state. +// This will also queue a component_removed signal if the computer is in a running state, unless silent is true. // If the component is not mounted, no error is returned. -nn_Exit nn_unmountComponent(nn_Computer *c, const char *address); +nn_Exit nn_unmountComponent(nn_Computer *c, const char *address, bool silent); +nn_Exit nn_swapComponents(nn_Computer *c, nn_Component *previous, nn_Component *next, int slot); // gets a component by address. Will return NULL if there is none. nn_Component *nn_getComponent(nn_Computer *c, const char *address); int nn_getComponentSlot(nn_Computer *c, const char *address); diff --git a/src/netlib.c b/src/netlib.c new file mode 100644 index 0000000..9ab4a41 --- /dev/null +++ b/src/netlib.c @@ -0,0 +1,55 @@ +#include "netlib.h" +// libm needed, rip +#include + +const net_NetworkLimits net_defaultLimits = (net_NetworkLimits) { + .maxRadioLifespan = 6000, + .radioSpeed = 10000, + .maxRadioPackets = 4096, + .maxConnections = 128, + .maxHops = 32, + .maxChannel = 128, +}; + +typedef struct net_Channel { + char *address; + net_Device **devices; + size_t len; + struct net_Channel *prevChannel; + struct net_Channel *nextChannel; +} net_Channel; + +typedef struct net_Network { + nn_Universe *universe; + nn_Context *ctx; + nn_Lock *lock; + net_NetworkLimits limits; + net_Device *allDevices; + net_Device *allTicked; + net_Channel *allChannels; + size_t nextMessageID; + size_t nextDiscoverID; + net_RadioPacket *radioPackets; + size_t radioPacketCount; +} net_Network; + +typedef struct net_DeviceConn { + net_Device *target; + int slot; +} net_DeviceConn; + +typedef struct net_Device { + net_Network *network; + net_Channel *channel; + net_Device *prevDevice; + net_Device *nextDevice; + net_Device *prevTicked; + net_Device *nextTicked; + void *state; + net_Filter *filter; + net_TickFunc *tickFunc; + net_DeviceConn *connections; + size_t connectionLen; + size_t lastMessageID; + size_t lastDiscoverID; +} net_Device; diff --git a/src/netlib.h b/src/netlib.h index 82d451e..b524243 100644 --- a/src/netlib.h +++ b/src/netlib.h @@ -6,9 +6,12 @@ // should be sent by computers when they start, // in order to mount every component accessible #define NET_COMPDISCOVER "componentDiscover" -#define NET_COMPADDED "componentAdded" -#define NET_COMPREMOVED "componentRemoved" +#define NET_COMPSWAP "componentSwapped" #define NET_OCMESSAGE "modemMessage" +// sent when devices with components are disconnected to tell +// computers to recheck accessibility. +// If not, remove the component +#define NET_RECOMPUTE "netRecompute" #define NET_RADIOMESSAGE "radioMessage" typedef struct net_ModemMessageData { @@ -25,23 +28,34 @@ typedef struct net_RadioMessageData { char *data; } net_RadioMessageData; +typedef struct net_ComponentSwappedData { + // if NULL, no component was removed + nn_Component *removed; + // if NULL, no component was added + nn_Component *added; +} net_ComponentSwappedData; + typedef struct net_NetworkLimits { // maximum radio lifespan in ticks + // maxRadioLifespan * radioSpeed is the maximum effective range of radio size_t maxRadioLifespan; // radio speed in coordinates per tick double radioSpeed; - // maximum connections in a device + // max radio packets at one instant + // If it overflows, the oldest is forcefully removed + size_t maxRadioPackets; + // maximum *DIRECT* connections in a device size_t maxConnections; - // maximum amount of devices in a octo-tree node. - // For efficient wireless comms, the devices are stored in an octo-tree, the 3D version of a quadtree. - size_t maxTreeNodeSize; // maximum number of hops past which the packet gets dropped. // Set to 0 for infinite hops, but then it is possible to stackoverflow. size_t maxHops; // maximum amount of devices in a single channel size_t maxChannel; + // TODO: use an RTree, and thus specify the capacity per node } net_NetworkLimits; +extern const net_NetworkLimits net_defaultLimits; + typedef struct net_Network net_Network; typedef struct net_Device net_Device; @@ -59,7 +73,7 @@ typedef struct net_RadioPacket { size_t expirationTick; char *data; size_t datalen; -} net_Radiopacket; +} net_RadioPacket; typedef enum net_MessagePropagation { // direct connections @@ -84,6 +98,7 @@ typedef struct net_Message { const char *type; void *data; size_t hops; + net_MessagePropagation propagation; union { // for NET_MESSAGE_WIRELESS size_t range; @@ -102,19 +117,23 @@ typedef void (net_TickFunc)(net_Device *device, size_t tickCount); net_Network *net_createNetwork(nn_Universe *universe, const net_NetworkLimits *limits); void net_destroyNetwork(net_Network *network); + +// locks the network. Only one thread may do ANYTHING to this network or its devices +// at the same time void net_lockNetwork(net_Device *device); void net_unlockNetwork(net_Device *device); net_Device *net_createDevice(net_Network *network, const char *type, size_t slotCount, net_DevicePosition position); void net_destroyDevice(net_Device *device); -void net_lockDevice(net_Device *device); -void net_unlockDevice(net_Device *device); +net_Network *net_getNetworkOf(net_Device *device); +// adds to a channel, will create the channel if it does not exist nn_Exit net_addToChannel(net_Device *device, const char *channel); +// removes from a channel, will delete the channel if it is empty void net_removeFromChannel(net_Device *device, const char *channel); // will automatically give the message an ID and hops count, so don't bother -// this will lock the network for its entire duration +// the sender must be set to the correct sender. This is primarily to allow faking senders if need be. void net_emit(net_Device *device, const net_Message *message); void net_setDevicePosition(net_Device *device, net_DevicePosition position); @@ -125,30 +144,37 @@ void *net_getDeviceState(net_Device *device); void net_setDeviceFilter(net_Device *device, net_Filter *filter); void net_setDeviceTick(net_Device *device, net_TickFunc *tick); void net_tickDevice(net_Device *device); +// automatically emits a COMPSWAPPED message. void net_setDeviceComponent(net_Device *device, nn_Component *component); nn_Component *net_getDeviceComponent(net_Device *device); size_t net_getDeviceSlotCount(net_Device *device); -net_Device *net_getDeviceSlot(net_Device *device, size_t slot); -// returns -1 if not directly connected +// returns whether there is a direction one-way connection from device to target +bool net_isDirectlyConnectedTo(net_Device *device, net_Device *target); +// returns -1 if not directly connected, but that can also be the slot its on int net_getSlotOfDevice(net_Device *device, net_Device *target); -// establishes a 1-WAY connection from device to target on a slot. Each slot may only have one, so previous ones are disconnected -void net_setDeviceSlot(net_Device *device, size_t slot, net_Device *target); -// adds a 1-WAY connection form device to target, but on not slot. -nn_Exit net_mountDevice(net_Device *device, net_Device *target); -// removes ALL 1-WAY connections from device to target +// does a depth-first-scan to see if a network device b remains accessible from a. +// It may be, due to one-way connections, that b is accessible from a but a not from b. +bool net_isNetworkDeviceAccessible(net_Device *a, net_Device *b); +// establishes a 1-WAY connection from device to target on a slot. The target may only be connected once, +// else this will return EBADSTATE. +nn_Exit net_addDeviceSlot(net_Device *device, int slot, net_Device *target); +// removes ALL 1-WAY connections from device to target. +// Also emits a RECOMPUTE for the target void net_removeDeviceOneWay(net_Device *device, net_Device *target); // disconnects the devices both-ways void net_disconnectDevices(net_Device *deviceA, net_Device *deviceB); typedef void (net_Visitor)(net_Network *network, void *state, net_Device *device); -// gets the network device count -size_t net_countNetworkDevices(net_Network *network); -// get the device, buffer must be big enough for all of them. -// Make sure to call net_countNetworkDevices. -// If running stuff on multiple threads, MAKE SURE THE NETWORK IS LOCKED. THIS IS VERY IMPORTANT -void net_getNetworkDevices(net_Network *network, net_Device **devices); +// iterates every network device +void net_visitNetworkDevices(net_Network *network, void *state, net_Visitor *visitor); +// iterates every network device that wants to be ticked +void net_visitTickingNetworkDevices(net_Network *network, void *state, net_Visitor *visitor); +// visits all devices in a cube with sides of range*2 and origin at origin +void net_visitNetworkDevicesBetween(net_Network *network, net_DevicePosition origin, double range, void *state, net_Visitor *visitor); +// visits all devices connected to a channel +void net_visitNetworkChannel(net_Network *network, const char *devices, void *state, net_Visitor *visitor); size_t net_getNetworkTickCount(net_Network *network); // increase the tick count by 1 @@ -157,7 +183,10 @@ void net_incNetworkTickCount(net_Network *network); // set it to 0 void net_resetNetworkTickCount(net_Network *network); -void net_spawnRadioPacket(net_Network *network); +// Increments the network tick count and iterates over all devices with ticking (using an internal linked list) +void net_tickNetwork(net_Network *network); + +void net_spawnRadioPacket(net_Device *sourceDevice, double range, const char *data, size_t len); size_t net_countRadioPackets(net_Network *network); void net_getRadioPackets(net_Network *network, net_RadioPacket *packets);