package net.sf.nebulacards;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.net.Socket;
import net.sf.nebulacards.comm.CommThread;
import net.sf.nebulacards.comm.PacketListener;
import net.sf.nebulacards.comm.netpkt.AcceptPkt;
import net.sf.nebulacards.comm.netpkt.BidPkt;
import net.sf.nebulacards.comm.netpkt.BootPkt;
import net.sf.nebulacards.comm.netpkt.ChatPkt;
import net.sf.nebulacards.comm.netpkt.DealPkt;
import net.sf.nebulacards.comm.netpkt.EndHandPkt;
import net.sf.nebulacards.comm.netpkt.GameNamePkt;
import net.sf.nebulacards.comm.netpkt.JoinPkt;
import net.sf.nebulacards.comm.netpkt.LeavePkt;
import net.sf.nebulacards.comm.netpkt.PassNotifyPkt;
import net.sf.nebulacards.comm.netpkt.PassPkt;
import net.sf.nebulacards.comm.netpkt.PlayPkt;
import net.sf.nebulacards.comm.netpkt.PositionPkt;
import net.sf.nebulacards.comm.netpkt.QueryPkt;
import net.sf.nebulacards.comm.netpkt.RejectPkt;
import net.sf.nebulacards.comm.netpkt.ResponsePkt;
import net.sf.nebulacards.comm.netpkt.SofarPkt;
import net.sf.nebulacards.comm.netpkt.TrickPkt;
import net.sf.nebulacards.comm.netpkt.TrumpPkt;
import net.sf.nebulacards.comm.netpkt.YourTurnPkt;
import net.sf.nebulacards.main.GameResult;
import net.sf.nebulacards.main.NebulaUI;
import net.sf.nebulacards.main.PileOfCards;
import net.sf.nebulacards.main.Player;
import net.sf.nebulacards.main.PlayingCard;
import net.sf.nebulacards.main.UIListener;
import net.sf.nebulacards.util.Queue;
/**
* Client class for Nebula Cards.
*
* @author James Ranson
*/
public class Client extends Thread implements PacketListener {
public static final boolean VERBOSE = true;
private String[] args;
private CommThread com;
private boolean inGame = false;
private boolean ourBid = false;
private boolean ourPlay = false;
private boolean ourPass = false;
private Queue incoming;
public static void main(String[] args) throws Exception {
Client c = new Client(args);
c.start();
}
/**
* Check if a UI uses the old polling scheme. That is, check for the
* following methods: getPass(), getBid(), getChat(), getPlay(), ready().
*
* @param ui
* Some class that should have its methods checked.
* @return true if the class contains a forbidden method, false otherwise.
*/
public static boolean isPollingScheme(Class ui) {
String[] s_forbidden = { "getPass", "getBid", "getChat", "getPlay",
"ready" };
java.util.Vector v_forbidden = new java.util.Vector(s_forbidden.length);
for (int i = 0; i < s_forbidden.length; i++)
v_forbidden.addElement(s_forbidden[i]);
Method[] uiMethods = ui.getMethods();
for (int i = 0; i < uiMethods.length; i++)
if (v_forbidden.contains(uiMethods[i].getName()))
return true;
return false;
}
public Client(String[] cmdline) {
args = (String[]) cmdline.clone();
incoming = new Queue();
}
public void run() {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String pname = null, portString = null, ifString = null, hostString = null;
// parse command line
for (int i = 0; i < args.length; i++) {
if (args[i].equals("-p")) {
try {
portString = args[++i];
} catch (Exception e) {
portString = null;
}
continue;
}
if (args[i].equals("-h")) {
try {
hostString = args[++i];
} catch (Exception e) {
hostString = null;
}
continue;
}
if (args[i].equals("-n")) {
try {
pname = args[++i];
} catch (Exception e) {
pname = null;
}
continue;
}
if (args[i].equals("-i")) {
try {
ifString = args[++i];
} catch (Exception e) {
ifString = null;
}
continue;
}
}
// get player's name
if (pname == null) {
System.err.print("Player's Name: ");
System.err.flush();
try {
pname = in.readLine();
} catch (IOException e) {
}
}
if (VERBOSE)
System.out.println("Hello, " + pname + ".");
Thread.yield();
// get interface
if (ifString == null) {
// FIXME command-line parameters
try {
ifString = in.readLine();
} catch (IOException e) {}
}
Class UI = null;
try {
UI = Class.forName(ifString);
} catch (ClassNotFoundException e) {
System.err.println("Error: Cannot open interface: " + args[1]);
try {
Thread.sleep(3000);
} catch (InterruptedException f) {}
return;
}
if (isPollingScheme(UI)) {
System.err.println("WARNING: The interface you selected uses\n"
+ " the old polling scheme. It may not\n"
+ " work with this version of Nebula Cards.");
}
if (hostString == null) {
System.err
.println("\nPlease enter the host name or IP address of the server that will");
System.err
.println("host this game. (If the server is on the same machine as this client,");
System.err.print("then leave this blank.)\n\nServer: ");
System.err.flush();
try {
hostString = in.readLine();
} catch (Exception e) {
}
if (hostString.equals(""))
hostString = "127.0.0.1";
}
if (portString == null) {
System.err.print("Server's listening port: ");
System.err.flush();
try {
portString = in.readLine();
} catch (Exception e) {
}
}
int port = Integer.parseInt(portString);
try {
Socket syncSock;
syncSock = new Socket(hostString, port);
if (VERBOSE)
System.out.println("Connected on port " + syncSock.getPort());
com = new CommThread(syncSock, 0);
com.addPacketListener(this);
com.start();
} catch (Exception e) {
System.err.println("Fatal Error while connecting to server:");
System.err.println(e.getMessage());
return;
}
try {
com.send(new JoinPkt(pname));
inGame = true;
} catch (Exception e) {
System.err.println("Fatal Error in client: " + e.getMessage());
e.printStackTrace(System.err);
return;
}
try {
NebulaUI iface = (NebulaUI) UI.newInstance();
iface.setCallbacks(new MyUIListener());
doGame(iface);
} catch (Exception e) {
System.err.println("Unexpected Error in client: " + e.getMessage());
e.printStackTrace();
}
com.end();
}
private void doGame(NebulaUI iface) throws Exception {
while (inGame) {
synchronized (this) {
while (inGame && incoming.empty()) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
}
if (!incoming.empty()) {
Object o = incoming.dequeue(); // try to read data
// System.out.println( "Client received: " + o );
// replace with a mapping
if (o instanceof ChatPkt) {
iface.chat(((ChatPkt) o).getMessage());
} else if (o instanceof PositionPkt) {
iface.setPosition(((PositionPkt) o).getPosition());
} else if (o instanceof YourTurnPkt) {
if (((YourTurnPkt) o).isBid()) {
ourBid = true;
iface.yourTurnToBid();
} else {
ourPlay = true;
iface.yourTurn();
}
} else if (o instanceof PassNotifyPkt) {
ourPass = true;
iface.yourTurnToPass(((PassNotifyPkt) o).getHowMany(),
((PassNotifyPkt) o).getWho());
} else if (o instanceof Player[]) {
iface.setPlayers((Player[]) o);
} else if (o instanceof DealPkt) {
iface.dealHand(((DealPkt) o).getCards());
} else if (o instanceof AcceptPkt) {
iface.accepted();
} else if (o instanceof RejectPkt) {
iface.rejected();
} else if (o instanceof TrumpPkt) {
iface.setTrump(((TrumpPkt) o).getTrump(), ((TrumpPkt) o)
.getTrumpName());
} else if (o instanceof GameNamePkt) {
iface.setGameName(((GameNamePkt) o).getName());
} else if (o instanceof GameResult) {
iface.endGame((GameResult) o);
} else if (o instanceof TrickPkt) {
iface.clearTableau(((TrickPkt) o).getWho());
} else if (o instanceof PlayPkt) {
iface.cardToTableau(((PlayPkt) o).getWho(), ((PlayPkt) o)
.getCard());
} else if (o instanceof BidPkt) {
BidPkt bp = (BidPkt) o;
iface.setBid(bp.getWho(), bp.getBid());
} else if (o instanceof SofarPkt) {
iface.playedSoFar(((SofarPkt) o).getCards());
} else if (o instanceof BootPkt) {
inGame = false;
iface.booted(((BootPkt) o).getMessage());
return;
} else if (o instanceof EndHandPkt) {
iface.endHand();
} else if (o instanceof QueryPkt) {
iface.respond(((QueryPkt) o).toString());
} else // unrecognized packets get sent to miscInfo
{
iface.miscInfo(o);
}
}
}
return;
}
// satisfy PacketListener interface
public synchronized void packetReceived(int id, Object o) {
if (id != 0)
return;
incoming.enqueue(o);
notifyAll();
}
public synchronized void packetStreamClosed(int id) {
if (id != 0)
return;
inGame = false;
notifyAll();
}
/* UIListener methods */
class MyUIListener implements UIListener {
public void wantToQuit() {
com.send(new LeavePkt());
inGame = false;
synchronized (Client.this) {
Client.this.notifyAll();
}
}
public void submitChat(String msg) {
com.send(new ChatPkt(msg));
}
public boolean submitBid(int bid) {
// TODO check synchronization
if (ourBid) {
com.send(new BidPkt(bid, 0));
ourBid = false;
return true;
} else
return false;
}
public boolean submitPlay(PlayingCard play) {
// TODO check synchronization
if (ourPlay) {
com.send(new PlayPkt(play, 0));
ourPlay = false;
return true;
} else
return false;
}
public boolean submitPass(PileOfCards pass) {
// TODO check synchronization
if (ourPass) {
com.send(new PassPkt(pass));
ourPass = false;
return true;
} else
return false;
}
public void submitResponse(String r) {
com.send(new ResponsePkt(r));
}
}
}