Пример #1
0
void WorldService::WorldLoader::connectDoors(WorldTreeNode &currentNode, 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 = &currentNode;
			childNode.value = childWorld->world;

			// recurse
			connectDoors(childNode, *childWorld, visitedWorlds);
		}

	}
}
Пример #2
0
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();
        }
    }
}