void WorldService::WorldLoader::connectDoors(WorldTreeNode ¤tNode, LoadedWorld &world, std::set<WorldID> &visitedWorlds) { if (visitedWorlds.find(world.world->getID()) != visitedWorlds.end()) return; visitedWorlds.insert(world.world->getID()); for (LoadedDoor &door : world.doors) { LoadedWorld *childWorld = getLoadedWorld(door.doorID > 0 ? door.worldID : currentNode.parent->value->getID()); if (childWorld == nullptr) { Logger::logError(format("World %1% has not been loaded yet in connectDoors()", _str(door.worldID))); return; } LoadedDoor *targetDoor = findPartnerDoor(*childWorld, door.doorID, world.world->getID()); if (targetDoor == nullptr) { Logger::logError(format("Cannot find partner door in world %1% for door %2% in world %3%", _str(door.worldID), _str(door.doorID), _str(world.world->getID()))); return; } // add connection to this world's lookup table Location loc(world.world->getID(), door.tile); connectionLookup.emplace(std::piecewise_construct, std::forward_as_tuple(loc), std::forward_as_tuple(childWorld->world->getID(), targetDoor->tile)); doorDetails.insert({loc, ConnectionDetails(loc, door.orientation, door.dimensions)}); Logger::logDebuggiest(format("Added world connection %1% to %2% from %3% through door %4%", door.doorID < 0 ? "up" : "down", _str(world.world->getID()), _str(childWorld->world->getID()), _str(door.doorID))); // add node if (door.doorID > 0) { currentNode.children.emplace_back(); WorldTreeNode &childNode = currentNode.children.back(); childNode.parent = ¤tNode; childNode.value = childWorld->world; // recurse connectDoors(childNode, *childWorld, visitedWorlds); } } }
void Blackboard::eventLoop(int _socketListener) { // set the listener socket socketListener = _socketListener; // loop forever while (true) { prepareSelect(); // prepare for select call by collecting every open fd if (WORDY) log("watching %d connections\n", (unsigned int) fdSet.size()); // block until one or more fd need attention or timeout int selectValue = select(maxfd + 1, &fd_r, &fd_w, &fd_x, &select_tv); if (selectValue < 0) { // check that select returned properly, if not, check why if (errno == EBADF) { // lost track of a fd (should never happen) log("invalid fd (closed connection) of a set of %d.\n", (unsigned int) fdSet.size()); // find out which fd we lost track of... // collect them all struct pollfd fds[fdSet.size()]; int i = 0; for (std::map<int, ConnectionDetails>::iterator it = fdSet.begin(); it != fdSet.end(); ++it) { int fd = it->first; fds[i].fd = fd; fds[i].events = POLLWRNORM | POLLRDBAND; ++i; } // check them all int pollret = 0; pollret = poll(fds, i, -1); if (pollret >= 0) { for (int j = 0; j < i; j++) { if (fds[j].revents == POLLNVAL) { log("%#010x was closed but never cleaned, marking it as dirty\n", fds[j].fd); // push the local connection to the deleteQueue for cleaning fdDeleteQueue.push_back(fds[j].fd); } } } else { // everything checks out but select still failed, panic. log("Unidentified non-cleaned fd: Blackboard cannot continue. (%d)\n\n", pollret); exit(1); } cleanClosedConnection(); // clean up anything we found continue; // restart the event loop } errorOnFail(FAIL, "select"); // select failed for some other reason } // if we have a new connection, accept it and add it to the fdSet if (FD_ISSET(socketListener, &fd_r)) { log("%#010x attempts connect... ", socketListener); int newfd = accept(socketListener, NULL, NULL); if (newfd >= 0) { log("got connection %#010x\n", newfd); // once we accepted the connection create a new entry for it. fdSet.insert(std::pair<int, ConnectionDetails > (newfd, ConnectionDetails())); } else { log("connect failed.\n"); } } // TODO: schedule connection handling in a more fair manner // currently: lowest descriptor first, all packets then yield // problems: starvation to newer connections // if packet rate is too fast, we never yield // loop over all the connections we have and check if they need anything for (std::map<int, ConnectionDetails>::iterator it = fdSet.begin(); it != fdSet.end(); ++it) { // assign values for what we are accessing and dealing with int fd = it->first; ConnectionDetails& cd = it->second; // check if we can read anything (or the connection has closed (gracefully or otherwise) if (FD_ISSET(fd, &fd_r)) { if (WORDY) log("%#010x recv\n", fd); Packet vecbuffer; vecbuffer.resize(MAX_BUFFER_SIZE, 0); // allocate memory for a full read int recvResult = recv(fd, &(vecbuffer.front()), vecbuffer.size(), 0); errorOnFail(recvResult < 0, "%#010x recv failed\n"); if (recvResult > 0) { // have something vecbuffer.resize(recvResult); // trim buffer size // TODO: consider a handle queue for packet scheduling handlePacket(fd, vecbuffer); // hand packet off the the handler } else { // recvResult == 0 implies the connection has gone away log("%#010x close\n", fd); close(fd); // close it on our end // we cannot remove the fd from the set just yet so save it to be swept up later fdDeleteQueue.push_back(fd); if (cd.sendQueue.size() > 0) { log("%#010x died with unsent packets\n", fd); } cd.sendQueue.clear(); // clear the sendQueue so even if we can write for some reason, we don't } } // check if we can write if (FD_ISSET(fd, &fd_w)) { // TODO: limit the amount to send while ((cd.sendQueue.size() > 0)) { // have something to send? Packet& packet = cd.sendQueue.front(); // get it if (WORDY) log("%#010x send\n", fd); // try to send it (do not block though) int sendResult = send(fd, &(packet.front()), packet.size(), MSG_DONTWAIT); // was data sent? if so remove it from the sent queue // if not, leave it there if it would block if (sendResult >= 0) { cd.sendQueue.pop_front(); } else { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { break; // would block so try again later } else { errorOnFail(FAIL, "send failed\n"); } } } } } if (fdDeleteQueue.size() > 0) { // IF any process asked to close, clean them // we must erase outside of the iterator to avoid SIGSEGV due to invalidated iterator log("%d connections to be cleaned.\n", (unsigned int) fdDeleteQueue.size()); cleanClosedConnection(); } } }