Newer
Older
hanabi-networking / src / server / ClientGame.java
package server;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;

import card.Card;

public abstract class ClientGame extends BaseGame {

	public enum MSG_TYPES {ERROR, WARNING, INFORMATION, QUESTION};

	//Options stuff
	final File options = new File("options.json");
	JSONObject optionsRoot;
	int difficulty;
	int fontSize;

//	Socket socket;
//	int port;
//	String hostName;
	Client client;
	volatile boolean isConnected;
	volatile boolean initGameReceived;
	volatile int ownId; // TODO lock?

	public ClientGame(Communicator comm) throws IOException{
		preinit(comm);
	}
	
	protected void preinit(Communicator comm) throws IOException{
		client = new Client(this, comm, -1);
		client.comm.establish();
	}

	@Override
	protected void init() {
		loadOptions();
		loadFont();
		super.init();
	}

	@Override
	protected void createHistory() {
		history = new ArrayList<>();
	}

	@Override
	protected void createTrash() {
		trash = new ArrayList<>();
	}

	protected void sendReady() throws IOException {
		try {
			JSONObject jo = new JSONObject();
			jo.put("origin", ownId);
			Message m = new Message(jo);
			m.setMsgType(Message.MSG_READY);
			send(m);
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
	}

	protected void sendUnReady() throws IOException {
		try {
			JSONObject jo = new JSONObject();
			jo.put("origin", ownId);
			Message m = new Message(jo);
			m.setMsgType(Message.MSG_UNREADY);
			send(m);
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
	}
	
	protected void loadOptions(){
		try {
			Scanner optionsScanner = new Scanner(options, "UTF-8");
			JSONTokener tokener = new JSONTokener(optionsScanner.useDelimiter("\\A").next());
			optionsScanner.close();
			optionsRoot = new JSONObject(tokener);
			difficulty = optionsRoot.getInt("difficulty");
			fontSize = optionsRoot.getInt("fontSize");
			if(difficulty < 0) difficulty = 0;
			if(fontSize <= 0) fontSize = 20;
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	protected void movePlaceCard(Card c) throws IOException{
		movePlaceCard(c, false);
	}

	protected void movePlaceCard(Card c, boolean isDry) throws IOException {
		byte msgType = isDry ? Message.PLACE_CARD | Message.DRY : Message.PLACE_CARD;
		send(msgType, c);
//		send(msgType, c.toJson().toString());
	}

	protected void moveTrashCard(Card c) throws IOException{
		moveTrashCard(c, false);
	}

	protected void moveTrashCard(Card c, boolean isDry) throws IOException {
		byte msgType = isDry ? Message.TRASH_CARD | Message.DRY : Message.TRASH_CARD;
		send(msgType, c);
//		send(msgType, c.toJson().toString());
	}

	protected void moveHint(boolean isValue, int value, int playerId) throws IOException {
		moveHint(isValue, value, playerId, false);
	}

	protected void moveHint(boolean isValue, int value, int playerId, boolean isDry) throws IOException {
		byte msgType = isDry ? Message.HINT|Message.DRY : Message.HINT;
		JSONObject msg = new JSONObject();
		try {
			if(isValue){
				msg.put("type", Message.HINT_VALUE);
			} else {
				msg.put("type",  Message.HINT_COLOR);
			}
			msg.put("value", value);
			msg.put("playerId", playerId);
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
		send(msgType, msg);
//		send(msgType, msg.toString());
	}

	protected void moveHint(Card c) throws IOException {
		moveHint(c, false);
	}

	protected void moveHint(Card c, boolean isDry) throws IOException {
		byte msgType = isDry ? Message.HINT|Message.DRY : Message.HINT;
		send(msgType, c);
//		send(msgType, c.toJson().toString());
	}

	private void send(byte msgType, Card c) throws IOException {
		try {
			send(msgType, c.toJson());
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
	}
	private void send(byte msgType, JSONObject jo) throws IOException {
		try {
			jo.put("origin", ownId);
			Message m = new Message(jo);
			m.setMsgType(msgType);
			send(m);
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
	}
	
	private void send(Message m) throws IOException {
		client.comm.sendMsg(m);
	}
	
//	private void send(byte msgType, String msg) {
//		Utils.log(Utils.VERBOSE, "Client - Sending to server");
//		Utils.log(Utils.VERBOSE, "sending " + msg);
//		DataOutputStream os = null;
//		try {
//			os = new DataOutputStream(socket.getOutputStream());
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		try {
//			os.write(Move.MAGIC);
//			os.write(ServerGame.PROTO_VIBE_DUMB);
//			os.write(msgType);
//			os.write(new byte[]{ 0, 0 });
//			os.writeUTF(msg);
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//	}

	//public abstract void printMessage(String msg, MSG_TYPES type);


	protected abstract void createUi();
	protected abstract void updateUi();
	
	public int getDifficulty() {
		return difficulty;
	}

	public abstract void loadFont();

	public int getFontSize() {
		return fontSize;
	}

	public Object getFont() {
		// TODO
		return null;
	}

//	@Override
	protected void receive(DataInputStream inStream) {
//		Utils.log(Utils.VERBOSE, "Client  - Received data!");
//		byte[] magic = new byte[4];
//		byte proto = 0;
//		byte msgType = 0;
//		byte[] useless = new byte[2];
//		try {
//			inStream.read(magic);
//			proto = inStream.readByte();
//			msgType = inStream.readByte();
//			inStream.read(useless);
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//
//		Utils.log(Utils.VERBOSE, "Received magic: " + magic[0] + magic[1] + magic[2] + magic[3]);
//		Utils.log(Utils.VERBOSE, "Received proto: " + proto);
//		Utils.log(Utils.VERBOSE, "Received msgType: " + msgType);
//		Utils.log(Utils.VERBOSE, "Received useless: " + useless[0] + useless[1]);

		// TODO check Protocol
//		switch(proto){
//		case VIBE_DUMB:
//			break;
//		}

//		String msg = null;
//		JSONObject jo = null;
//		try {
//			msg = inStream.readUTF();
//		} catch (IOException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		// TODO error handling
//		if(msg != null){
//			Utils.log(Utils.VERBOSE, "The not null msg is: " + msg);
//			jo = new JSONObject(msg);
//		}
//		else return;
		// TODO MOVE_DRY
//		switch(msgType){
//		case CONNECTION_VALID:
//			isConnected = true;
//			ownId = jo.getInt("id");
//			break;
//		case Move.UPDATE:
//			Utils.log(Utils.VERBOSE, "received update");
//			if(!initGameReceived){
//				Utils.log(Utils.VERBOSE, "first update!");
//				init();
//				updateGame(jo);
//				createUi();
//				initGameReceived = true;
//				break;
//			}
//			updateGame(jo);
//			updateUi();
//			break;
//		}
	}

	protected int getPlayersSightId(int id) {
		// TODO get the id of a player with ownId as 0-based index
		return (id + (nPlayers - ownId)) % nPlayers;
	}

	protected int getRealId(int id) {
		// TODO get the real id of a player
		return (id + ownId) % nPlayers;
	}

	private void updateGame(Message m) {
		try {
			updateGame(m.getJson());
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
	}
	
	private void updateGame(JSONObject jo) throws JSONException {
		nPlayers = jo.getInt("nPlayers");
		if(!initGameReceived){
			createPlayers();
		}
		// TODO movesLeft = jo.getInt("movesLeft");
		nCardsInDeck = jo.getInt("nCardsInDeck");
		hints = jo.getInt("hints");
		flashs = jo.getInt("nFlashs");
		JSONArray trashArray = jo.getJSONArray("trash");
		trash.clear(); // TODO depends on protocol version
		for(int i=0; i<trashArray.length(); ++i){
			JSONObject cardObject = trashArray.getJSONObject(i);
			Card card = new Card(new Message(cardObject));
			trash.add(card);
		}

		JSONObject deckRow = jo.getJSONObject("playingDeck");
		for(int i=0; i<playingDeck.length; ++i){
			JSONObject deckCol = deckRow.getJSONObject(String.valueOf(i));
			for(int j=0; j<playingDeck[i].length; ++j){
				playingDeck[i][j] = new Card(new Message(deckCol.getJSONObject(String.valueOf(j))));
			}
		}

		JSONObject deckCounterObj = jo.getJSONObject("playingDeckCounter");
		for(int i=0; i<playingDeckCounter.length; ++i){
			playingDeckCounter[i] = deckCounterObj.getInt(String.valueOf(i));
		}

		JSONArray playerArray = jo.getJSONArray("players");
		for(int i=0; i<players.length; ++i){
			if(players[i] == null) players[i] = createPlayer(new Message(playerArray.getJSONObject(i)));
			players[i].setCards(playerArray.getJSONObject(i).getJSONArray("cards"));
			
		}
		currentPlayer = jo.getInt("currentPlayer");
	}
	
	protected void processMsg(Message m) {
//		while(!isConnected){
//			Utils.sleep(1000);
//			Utils.log(Utils.VERBOSE, "Waiting for connection to host");
//		}
		Utils.log(Utils.VERBOSE, client.comm.getConnector().toString());
		while(!initGameReceived){
			Utils.sleep(1000);
			Utils.log(Utils.VERBOSE, "Client " + ownId + " waiting for game...");
		}
		switch(m.getMsgType()){
		case Message.MSG_CONNECTION_VALID:
			isConnected = true;
			ownId = m.getOwnId();
			break;
		case Message.UPDATE:
			Utils.log(Utils.VERBOSE, "received update");
			if(!initGameReceived){
				Utils.log(Utils.VERBOSE, "first update!");
				init();
				updateGame(m);
				createUi();
				initGameReceived = true;
				break;
			}
			updateGame(m);
			updateUi();
			break;
		}
		
		updateGame(m);
	}
}