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

import java.io.IOException;
import java.io.DataInputStream;
import java.io.DataOutputStream;

import org.json.JSONObject;

import card.Card;
import card.CardInfo;
import player.BasePlayer;

import org.json.JSONArray;
import org.json.JSONException;

import java.util.Arrays;
import java.util.List;

public class VibeDumb implements Communicator {
	public static final byte[] MAGIC = new byte[]{ 1, 3, 3, 7 };

	private Connector conn;
	private DataInputStream inStream;
	private DataOutputStream outStream;

	public VibeDumb(Connector c) {
		conn = c;
	}

	public void sendMsg(Message m) throws IOException {
		if(outStream == null) {
			outStream = conn.getOutputStream();
		}
		byte version = 0;
		byte msgType = m.getMsgType();
		byte[] unused = new byte[]{ 0, 0 };
		outStream.write(MAGIC);
		outStream.write(version);
		outStream.write(msgType);
		outStream.write(unused);
		if(msgType == Message.MSG_MOVE) outStream.writeUTF(moveToJsonString(m.getMove()));
		else if(msgType == Message.MSG_CONNECTION_VALID) outStream.writeInt(m.getOwnId());
	}

	public Message receiveMsg() throws IOException {
		if(inStream == null) {
			inStream = conn.getInputStream();
		}
		byte[] magic = new byte[4];
		byte version = 0;
		byte msgType = 0;
		byte[] unused = new byte[2];
		inStream.read(magic);
		if(!isMagicCorrect(magic)) {
			throw new InternalHanabiError("Malformed message (wrong magic number).");
		}
		version = inStream.readByte();
		switch(version) {
		case 0: // TODO magic number?
			break;
		default:
			throw new InternalHanabiError("Version " + version + " of protocol doesn't exist.");
		}
		msgType = inStream.readByte();
		inStream.read(unused);
		Message m = new Message(msgType);
		if(msgType == Message.MSG_MOVE) {
			String msg = inStream.readUTF(); // TODO ddos?
			if(msg == null) throw new InternalHanabiError("Purported move doesn't exist.");
			try {
				m.setMove(jsonToMove(new JSONObject(msg)));
			} catch(JSONException ex) {
				throw new InternalHanabiError(ex);
			}
		} else if(msgType == Message.MSG_CONNECTION_VALID) {
			m.setOwnId(inStream.readInt());
		}
		return m;
	}
	
	private static String moveToJsonString(Move move) {
		try {
			JSONObject jo = new JSONObject();
			byte moveType = (byte) move.getMoveType();
			jo.put(Keys.VibeDumb.MOVE_TYPE, moveType);
			// TODO: dry moves
			switch(moveType) {
			case Move.HINT:
				int type = move.getHintType();
				jo.put(Keys.VibeDumb.HINT_TYPE, type);
				if(type == Move.HINT_COLOR) {
					jo.put(Keys.VibeDumb.COLOR, move.getColor());
				} else if(type == Move.HINT_VALUE) {
					jo.put(Keys.VibeDumb.VALUE, move.getValue());
				} else {
					throw new InternalHanabiError("Unknown hint type.");
				}
				break;
			case Move.PLACE_CARD:
			case Move.TRASH_CARD:
				jo.put(Keys.VibeDumb.COLOR, move.getColor());
				jo.put(Keys.VibeDumb.VALUE, move.getValue());
				break;
			default:
				throw new InternalHanabiError("Move is neither hint nor place nor trash.");
			}
			return jo.toString();
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
	}
	
	private static Move jsonToMove(JSONObject jo) {
		try {
			byte moveType = (byte)jo.getInt(Keys.VibeDumb.MOVE_TYPE);
			Move move = new Move(moveType);
			// TODO: dry moves
			switch(moveType) {
			case Move.HINT:
				int type = jo.getInt(Keys.VibeDumb.HINT_TYPE);
				move.setHintType(type);
				if(type == Move.HINT_COLOR) {
					move.setColor(jo.getInt(Keys.VibeDumb.COLOR));
				} else if(type == Move.HINT_VALUE) {
					move.setValue(jo.getInt(Keys.VibeDumb.VALUE));
				} else {
					throw new InternalHanabiError("Unknown hint type.");
				}
				break;
			case Move.PLACE_CARD:
			case Move.TRASH_CARD:
				move.setColor(jo.getInt(Keys.VibeDumb.COLOR));
				move.setValue(jo.getInt(Keys.VibeDumb.VALUE));
				break;
			default:
				throw new InternalHanabiError("Move is neither hint nor place nor trash.");
			}
			return move;
		} catch(JSONException ex) {
			throw new InternalHanabiError(ex);
		}
	}
	
	public void establish() throws IOException {
		// FIXME
		conn.establishConnection();
	}
	private static boolean isMagicCorrect(byte[] magic) {
		return Arrays.equals(MAGIC, magic);
	}

	@Override
	public Connector getConnector() {
		return conn;
	}
	
	protected JSONObject serverGameToJson(ServerGame sg) throws JSONException {
		JSONObject root = new JSONObject();
		root.put("nCardsInDeck", sg.nCardsInDeck);
		root.put("hints", sg.hints);
		root.put("nFlashs", sg.flashs);
		root.put("movesLeft", sg.movesLeft);
		JSONArray trashArray = new JSONArray();
		for(Card c : sg.trash){
			trashArray.put(cardToJson(c));
		}
		root.put("trash", trashArray);

		JSONObject deckRow = new JSONObject();
		for(int i=0; i<sg.playingDeck.length; ++i){
			JSONObject deckCol = new JSONObject();
			for(int j=0; j<sg.playingDeck[i].length; ++j){
				deckCol.put(String.valueOf(j), cardToJson(sg.playingDeck[i][j]));
				//else deckCol.put(String.valueOf(j), new JSONObject());
			}
			deckRow.put(String.valueOf(i), deckCol);
		}
		root.put("playingDeck",  deckRow);

		JSONObject deckCounterObj = new JSONObject();
		for(int i=0; i<sg.playingDeckCounter.length; ++i){
			deckCounterObj.put(String.valueOf(i), sg.playingDeckCounter[i]);
		}
		root.put("playingDeckCounter", deckCounterObj);

		JSONArray playerArray = new JSONArray();
		for(BasePlayer p : sg.players){
			playerArray.put(p.toJson());
		}
		root.put("players", playerArray);
		root.put("currentPlayer",  sg.currentPlayer);
		root.put("nPlayers", sg.nPlayers);
		return root;
	}

	public JSONObject cardToJson(Card c) throws JSONException {
		// TODO: handle dummy card
		JSONObject root = new JSONObject();
		root.put("value", c.getValue());
		root.put("id", c.getId());
		root.put("color", c.getColor());
		root.put("cardInfos", cardInfosToJson(c));
		return root;
	}

	private JSONArray cardInfosToJson(Card c) throws JSONException {
		JSONArray infoArray = new JSONArray();
		List<CardInfo> cardInfos = c.getCardInfos();
		if(cardInfos == null) return infoArray;
		for(CardInfo ci : cardInfos){
			infoArray.put(cardInfoToJson(ci));
		}
		return infoArray;
	}

	public JSONObject cardInfoToJson(CardInfo ci) throws JSONException {
		JSONObject root = new JSONObject();
		root.put("type", ci.getType());
		root.put("is", ci.isIs());
		root.put("what", ci.getWhat());
		root.put("from", ci.getFrom());
		return root;
	}

//	public Message jsonToMsg(JSONObject jo) throws JSONException {
//		this.jo = jo; // TODO: clone?
//		moveType = (byte)jo.getInt("msgType");
//	}
}