@Override
  public void run() {
    try {
      if (match.getAnalysisClock() >= 0) {
        sendAnalyzeRequests();
      }

      notifyObservers(new ServerNewMatchEvent(stateMachine.getRoles(), currentState));
      notifyObservers(new ServerTimeEvent(match.getStartClock() * 1000));
      sendStartRequests();
      appendErrorsToMatchDescription();

      while (!stateMachine.isTerminal(currentState)) {
        publishWhenNecessary();
        saveWhenNecessary();
        notifyObservers(new ServerNewGameStateEvent(currentState));
        notifyObservers(new ServerTimeEvent(match.getPlayClock() * 1000));
        notifyObservers(new ServerMatchUpdatedEvent(match, spectatorServerKey, saveToFilename));
        previousMoves = sendPlayRequests();

        notifyObservers(new ServerNewMovesEvent(previousMoves));
        currentState = stateMachine.getNextState(currentState, previousMoves);

        match.appendMoves2(previousMoves);
        match.appendState(currentState.getContents());
        appendErrorsToMatchDescription();
      }
      match.markCompleted(stateMachine.getGoals(currentState));
      publishWhenNecessary();
      saveWhenNecessary();
      notifyObservers(new ServerNewGameStateEvent(currentState));
      notifyObservers(new ServerCompletedMatchEvent(getGoals()));
      notifyObservers(new ServerMatchUpdatedEvent(match, spectatorServerKey, saveToFilename));
      sendStopRequests(previousMoves);
    } catch (InterruptedException ie) {
      if (match.isAborted()) {
        return;
      } else {
        ie.printStackTrace();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
 private synchronized void sendStartRequests() throws InterruptedException {
   List<StartRequestThread> threads = new ArrayList<StartRequestThread>(hosts.size());
   for (int i = 0; i < hosts.size(); i++) {
     if (!playerPlaysRandomly[i]) {
       threads.add(
           new StartRequestThread(
               this,
               match,
               stateMachine.getRoles().get(i),
               hosts.get(i),
               ports.get(i),
               getPlayerNameFromMatchForRequest(i)));
     }
   }
   for (StartRequestThread thread : threads) {
     thread.start();
   }
   if (forceUsingEntireClock) {
     Thread.sleep(match.getStartClock() * 1000);
   }
   for (StartRequestThread thread : threads) {
     thread.join();
   }
 }