package ipcards.net;
import ipcards.events.*;
import ipcards.*;
import ipcards.Player;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
/**
* Represents a client connected to the server.
*
* @author Ricky Vincent
*/
public class Client implements Runnable, Comparable<Client> {
private static final String END_LINE = "\n";
private Socket socket;
private DataOutputStream out;
private BufferedReader in;
private boolean connected = false;
private boolean isturn = false;
private int id;
private Table table;
private ServerModel model;
private CardsServer cardsserver;
public Player thisplayer;
/**
* Constructs a new <code>Client</code> object with a <code>Socket</code>.
*
* @param socket The <code>Socket</code> to bind to this <code>Client</code>.
* @param table The {@link Table} this client is at.
*/
public Client (Socket socket, Table table, CardsServer cardsserver) {
this.cardsserver = cardsserver;
this.socket = socket;
this.table = table;
model = new ServerModel(table, this);
thisplayer = new Player("new");
thisplayer.setClient(this);
try {
out = new DataOutputStream(socket.getOutputStream());
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
connected = true;
//send cards initially
//TODO use full sync
model.playerActionPerformed(new PlayerAction(this, PlayerAction.SYNC_LOCATIONS));
} catch (IOException e) {
System.err.println("Could not connect to client");
}
}
public int getID() {
return id;
}
public ServerModel getServerModel() {
return model;
}
/**
* Start this thread
*/
public void run() {
receiveData();
}
public void myTurn(boolean ismyturn) {
isturn = ismyturn;
}
/**
* Receives data from the <code>Socket</code>. Blocks until data is received, or <code>disconnect</code> is called.
*/
private void receiveData() {
String input = null;
while (connected) {
try {
input = in.readLine();
//If it is not the players turn, only allow these commands, if it is then allow everything
if(isturn || input.charAt(0) == PlayerAction.SYNC_LOCATIONS
|| input.charAt(0) == PlayerAction.HOLD
|| input.charAt(0) == PlayerAction.LOCAL_FLIP) {
commandHandler(input);
}
if(input.charAt(0) == PlayerAction.NEW_GAME && input.charAt(1) == PlayerAction.NEW_GAME){
commandHandler(input);
}
} catch (IOException e) {
System.err.println("Lost connection to client... retrying");
try {
//sleep for 1 second between reconnection attempts
Thread.sleep(1000);
} catch (InterruptedException e1) {
continue;
}
}
}
}
/**
* Handles commands. Creates a PlayerAction object and passes it to the ServerModel.
*
* @param command the command to handle.
*/
private void commandHandler (String command) {
Dbg.serverLog(command);
PlayerAction newAction = new PlayerAction(this, command.charAt(0), command.substring(1));
model.playerActionPerformed(newAction);
}
/**
* Writes bytes directly to the <code>Socket</code>.
*
* @param data the array of bytes to write.
* @throws IOException
*/
private void sendData (byte[] data) throws IOException {
out.write(data);
out.flush();
}
/**
* Sends a command to this <code>Client</code>
*
* @param command The command to send from {@link PlayerAction}.
* @param args The arguments to send with the command.
* @throws IOException
*/
public void sendCommand(char command, String args) {
args = command + args + END_LINE;
try {
sendData(args.getBytes());
} catch (IOException e) {
connected = false;
e.printStackTrace();
}
}
/**
* Sends a command to all connected clients
* @throws IOException
*/
public void sendCommandToAll (char command, String args) {
for(Client client: CardsServer.clients) {
client.sendCommand(command, args);
}
}
/**
* Sends a command to all connected clients except the one who sent it.
* @throws IOException
*/
public void sendCommandToAllX (char command, String args) {
for(Client client: CardsServer.clients) {
if(client != this)
client.sendCommand(command, args);
}
}
/**
* @return the <code>Socket</code>
*/
public Socket getSocket() {
return socket;
}
/**
* Disconnects this client.
*/
public void disconnect() {
connected = false;
CardsServer.clients.remove(this);
}
/**
* @return the table
*/
public Table getTable() {
return table;
}
/**
* @param table the table to set
*/
public void setTable(Table table) {
this.table = table;
}
/**
* Ends the current players turn
*/
public void endTurn() {
if(isturn) {
myTurn(false);
cardsserver.nextTurn();
}
}
/**
* Ends the current players turn, sets turn to the previous player
*/
public void endTurnReverse(){
if(isturn) {
myTurn(false);
cardsserver.nextTurnReverse();
}
}
public void resetLocalFlip() {
model.resetLocalFlip();
}
public int compareTo(Client client) {
return (client.socket.hashCode()) - (socket.hashCode());
}
}
|