@Override
        protected N computeNext() {
            while (true) {
                if (this.stack.isEmpty()) {
                    return endOfData();
                }
                NodeAndSuccessors node = this.stack.getFirst();
                boolean firstVisit = this.visited.add(node.node);
                boolean lastVisit = !node.successorIterator.hasNext();
                boolean produceNode = firstVisit && this.order == Order.PRE_ORDER || lastVisit && this.order == Order.POST_ORDER;
                if (lastVisit) {
                    this.stack.pop();
                } else {
                    // we need to push a neighbor, but only if we haven't already seen it
                    N successor = node.successorIterator.next();
                    if (!this.visited.contains(successor)) {
                        this.stack.push(withSuccessors(successor));
                    }
                }
                if (produceNode) {
                    return node.node;
                }
            }
        }
Returns an unmodifiable {@code Iterable} over the nodes reachable from
{@code startNode}, in the order defined by the {@code algorithm}.

@param graph the graph
@param startNode the start node
@param <N> the node type
@return the traversal
common/src/main/java/me/lucko/luckperms/common/graph/TraversalAlgorithm.java