Package pong.server.model

Source Code of pong.server.model.ServerModel

package pong.server.model;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Queue;
import pong.common.GlobalSettings;
import pong.common.Position;
import pong.server.ServerSettings;

/**
* The server model class. It creates the server socket, wait for clients to connect and manage
* rounds.
*
* @author Lorenzo Gatto
*
*/
public class ServerModel extends Thread {
  private static final double NANOSECONDS_PER_SECOND = 1000000000;

  private final CollisionDetectorInterface collisionDetector;
  private final Player[] player;
  private final Ball ball;
  private final ServerModelListener listener;
  private final int portNumber;
  private final int roundNumber;
  private final Queue<ServerProtocolThread> disconnected;
  private ServerProtocolThread[] connectionThread;
  private int roundPlayed;
  private int[] roundWon;
  private long lastFrameTime;
  private Boolean waitingPlayer;
  private Double currentBallSpeed;

  /**
   * Initializer.
   *
   * @param portNumber
   *            server's port number
   * @param roundNumber
   *            total number of rounds for a match
   * @param listener
   *            the server model listener
   */
  public ServerModel(final int portNumber, final int roundNumber,
      final ServerModelListener listener) {
    super();

    this.collisionDetector = new CollisionDetector();
    this.connectionThread = new ServerProtocolThread[2];
    this.portNumber = portNumber;
    this.roundNumber = roundNumber;
    this.roundWon = new int[2];
    this.player = new Player[2];
    this.ball = new Ball();
    for (int i = 0; i < 2; i++) {
      this.player[i] = new Player();
    }
    this.listener = listener;
    this.listener.drawLogPanel();
    this.disconnected = new LinkedList<>();
    this.waitingPlayer = false;
  }

  /**
   * Creates the socket and starts listening to it. When both players are connected the match
   * starts.
   */
  @Override
  public synchronized void run() {
    this.listener.printMessage("Server is creating socket on port " + portNumber);
    // Create the socket
    ServerSocket serverSocket;
    try {
      serverSocket = new ServerSocket(portNumber);
    } catch (IllegalArgumentException | IOException e) {
      listener.showErrorDialog("Error creating socket!", "Error!");
      this.listener.drawOptionsPanel();
      return;
    }
    this.listener.printMessage("Socket created successfully...");
    try {
      while (true) {
        while (checkDisconnections()) {
          if (!waitingPlayer) {
            new SocketWaiter(serverSocket, this).start();
            waitingPlayer = true;
          }
          wait(ServerSettings.SERVER_SLEEP_TIME);
        }

        initializeForMatchPlaying();
        // PLAYING: here the match is played
        // avviso che inizia il gioco
        for (int i = 0; i < 2; i++) {
          this.connectionThread[i].matchBeginsEvent(roundNumber);
        }
        while (true) {
          wait(ServerSettings.ROUND_BEGIN_PAUSE);
          if (checkDisconnections()) {
            break;
          }

          if (roundPlayed == roundNumber) { // end of the match
            connectionThread[0].matchEndsEvent();
            connectionThread[1].matchEndsEvent();
            checkMatchWinner();
            break;
          } else {
            playRound();
            if (checkDisconnections()) {
              break;
            }
          }
        }

        wait(GlobalSettings.MATCH_END_PAUSE);
      }
    } catch (InterruptedException e) {
      for (int i = 0; i < 2; i++) {
        if (connectionThread[i] != null) {
          connectionThread[i].interrupt();
        }
      }
      listener.drawOptionsPanel();
      try {
        serverSocket.close();
      } catch (IOException e1) {
        return;
      }
      return;
    }
  }

  /**
   * A player connected to the server.
   *
   * @param socket
   *            socket
   */
  public synchronized void playerConnectedEvent(final Socket socket) {
    waitingPlayer = false;
    for (int i = 0; i < 2; i++) {
      if (connectionThread[i] == null) {
        connectionThread[i] = new ServerProtocolThread(socket, player[i], this, i);
        connectionThread[i].start();
        listener.printMessage("Player " + (i + 1) + " connected");
        break;
      }
    }
  }

  /**
   * Return objects positions.
   *
   * @return an array with 3 Position objects: player 1, player 2 and ball positions
   */
  public Position[] getObjectsPositions() {
    Position[] ret = new Position[3];
    ret[0] = player[0].getPosition();
    ret[1] = player[1].getPosition();
    ret[2] = ball.getPosition();
    return ret;
  }

  /**
   * A player has disconnected.
   *
   * @param disconnectedThread
   *            the thread that managed the connection with the client.
   */
  public void playerDisconnectedEvent(final ServerProtocolThread disconnectedThread) {
    synchronized (disconnected) {
      disconnected.add(disconnectedThread);
    }
  }

  /**
   * Check for disconnections and, if any, prints a disconnection message and warn the other
   * player.
   *
   * @return false if two players are connected, true otherwise
   */
  /*
   * this method takes into consideration the fact that the lock on disconnected should be
   * acquired after the lock on ServerProtocolThread objects.
   */
  private Boolean checkDisconnections() {
    while (true) {
      ServerProtocolThread disc = null;
      synchronized (disconnected) {
        if (disconnected.isEmpty()) {
          break;

        } else {
          disc = disconnected.remove();
        }
      }
      for (int i = 0; i < 2; i++) {
        if (disc == connectionThread[i]) {
          listener.printMessage("Player " + (i + 1) + " disconnected");
          connectionThread[i] = null;
          if (connectionThread[1 - i] != null) {
            connectionThread[1 - i].otherDisconnectedWhilePlayingEvent();
          }
        }
      }
    }

    for (int i = 0; i < 2; i++) {
      if (connectionThread[i] == null) {
        return true;
      }
    }
    return false;
  }

  /**
   * Plays one round of the match. Call this function only if both players are connected.
   *
   * @throws InterruptedException
   *             in case of thread interruption
   */
  private void playRound() throws InterruptedException {
    listener.printMessage("Starting round...");
    initializeForRoundPlaying();
    for (int i = 0; i < 2; i++) {
      connectionThread[i].roundBeginsEvent();
    }
    while (true) {
      if (checkDisconnections()) {
        break;
      }
      int roundWinner = updatePositions();
      if (roundWinner != 0) {
        roundWinner--;
        connectionThread[roundWinner].roundEndEvent(true);
        connectionThread[1 - roundWinner].roundEndEvent(false);
        roundWon[roundWinner]++;
        listener.printMessage("Player " + (roundWinner + 1) + " won the round");
        roundWinner++;
        break;
      }
      wait(ServerSettings.SERVER_SLEEP_TIME);
    }
    roundPlayed++;
  }

  private void initializeForMatchPlaying() {
    roundPlayed = 0;
    roundWon[0] = 0;
    roundWon[1] = 0;
  }

  private void initializeForRoundPlaying() {
    player[0].initializeForRoundPlaying(0);
    player[1].initializeForRoundPlaying(1);
    ball.initializeForRoundPlaying();
    currentBallSpeed = (double) GlobalSettings.INITIAL_BALL_SPEED;
    lastFrameTime = System.nanoTime();
  }

  /**
   * Updates positions of objects and check for round end
   *
   * @return the round winner index (1 or 2). 0 if none still won
   */
  private int updatePositions() {

    final long newFrameTime = System.nanoTime();
    final long elapsedTimeNanoSeconds = newFrameTime - lastFrameTime;
    currentBallSpeed += (double) GlobalSettings.BALL_ACCELERATION * elapsedTimeNanoSeconds
        / NANOSECONDS_PER_SECOND;
    currentBallSpeed = Math.min(currentBallSpeed, GlobalSettings.MAX_BALL_SPEED);
    lastFrameTime = newFrameTime;
    final Position[] positions = getObjectsPositions();
    // move rackets
    for (int i = 0; i < 2; i++) {
      float y = positions[i].getY();
      if (player[i].isGoingUp()) {
        y -= GlobalSettings.RACKETS_SPEED * elapsedTimeNanoSeconds / NANOSECONDS_PER_SECOND;
      }
      if (player[i].isGoingDown()) {
        y += GlobalSettings.RACKETS_SPEED * elapsedTimeNanoSeconds / NANOSECONDS_PER_SECOND;
      }
      if (y < 0) {
        y = 0;
      } else if (y > GlobalSettings.FIELD_HEIGHT - GlobalSettings.RACKET_HEIGHT) {
        y = GlobalSettings.FIELD_HEIGHT - GlobalSettings.RACKET_HEIGHT;
      }
      player[i].getPosition().setY(y);
    }
    // move ball
    float x = positions[2].getX();
    final double deltaX = Math.cos(Math.atan(ball.getAngularCoefficient())) * currentBallSpeed
        * elapsedTimeNanoSeconds / NANOSECONDS_PER_SECOND;
    if (ball.getDirection() == Ball.Direction.LEFT_TO_RIGHT) {
      x += deltaX;
    } else {
      x -= deltaX;
    }
    float y = positions[2].getY();
    final double deltaY = deltaX * ball.getAngularCoefficient();
    y += deltaY;
    ball.setPosition(new Position(x, y));
    if (ball.getPosition().getX() < 0 - GlobalSettings.BALL_DIAMETER) {
      return 2;
    }
    if (ball.getPosition().getX() > GlobalSettings.FIELD_WIDTH) {
      return 1;
    }
    this.collisionDetector.handleCollisions(player, ball);
    return 0;
  }

  /**
   * Checks who won the match and prints it to screen
   */
  private void checkMatchWinner() {
    if (roundWon[0] > roundWon[1]) {
      this.listener.printMessage("Player 1 won the match");
    } else if (roundWon[0] == roundWon[1]) {
      this.listener.printMessage("Match drawn");
    } else {
      this.listener.printMessage("Player 2 won the match");
    }
  }

}
TOP

Related Classes of pong.server.model.ServerModel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.