package pong.client.model;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import pong.common.Debugger;
import pong.common.Message;
import pong.common.Position;
/**
* This class manages the client protocol to communicate with the Pong server.
*
* @author Lorenzo Gatto
*
*/
public class ClientProtocolThread extends Thread {
private Socket socket;
private ObjectOutputStream output;
private ObjectInputStream input;
private final EventsHandler eventsHandler;
private final String hostName;
private final int port;
/**
* Class constructor.
*
* @param eventsHander
* the handler of network events
* @param host
* the server host name
* @param port
* the port number of the server
*/
public ClientProtocolThread(final EventsHandler eventsHander, final String host, final int port) {
super();
this.eventsHandler = eventsHander;
this.hostName = host;
this.port = port;
}
/**
* This method connects to the server and waits for messages continuosly, sending player
* movement information after receiving object positions.
*/
public synchronized void run() {
try {
this.socket = new Socket(hostName, port);
this.socket.setTcpNoDelay(true);
} catch (IOException | IllegalArgumentException | NullPointerException e) {
this.eventsHandler.connectionFailedEvent();
return;
}
assert this.socket != null;
try {
OutputStream o;
InputStream i;
o = this.socket.getOutputStream();
i = this.socket.getInputStream();
this.output = new ObjectOutputStream(o);
this.input = new ObjectInputStream(i);
} catch (IOException e) {
if (!this.isInterrupted()) {
onServerDisconnect();
}
return;
}
this.eventsHandler.connectionSuccessfullEvent();
while (!this.isInterrupted()) {
getMessage();
}
}
/**
* Disconnects the client from the server.
*/
public void disconnect() {
interrupt();
try {
this.socket.close();
} catch (IOException e) {
return;
}
}
/**
* This method gets a message from the server and performs an appropriate action
*/
private void getMessage() {
Message message = null;
try {
message = (Message) input.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.exit(1);
} catch (IOException e) {
Debugger.printLine("Error receiving message");
if (!this.isInterrupted()) {
onServerDisconnect();
}
return;
}
Debugger.printLine(message.getMessageType().name() + " message received" + " time "
+ System.currentTimeMillis());
if (message.getMessageType() == Message.Type.POSITIONS) {
this.eventsHandler.objectPositionsReceivedEvent((Position[]) message.getContent());
sendMovementInformation();
} else if (message.getMessageType() == Message.Type.MATCH_BEGINS) {
final Integer roundsNumber = (Integer) message.getContent();
this.eventsHandler.matchBeginsEvent(roundsNumber);
} else if (message.getMessageType() == Message.Type.ROUND_BEGINS) {
this.eventsHandler.roundBeginsEvent();
} else if (message.getMessageType() == Message.Type.ROUND_END) {
final Boolean won = (Boolean) message.getContent();
this.eventsHandler.roundEndEvent(won);
} else if (message.getMessageType() == Message.Type.OTHER_DISCONNECTED) {
this.eventsHandler.otherDisconnectedWhilePlayingEvent();
} else if (message.getMessageType() == Message.Type.YOUR_PLAYER_INDEX) {
final Integer playerIndex = (Integer) message.getContent();
this.eventsHandler.playerIndexReceivedEvent(playerIndex);
}
assert message.getMessageType() == Message.Type.ARE_YOU_ALIVE;
}
/**
* This method sends information about player movement
*/
private void sendMovementInformation() {
final Message message = new Message(Message.Type.MOVEMENT,
this.eventsHandler.movementInformationRequestedEvent());
try {
this.output.writeObject(message);
this.output.reset();
} catch (IOException e) {
if (!this.isInterrupted()) {
onServerDisconnect();
}
Debugger.printLine("Error sending message");
}
}
/**
* This method tells the model that the server disconnected, interrupting the current thread
*/
private void onServerDisconnect() {
this.eventsHandler.serverDisconnectedEvent();
interrupt();
}
}