public class PES6JWebHandler implements HttpHandler {
	ChannelList channels;
	
	public PES6JWebHandler( ChannelList channels ) {
		this.channels = channels;
	}
	
	public Map<String, String> getQueryMap(String query)  
	{  
	     String[] params = query.split("&");  
	     Map<String, String> map = new HashMap<String, String>();  
	     for (String param : params)  
	     {  
	         String name = param.split("=")[0];  
	         String value = param.split("=")[1];  
	         map.put(name, value);  
	     }  
	     return map;
	}  
	
    public void handle(HttpExchange t) throws IOException {
//        InputStream is = t.getRequestBody();
//        read( is );
    	String response = "";
    	URI uri = t.getRequestURI();
    	Map params = getQueryMap(uri.getQuery());
    	if( params.containsKey("cmd") ) {
    		String cmd = (String)params.get("cmd");
    		if( cmd == null ) {
    			response = "ERROR! no command specified";
    			sendOutput( t, response );
    			return;
    		}
    		if( cmd.equals( "getNumPlayers")) {
    			response = "" + channels.getNumPlayers();
    			sendOutput( t, response );
    			return;
    		}
    		if( cmd.equals( "getLobbyList")) {
    			for( int i = 0; i < channels.size(); i++ ) {
    				response += i + "|" + new Integer(channels.getChannel(i).getType()) + "|" + channels.getChannel(i).getName() + "|" +
    					channels.getChannel(i).getPlayerList().getNumPlayers() + "\n";
    			}
    			sendOutput( t, response );
    			return;
    		}
    		if( cmd.equals( "getPlayerList")) {
    			if( params.containsKey("lobby")) {
    				int lobbyId = Integer.parseInt((String)params.get("lobby"));
    				if( lobbyId >= channels.size() ) {
    					response += "That lobby doesn't exist!";
    				}
    				PlayerList pl = channels.getChannel(lobbyId).getPlayerList();
    				for( int i = 0; i < pl.getNumPlayers(); i++ ) {
    					PlayerInfo p = pl.getPlayer(i);
    					response += i + "|" + buildPlayerInfoOutput( p ) + "\n";
    				}
    			} else {
    				int count = 0;
    				for( int j = 0; j < channels.size(); j++ ) {
    					PlayerList pl = channels.getChannel(j).getPlayerList();
        				for( int i = 0; i < pl.getNumPlayers(); i++ ) {
        					PlayerInfo p = pl.getPlayer(i);
        					response += count + "|" + buildPlayerInfoOutput( p ) + "\n";
        					count++;
        				}
    				}
    			}
    		}
    	}
        sendOutput( t, response );
    }

    public String buildPlayerInfoOutput( PlayerInfo p ) {
    	return( p.getId() + "|" + p.getName() + "|" + p.getGid() + "|" + p.getGroupName() +
				"|" + p.getCategory() + "|" + p.getDivision() );
    }
    
    public void sendOutput(HttpExchange t, String output) throws IOException {
    	t.sendResponseHeaders(200, output.length());
        OutputStream os = t.getResponseBody();
        os.write(output.getBytes());
        os.close();
    }
} public class Db implements Serializable {
	private Connection conn;
	private Statement stmt;
	private ResultSet rset;
	private Properties properties;
	private int dbType = -1;
	private int isolationLevel;

	public static int ISO_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;

	public static final String DbTypeNames[] = new String[] {
			"MYSQL", "POSTGRES" //$NON-NLS-1$ //$NON-NLS-2$
	};

	public static final int TYPE_MYSQL = 0;
	public static final int TYPE_POSTGRES = 1;

	public Db(Properties prop) {
		this.properties = prop;

		String type = properties.getProperty("db-type"); //$NON-NLS-1$

		if (type == null) {
			dbType = -1;
			return;
		}

		if (type.toUpperCase().equals(DbTypeNames[TYPE_POSTGRES])) {
			dbType = TYPE_POSTGRES;
		}
		if (type.toUpperCase().equals(DbTypeNames[TYPE_MYSQL])) {
			dbType = TYPE_MYSQL;
		}
	}

	public void connect() throws SQLException {
		switch (dbType) {
		case TYPE_POSTGRES:
			connect_postgres();
			break;
		case TYPE_MYSQL:
			connect_mysql();
			break;
		default:
			throw new SQLException(
					"Incorrect database type, check the properties file"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
	}

	public int getDbType() {
		return (dbType);
	}

	private void connect_postgres() throws SQLException {
		try {
			Class.forName("org.postgresql.Driver"); //$NON-NLS-1$
		} catch (Exception ex) {
			ex.printStackTrace();
			throw new SQLException(ex.getMessage());
		}

		// Connect to the Database

		// conn = DriverManager.getConnection
		// ("jdbc:oracle:thin:@localhost:1521:rinol", "rinoluser", "rinoluser");
		if (conn == null || conn.isClosed()) {
			conn = DriverManager.getConnection("jdbc:postgresql://" //$NON-NLS-1$
					+ properties.getProperty("db-host") //$NON-NLS-1$
					+ "/" //$NON-NLS-1$
					+ properties.getProperty("db-name"), //$NON-NLS-1$
					properties.getProperty("db-username"), //$NON-NLS-1$
					properties.getProperty("db-password")); //$NON-NLS-1$
			isolationLevel = conn.getTransactionIsolation();
		}
	}
	
	private void connect_mysql() throws SQLException {


		// Connect to the Database

		// conn = DriverManager.getConnection
		// ("jdbc:oracle:thin:@localhost:1521:rinol", "rinoluser", "rinoluser");
		if (conn == null || conn.isClosed()) {
			conn =
				DriverManager.getConnection("jdbc:mysql://"+properties.getProperty("db-host")+"/"+
						properties.getProperty("db-name")+"?" +
				"user="+properties.getProperty("db-username")+
				"&password="+properties.getProperty("db-password"));
			isolationLevel = conn.getTransactionIsolation();
		}
	}

	public void disconnect() throws SQLException {
		conn.close();
	}

	public String parseSQL(String raw) {
		String parsed = ""; //$NON-NLS-1$

		return (parsed);
	}

	public PreparedStatement prepareStatement(String query ) throws SQLException {
		PreparedStatement ps = conn.prepareStatement(query/*, Statement.RETURN_GENERATED_KEYS*/);
		ps.clearParameters();
		ps.setEscapeProcessing(true);
		
		return( ps );
	}

	public String getInsertStatement(String table, Row r) {
		String column, columns, values;
		String query = "insert into " + table + " ("; //$NON-NLS-1$ //$NON-NLS-2$
		Enumeration keys = r.keys();
		columns = ""; //$NON-NLS-1$
		while (keys.hasMoreElements()) {
			column = (String) keys.nextElement();
			if (columns.length() > 0)
				columns += ","; //$NON-NLS-1$
			columns += column;
		}
		query += columns + ") values ("; //$NON-NLS-1$
		values = ""; //$NON-NLS-1$
		for (int i = 0; i < r.getColumnCount(); i++) {
			if (values.length() > 0)
				values += ","; //$NON-NLS-1$
			values += "?"; //$NON-NLS-1$
		}
		query += values + ")"; //$NON-NLS-1$
		return query;
	}

	public void setStatementValues(Row r) throws SQLException {
		Enumeration keys = r.keys();
		String column;
		int i = 1;
		while (keys.hasMoreElements()) {
			column = (String) keys.nextElement();
			setStatementValue(null, i++, r.getObject(column));
		}
	}

	public void setStatementValue(PreparedStatement stmt, int idx, Object value) throws SQLException {
		// Codigo para parsear aqui...
		String encoded;
		// System.out.println("Poniendo idx " + idx + "=" + value.toString() );
		if (value instanceof String) {
			((String) value).replaceAll("'", "''"); //$NON-NLS-1$ //$NON-NLS-2$
		}

		/*
		 * Esto es para evitar petes de nullazos en bases de datos oracle 8i
		 */
		// if( value == null ) value = new String( "" );
		try {

			stmt.setObject(idx, value);
		} catch (SQLException e) {
			System.err.println("excep" + e.getMessage()); //$NON-NLS-1$
			e.printStackTrace();
			throw e;
		}

	}

	public void setStatementValues( Object[] values, PreparedStatement stmt) throws SQLException {
		for (int i = 1; i <= values.length; i++) {
			// System.out.println( i-1 + " -> " + values[i-1] ); //$NON-NLS-1$
			setStatementValue(stmt, i, values[i - 1]);
		}
	}

	public void closeStatement(PreparedStatement stmt) throws SQLException {
		stmt.close();
	}

	public int executeUpdate(PreparedStatement stmt) throws SQLException {
		return stmt.executeUpdate();
	}
	
	public Table getKeys(PreparedStatement stmt) throws SQLException {
		ResultSet rset = stmt.getGeneratedKeys(); 
		return( getResults( rset ));
	}

	public Table executeQuery(PreparedStatement stmt) throws SQLException {
		Table t;
		ResultSet rset = stmt.executeQuery();
		t = getResults(rset);
		rset.close();
		return (t);
	}

	private Table getResults(ResultSet rset) throws SQLException {
		Table ret = new Table();

		ResultSetMetaData rsmeta = rset.getMetaData();

		while (rset.next()) {
			Row info = new Row();
			for (int i = 1; i <= rsmeta.getColumnCount(); i++) {
				switch (rsmeta.getColumnType(i)) {
				case Types.FLOAT:
				case Types.DOUBLE:
					info.setFloat(rsmeta.getColumnName(i).toLowerCase(), rset
							.getFloat(i));
					break;
	
				case Types.INTEGER:
				case Types.NUMERIC:
				case Types.BIGINT:

					info.setLong(rsmeta.getColumnName(i).toLowerCase(), rset
							.getLong(i));

					break;
				case Types.BINARY:
				case Types.VARBINARY:
					info.setBytes(rsmeta.getColumnName(i).toLowerCase(), rset
							.getBytes(i));
					break;
				case Types.VARCHAR:
				default:
					info.set(rsmeta.getColumnName(i).toLowerCase(), rset
							.getString(i));
					break;
				}
			}
			ret.put(info);
		}
		return (ret);
	}

	public Table executeQuery(String query) throws SQLException {
		Table ret;

		Statement stmt = conn.createStatement();
		// stmt.setEscapeProcessing( true );
		ResultSet rset = stmt.executeQuery(query);
		ret = getResults(rset);
		rset.close();
		stmt.close();
		return (ret);
	}

	public int executeUpdate(String query) throws SQLException {
		int ret = 0;
		Statement stmt = conn.createStatement();
		stmt.setEscapeProcessing(true);
		stmt.executeUpdate(query);
		ResultSet tmp = stmt.getGeneratedKeys();
		if (tmp.next()) {
			// Retrieve the auto generated key(s).
			ret = tmp.getInt(1);
		}
		stmt.close();
		return ret;
	}

	public void commit() throws SQLException {
		conn.commit();
	}

	public void rollback() throws SQLException {
		conn.rollback();
	}

	public void setAutoCommit(boolean value) throws SQLException {
		conn.setAutoCommit(value);
	}

	public void changeIsolation(int value) throws SQLException {
		conn.setTransactionIsolation(value);
	}

	public void restoreIsolation() throws SQLException {
		conn.setTransactionIsolation(isolationLevel);
	}

	/**
	 * Permite saber si la conexi�n est� abierta
	 * 
	 * @return - true si lo esta y false en otro caso.
	 */
	public boolean isConnected() {
		try {
			return !conn.isClosed();
		} catch (SQLException e) {
			return false;
		}
	}
} public class ChannelInfo {
	byte type;
	String name;
	PlayerList playerList;
	RoomList roomList;
	String continent;
	int id;
	
	public static byte TYPE_MENU = (byte) 0x1F;
	public static byte TYPE_PC_CHANNEL = (byte) 0x5F;
	public static byte TYPE_COMMON_FRIENDLY = (byte) 0x3F; 
	public static byte TYPE_PS2_CHANNEL = (byte) 0X9F;

	public ChannelInfo(byte type, String name, String continentCode ) {
		this.name = name;
		this.type = type;
		this.playerList = new PlayerList();
		this.roomList = new RoomList();
		this.continent = continentCode;
	}

	public void setId( int id ) {
		this.id = id;
	}
	
	public int getId() {
		return( this.id );
	}
	
	public String getName() {
		return (name);
	}

	public byte getType() {
		return (type);
	}
	
	public PlayerList getPlayerList() {
		return( playerList );
	}
	
	public RoomList getRoomList() {
		return( roomList );
	}
	
	public boolean inContinentList( String countryCode ) {
		if( continent == null ) return true;
		return( continent.indexOf( countryCode ) != -1 );
	}
} public class ChannelList {
	Hashtable<Integer,ChannelInfo> channels;
	
	public ChannelList( ) {
		channels = new Hashtable<Integer,ChannelInfo>();
	}
	
	public void addChannel( ChannelInfo ch ) {
		channels.put( ch.getId(), ch );
	}
	
	public ChannelInfo getChannel( int idx ) {
		return( channels.get( idx ));
	}
	
	public int size() {
		return( channels.size() );
	}
	
	public int getNumPlayers() {
		int ret = 0;
		for( int i = 0; i < channels.size(); i++ ) {
			ret += getChannel(i).getPlayerList().getNumPlayers();
		}
		return( ret );
	}
} public class DataCdKeyAndPass {
	byte[] data;

	public DataCdKeyAndPass(byte[] data) {
		this.data = data;
	}

	public byte[] getCdKey() {
		byte[] ret = new byte[32];
		System.arraycopy(data, 0, ret, 0, 32);
		return (ret);
	}
} public class DataChatMessage {
	byte[] data;

	public DataChatMessage(byte[] data) {
		this.data = data;
	}

	/*
	 * 4 bytes anteriores el id del player 4 anteriores FFFFFFFF (probablemente
	 * el id del salon) 2 anteriores 00 01
	 */

	public int getChannel() {
		return( data[1]);
//		int chan_id = Util.word2Int(data[0], data[1]);
//		return (chan_id);
	}
	
	public int getPlace() {
		return( data[0]);
	}

	public String getMessage() {
		return (new String(data, 10, Util.strlen(data, 10)));
	}
} public class DataId {
	byte[] data;

	public DataId(byte[] data) {
		this.data = data;
	}

	public long getId() {
		return (Util.word2Long(data[0], data[1], data[2], data[3]));
	}
} public class DataIp {
	byte[] data;

	public DataIp(byte[] data) {
		this.data = data;
	}

	public int getChannelId() {
		return( (int)data[0] );
	}
	
	public String getIp1() {
		return (new String(data, 1, Util.strlen(data, 1)));
	}

	public int getPort1() {
		return (Util.word2Int(data[17], data[18]));
	}

	public String getIp2() {
		return (new String(data, 19, Util.strlen(data, 19)));
	}

	public int getPort2() {
		return (Util.word2Int(data[35], data[36]));
	}
} public class DataJoinRoom {
	byte[] data;

	public DataJoinRoom(byte[] data) {
		this.data = data;
	}

	public long getRoomId() {
		return (Util.word2Long(data[0], data[1], data[2], data[3]));
	}

	public String getPassword() {
		return (new String(data, 4, Util.strlen(data, 4)));
	}
} public class DataPlayerTeamPositions {
	byte[] data;

	public DataPlayerTeamPositions(byte[] data) {
		this.data = data;
	}

	public int getPlayerTeam(long pid) {
		for (int i = 0; i < 4; i++) {
			int idx = i * 8;
			long t_pid = Util.word2Long(data[idx], data[idx + 1],
					data[idx + 2], data[idx + 3]);
			if (t_pid != pid)
				continue;
			return (data[idx + 4]);
		}
		return (0x000000FF);
	}

	public int getPlayerTeamPos(long pid) {
		for (int i = 0; i < 4; i++) {
			int idx = i * 8;
			long t_pid = Util.word2Long(data[idx], data[idx + 1],
					data[idx + 2], data[idx + 3]);
			if (t_pid != pid)
				continue;
			return (data[idx + 7]);
		}
		return (0x00000000);
	}
} public class DataRoom {
	byte[] data;

	public DataRoom(byte[] data) {
		this.data = data;
	}

	public String getName() {
		return (new String(data, 0, Util.strlen(data, 0)));
	}

	public boolean hasPassword() {
		byte b_haspass = data[64];
		if (b_haspass == (byte) 0x01)
			return (true);
		return (false);
	}

	public String getPassword() {
		return (new String(data, 65, Util.strlen(data, 65)));
	}
} public class GroupInfo {
	long id;
	String name;
	String comment;
	long position;
	long points;
	int victories;
	int defeats;
	int draws;
	int matches;
	int slots;
	long level;
	long leaderId;
	int recruiting;
	int numPlayers;
	


	String recruitComment;
	String leaderName;
	
	PlayerInfo[] players;

	public GroupInfo(long id, String name) {
		this.id = id;
		this.name = name;
		this.players = new PlayerInfo[0];
		this.comment = "";
	}

	public void setNumPlayers(int numPlayers) {
		this.numPlayers = numPlayers;
	}
	
	public int getRecruiting() {
		return recruiting;
	}

	public void setRecruiting(int recruiting) {
		this.recruiting = recruiting;
	}

	public String getRecruitComment() {
		return recruitComment;
	}

	public void setRecruitComment(String recruitComment) {
		this.recruitComment = recruitComment;
	}

//	public int getNumPlayers() {
//		return (players.length );
//	}
	
	public int getNumPlayers() {
		return( this.numPlayers );
	}
	
	public long[] getPlayerIds() {
		long[] pids = new long[players.length];
		for( int i = 0; i < players.length; i++ ) {
			pids[i] = players[i].getId();
		}
		return( pids );
	}

	public void setLeaderId(long pid) {
		this.leaderId = pid;
	}
	
	public void setLeaderName( String name ) {
		this.leaderName = name;
	}

	public long getLeaderId( ) {
		return( this.leaderId );
	}
	
	public String getLeaderName() {
		return( this.leaderName );
	}

	public void setSlots(int slots) {
		this.slots = slots;
	}

	public int getSlots() {
		return (slots);
	}

	public long getPoints() {
		return (points);
	}

	public void setPoints(long points) {
		this.points = points;
	}

	public long getPosition() {
		return (position);
	}

	public void setPosition(long position) {
		this.position = position;
	}

	public long getId() {
		return (id);
	}

	public String getName() {
		return (name);
	}

	public void setPlayers(PlayerInfo[] players) {
		this.players = players;
	}

	public void addPlayer(PlayerInfo player) {
		PlayerInfo newPInfo[] = new PlayerInfo[players.length + 1];
		System.arraycopy(players, 0, newPInfo, 0, players.length);
		newPInfo[players.length] = player;
		players = newPInfo;
	}
	
	public void removePlayer(long pid) {
		int idx = 0;
		PlayerInfo newPlayers[] = new PlayerInfo[players.length - 1];
		for( int i = 0; i < players.length; i++ ) {
			if( players[i].getId() != pid ) {
				newPlayers[idx++] = players[i];
			}
		}
		players = newPlayers;
	}

	public PlayerInfo[] getPlayers() {
		return (players);
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public String getComment() {
		return (this.comment);
	}

	public int getMatches() {
		return matches;
	}

	public void setMatches(int matches) {
		this.matches = matches;
	}

	public long getLevel() {
		return level;
	}

	public void setLevel(long level) {
		this.level = level;
	}

	public int getDraws() {
		return draws;
	}

	public void setDraws(int draws) {
		this.draws = draws;
	}

	public int getDefeats() {
		return defeats;
	}

	public void setDefeats(int defeats) {
		this.defeats = defeats;
	}

	public int getVictories() {
		return victories;
	}

	public void setVictories(int victories) {
		this.victories = victories;
	}
} public class IpInfo {
	long pId;
	String ip1;
	String ip2;
	int port1;
	int port2;
	int channel;

	public IpInfo(long pId, int channel, String ip1, int port1, String ip2, int port2) {
		this.ip1 = ip1;
		this.ip2 = ip2;
		this.channel = channel;
		this.port1 = port1;
		this.port2 = port2;
		this.pId = pId;
	}

	public int getChannel() {
		return( channel );
	}
	
	public String getIp1() {
		return (ip1);
	}

	public String getIp2() {
		return (ip2);
	}

	public int getPort1() {
		return (port1);
	}

	public int getPort2() {
		return (port2);
	}

	public long getPlayerId() {
		return (pId);
	}
} public class Message {
	Vector<MetaMessageBlock> blocks;
	int byteSize;

	public static Message getInstance(byte[] data) {
		
		if( data == null ) return( null );
		
		Message mes = new Message();
		mes.byteSize = 0;
		mes.blocks = new Vector<MetaMessageBlock>();
		int idx = 0;

		if( data.length < GenericHeader.getByteSize()) return null;
		while (idx < data.length) {
			byte[] header = new byte[GenericHeader.getByteSize()];
			System.arraycopy(data, idx, header, 0, GenericHeader.getByteSize());
			MetaMessageBlock mb = new MessageBlock();
			GenericHeader gh = new GenericHeader(header);
			if( (gh.getSize() + GenericHeader.getByteSize()) > (data.length - idx)) {
				return( null );
			}
			((MessageBlock) mb).setHeader(gh);
			byte[] blockData = new byte[gh.getSize()];

			/*
			 * Problemas de fragmentacion!
			 */
			System.arraycopy(data, idx + GenericHeader.getByteSize(),
					blockData, 0, gh.getSize());
			((MessageBlock) mb).setData(blockData);
			mes.addBlock(mb);
			idx += (GenericHeader.getByteSize() + gh.getSize());
		}
		return( mes );
	}
	
	public Message(byte[] data) {
		this.byteSize = 0;
		this.blocks = new Vector<MetaMessageBlock>();
		int idx = 0;

		while (idx < data.length) {
			byte[] header = new byte[GenericHeader.getByteSize()];
			System.arraycopy(data, idx, header, 0, GenericHeader.getByteSize());
			MetaMessageBlock mb = new MessageBlock();
			GenericHeader gh = new GenericHeader(header);
			((MessageBlock) mb).setHeader(gh);
			byte[] blockData = new byte[gh.getSize()];

			/*
			 * Problemas de fragmentacion!
			 */
			System.arraycopy(data, idx + GenericHeader.getByteSize(),
					blockData, 0, gh.getSize());
			((MessageBlock) mb).setData(blockData);
			addBlock(mb);
			idx += (GenericHeader.getByteSize() + gh.getSize());
		}
	}

	public Message() {
		this.blocks = new Vector<MetaMessageBlock>();
	}

	public void addBlock(MetaMessageBlock mb) {
		blocks.add(mb);
		byteSize += mb.getByteSize();
	}

	public void setBlock(int idx, MetaMessageBlock mb) {
		blocks.set(idx, mb);
	}

	public byte[] getBytes() {
		byte[] ret = new byte[byteSize];
		int idx = 0;
		for (int i = 0; i < blocks.size(); i++) {
			MetaMessageBlock mb = (MetaMessageBlock) blocks.get(i);
			System.arraycopy(mb.getBytes(), 0, ret, idx, mb.getByteSize());
			idx += mb.getByteSize();
		}
		return (ret);
	}

	public MetaMessageBlock getBlock(int idx) {
		return ((MetaMessageBlock) this.blocks.get(idx));
	}

	public int getNumBlocks() {
		return (blocks.size());
	}
} public class MessageBlock implements MetaMessageBlock {
	GenericHeader header;
	byte[] data;

	public MessageBlock() {
		this.header = new GenericHeader();
		this.data = new byte[0];
	}

	public MessageBlock(GenericHeader header, byte[] data) {
		this.header = header;
		this.data = data;
	}

	public GenericHeader getHeader() {
		return (header);
	}

	public void setHeader(GenericHeader header) {
		this.header = header;
	}

	public byte[] getData() {
		return (data);
	}

	public byte[] getBytes() {
		byte[] ret = new byte[GenericHeader.getByteSize() + data.length];
		System.arraycopy(header.getBytes(), 0, ret, 0, GenericHeader
				.getByteSize());
		System
				.arraycopy(data, 0, ret, GenericHeader.getByteSize(),
						data.length);
		return (ret);
	}

	public int getByteSize() {
		return (GenericHeader.getByteSize() + data.length);
	}

	public void setData(byte[] data) {
		this.data = data;
		header.setSize(data.length);
	}
} public class MessageBlockByte extends MessageBlock {

	public MessageBlockByte(byte value, int qId) {
		super();
		header.setQuery(qId);
		setData(new byte[] { value });
	}
} public class MessageBlockFriendsList extends MessageBlock {

	static final int MAX_PLAYERS_PER_BLOCK = 6;
	
//	public MessageBlockFriendsList(PlayerInfo[] players,
//			int qId) {
//		super();
////		header.setQuery(qId);
////		header.setSeq(seq);
//
//		/*
//		 * Correccion, nada de 314, son 23
//		 */
//
////		byte[] data = new byte[(4 + 48 + 112)
////				* player.getGroupInfo().getPlayers().length];
////		byte[] tail = new byte[112];
////		try {
////			FileInputStream fi = new FileInputStream("5062tail.dec");
////			fi.read(tail);
////			fi.close();
////		} catch (Exception ex) {
////			ex.printStackTrace();
////		}
//
//		//Arrays.fill(data, (byte) 0x00);
//
//		int numBlocks = (players.length / MAX_PLAYERS_PER_BLOCK) + 1;
//		
//		for( int j = 0; j < numBlocks; j++ ) {
//			int start = j*MAX_PLAYERS_PER_BLOCK;
//			int end = j*MAX_PLAYERS_PER_BLOCK + MAX_PLAYERS_PER_BLOCK;
//			if( j == (numBlocks - 1) ) end = players.length;
//			byte[] data = new byte[(4 + 48 + 112) * (end - start)];
//			Arrays.fill(data, (byte) 0x00);
//			int idx = 0;
//			for (int i = start; i < end; i++) {
//				PlayerInfo p = players[i];
//				String plName = p.getName();
//				long plId = p.getId();
//				int chan_id = p.getLobbyId();
//				byte[] b_plid = Util.long2Word(plId);
//	
//				System.arraycopy(b_plid, 0, data, idx, b_plid.length);
//				System.arraycopy(plName.getBytes(), 0, data, idx + 4, plName
//						.getBytes().length);
//				/*
//				 * Esto vale 04 si soy yo, FF en caso contrario
//				 * 
//				 */
//				//System.arraycopy(pinfo, 0, data, idx + 4 + 48, pinfo.length);
//				
//				/*
//				 * Si pongo un 0, no carga la lista bien
//				 * Si pongo un 1, no puedo invitar
//				 * Si pongo un 2, puedo invitar
//				 * Si pongo un 3, puedo invitar y salgo el primero de la lista.
//				 * Si pongo un 4, no puedo invitar y no cargo toda la lista bien.
//				 * Si pongo un 5, no puedo invitar y no cargo toda la lista bien.
//				 * Si pingo un 6, no puedo invitar y no cargo toda la lista bien.
//				 */
////				if( g.getLeaderId() == p.getId() )
////					data[idx + 4+ 48] = 3;
////				else
//					data[idx + 4+ 48] = Util.int2Word(p.getInvitation())[1];
//				
//				data[idx+4+48+1] = Util.int2Word(p.getDivision())[1];
//				
//				System.arraycopy(Util.int2Word(p.getCategory()), 0, data, idx + 4 + 48 + 2, 2); 
//				System.arraycopy(Util.int2Word(p.getServerId()), 0, data, idx + 4 + 48 + 4, 2);
//				if ( chan_id != 255 ) {
//					/*
//					 * No se, bocachoti ten�a un 4
//					 * Claro, esto dice en que salón está el tio
//					 */
////					ChannelInfo chan = channels.getChannel( chan_id );
//					data[idx + 4 + 48 + 6] = (byte)chan_id;
//					System.arraycopy(p.getLobbyName().getBytes(), 0, data,
//							idx + 4 + 48 + 7, p.getLobbyName().getBytes().length);
//					data[idx + 4 + 48 + 7 + 32] = (byte) 0x03; // OJO ERA UN 3!
//					data[idx + 4 + 48 + 7 + 32 + 1] = Util.int2Word(p.getLobbyType())[1];
//					System.arraycopy(Util.int2Word(p.getGroupMatches()), 0, data, idx+3+48+7+32+2, 2);
//					
////					data[idx + 4 + 48 + 7 + 32 + 3] = ; // Numero de
////																	// partidos de
////																	// grupo jugados
//				} else {
//					data[idx + 4 + 48 + 6] = (byte) 0xFF;
//				}
//	
//				/*
//				 * Aqui hay algo que para boca vale 4, marba 4, evelito 8 y fedekan
//				 * 0 -- son los partidos de grupo jugados!!
//				 */
//				// data[idx+4+48+7+32+3] = (byte)0x04;
//				idx += (4 + 48 + 112);
//			}
//			MessageBlock mb = new MessageBlock();
//			mb.header.query = qId;
//			mb.setData(data);
//			addBlock(mb);
//		}
//		//setData(qId, seq, data);
//	}
	
	public MessageBlockFriendsList(PlayerInfo players[], int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[(4 + 48) * players.length];
		int idx = 0;
		for (int i = 0; i < players.length; i++) {
			PlayerInfo pi = players[i];
			String plName = pi.getName();
			long id = pi.getId();

			if (plName.length() > 48)
				plName = plName.substring(0, 48);

			byte[] b_header = buildPlyHeader(pi);
			byte[] b_name = new byte[48];
			Arrays.fill(b_name, (byte) 0x00);
			System.arraycopy(plName.getBytes(), 0, b_name, 0,
					plName.getBytes().length);

			System.arraycopy(b_header, 0, data, idx, b_header.length);
			idx += b_header.length;
			System.arraycopy(b_name, 0, data, idx, b_name.length);
			idx += b_name.length;
		}

		setData(data);
	}

	byte[] buildPlyHeader(PlayerInfo player) {
		byte[] ret = new byte[4];
		byte[] b_id = Util.long2Word(player.getId());
		System.arraycopy(b_id, 0, ret, 0, 4);
		//ret[4] = (byte)Util.int2Word(player.getInvitation())[1];
		//ret[4] = 0x00;
		return (ret);
	}
} public class MessageBlockGameAnyId extends MessageBlock {

	public MessageBlockGameAnyId(long rId, int qId) {
		super();
		header.setQuery(qId);
		setData(Util.long2Word(rId));
	}
} public class MessageBlockGameChatMessage extends MessageBlock {

	/*
	 * Se ha probado a cambiar los cuatro FF y no hay variacion ninguna
	 * en los mensajes, ni de color ni de nada.
	 */
	
	byte[] HEADER = new byte[] { (byte) 0x00, (byte) 0x01, (byte) 0xFF,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF };

	public MessageBlockGameChatMessage(PlayerInfo player, byte place, byte channel, 
			String message, boolean encode, int qId) {
		super();
		header.setQuery(qId);
		
		byte[] b_mes;
		if( encode ) {
			try {
				b_mes = message.getBytes("UTF-8");
			} catch( UnsupportedEncodingException e) {
				b_mes = message.getBytes();
				System.out.println( "Unsupported CHARSET Exception!");
			}
		} else {
			b_mes = message.getBytes();
		}

		byte[] data = new byte[HEADER.length + 4 + 48 + b_mes.length + 1];
		Arrays.fill(data, (byte) 0x00);
		byte[] b_pid = Util.long2Word(player.getId());
		byte[] b_pname = player.getName().getBytes();

		int idx = 0;
		System.arraycopy(HEADER, 0, data, idx, HEADER.length);
		data[0] = place;
		data[1] = channel;
		
		//System.arraycopy(b_channel, 0, data, 0, 2);
		idx += HEADER.length;
		System.arraycopy(b_pid, 0, data, HEADER.length, b_pid.length);
		idx += 4;
		System.arraycopy(b_pname, 0, data, idx, b_pname.length);
		idx += 48;
		System.arraycopy(b_mes, 0, data, idx, b_mes.length);
		data[data.length - 1] = (byte) 0x00;

		setData(data);
	}
} public class MessageBlockGameEndInfo extends MessageBlock {

	RoomInfo room;
	/*
	 * Primero van los ids de los contendientes, luego los id de grupo de cada
	 * uno
	 */
	byte[] HEADER = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00 };
	
	// En una captura he visto un header que es ff ff fe 00

	// 20 zeros
//	byte[] TAIL = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00,
//			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
//			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
//			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
//			(byte) 0x00, (byte) 0x00 };

	byte[] TAIL = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x01,
			(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
			(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
			(byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x01,
			(byte) 0x01, (byte) 0x01 };
	
	public MessageBlockGameEndInfo(RoomInfo room, int qId) {
		super();
		this.room = room;
		header.setQuery(qId);

		byte[] data = new byte[HEADER.length + 22 * 4 + TAIL.length];
		Arrays.fill(data, (byte) 0x00);
		
		for (int i = 0; i < room.getNumPlayersOnSel(); i++) {
			long pid = room.getPlayerIdOnSel(i);
			PlayerInfo p = room.getPlayerFromPid(pid);
			if( p == null ) {
				System.out.println( "ATENCIÓN!");
				System.out.println( "No existe jugador en sel: " + i + ", su pid debería ser: " + pid );
				continue;
			}
			byte b_pid[] = Util.long2Word(pid);
			System.arraycopy(b_pid, 0, data, (i * 22) + 4, b_pid.length);
//			int k = 0;
//			for( int j = (i * 22) + 8; j < (i * 22) + 8 + 18; j++ ) {
//				data[j] = (byte)k++; 
//			}
			
			System.arraycopy( Util.int2Word(p.getMatchPoints()), 0, data, (i*22) + 8, 2);
			System.arraycopy( Util.long2Word(p.getPoints()), 0, data, (i*22) + 10, 4 );
			
			System.arraycopy( Util.int2Word
					(p.getOldCategory()), 0, data, (i*22) + 8 + 16, 2 );
			System.arraycopy( Util.int2Word(p.getCategory()), 0, data, (i*22) + 8 + 14, 2 );
		}
//		int k = 0;
//		for( int i = HEADER.length + 22 * 4; i < data.length; i++ ) {
//			data[i] = (byte)k++;
//		}
		
		/*
		 * Los 20 bytes que quedan al final son los puntos del grupo, se le dan los puntos
		 * del partido al grupo para cada cual, primero va el id de un grupo luego los dos
		 * siguiente bytes son los puntos que se les ha dado en este partido y los 4 siguientes
		 * son los puntos totales del grupo.
		 * 
		 * De momento lo dejamos a cero porque no nos queremos meter en el follón de los
		 * grupos aún.
		 */
		
		setData(data);
	}
} public class MessageBlockGameEnterRoomFailFull extends MessageBlock {
	byte[] RAWDATA = new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFE,
			(byte) 0x09, (byte) 0x01 };

	public MessageBlockGameEnterRoomFailFull(int qId) {
		super();
		header.setQuery(qId);

		setData(RAWDATA);
	}
} public class MessageBlockGameEnterRoomFailPass extends MessageBlock {
	byte[] RAWDATA = new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFD,
			(byte) 0xDA, (byte) 0x00 };

	public MessageBlockGameEnterRoomFailPass(int qId) {
		super();
		header.setQuery(qId);
		setData(RAWDATA);
	}
} public class MessageBlockGameGroupApplicationList extends MessageBlockMulti {

	static final int MAX_PLAYERS_PER_BLOCK = 8;


	public MessageBlockGameGroupApplicationList(PlayerInfo[] players,
			 int qId) {
		super();		

		int numBlocks = (players.length / MAX_PLAYERS_PER_BLOCK) + 1;
		
		for( int j = 0; j < numBlocks; j++ ) {
			int start = j*MAX_PLAYERS_PER_BLOCK;
			int end = j*MAX_PLAYERS_PER_BLOCK + MAX_PLAYERS_PER_BLOCK;
			if( j == (numBlocks - 1) ) end = players.length;
			byte[] data = new byte[(4 + 4 + 48 + 4) * (end - start)];
			Arrays.fill(data, (byte) 0x00);
			int idx = 0;
			for (int i = start; i < end; i++) {
				String plName = players[i].getName();
				long plId = players[i].getId();
				byte[] b_plid = Util.long2Word(plId);
	
//				System.arraycopy(Util.long2Word(i), 0, data, idx, 4);
//				idx += 4;
				System.arraycopy(b_plid, 0, data, idx, b_plid.length);
				idx += b_plid.length;
				System.arraycopy(b_plid, 0, data, idx, b_plid.length);
				idx += b_plid.length;
				System.arraycopy(plName.getBytes(), 0, data, idx,
						plName.getBytes().length);
				idx += 48 + 4;
			}
			MessageBlock mb = new MessageBlock();
			mb.header.query = qId;
			mb.setData(data);
			addBlock(mb);
		}
	}
} public class MessageBlockGameGroupInfo extends MessageBlock {

	/*
	 * Hay que determinar que es cada cosa...
	 */
	byte[] groupData = new byte[] {
			// ----------- Posicion ------------ ----------------- Puntos
			// ------------
			(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x5E, (byte) 0x01,
			(byte) 0x02, (byte) 0xFF, (byte) 0xE6,
			// --- Partidos --- --- Victorias --- --- Derrotas --- --- Empates
			// ---
			(byte) 0x01, (byte) 0x7A, (byte) 0x00, (byte) 0x82, (byte) 0x00,
			(byte) 0xC7, (byte) 0x00, (byte) 0x31 };

	public MessageBlockGameGroupInfo(GroupInfo group, int qId) {
		
		/*
		 * NECESITO SABER DONDE SE ESPECIFICA SI EL GRUPO ESTA O NO CONTRATANDO Y SU COMENTARIO SI
		 * LO ESTA!
		 */
		
		super();
		header.setQuery(qId);

		byte[] data = new byte[4 + 4 + 48 + 16 + 4 + 48 + 519];
//		byte[] tail = new byte[519];
//		try {
//			FileInputStream fi = new FileInputStream("5025tail.dec");
//			fi.read(tail);
//			fi.close();
//		} catch (Exception ex) {
//			ex.printStackTrace();
//		}

		Arrays.fill(data, (byte) 0x00);

		String plName = group.getLeaderName();
		String grName = group.getName();
		// String comment = leader.getComment();
		long grId = group.getId();
		long plId = group.getLeaderId();
		byte[] b_plid = Util.long2Word(plId);
		byte[] b_grid = Util.long2Word(grId);
		System.arraycopy(b_grid, 0, data, 4, b_grid.length);
		System.arraycopy(grName.getBytes(), 0, data, 4 + 4,
				grName.getBytes().length);
		byte[] b_position = Util.long2Word(group.getPosition());
		byte[] b_points = Util.long2Word(group.getPoints());
		byte[] b_matches = Util.int2Word(group.getMatches());
		byte[] b_victories = Util.int2Word(group.getVictories());
		byte[] b_defeats = Util.int2Word(group.getDefeats());
		byte[] b_draws = Util.int2Word(group.getDraws());
		byte[] b_level = Util.long2Word(group.getLevel());
		System.arraycopy(b_position, 0, data, 4 + 4 + 48, b_position.length);
		System.arraycopy(b_points, 0, data, 4 + 4 + 48 + 4, b_points.length);
		System.arraycopy(b_matches, 0, data, 4 + 4 + 48 + 4 + 4,
				b_matches.length);
		System.arraycopy(b_victories, 0, data, 4 + 4 + 48 + 4 + 4 + 2,
				b_victories.length);
		System.arraycopy(b_defeats, 0, data, 4 + 4 + 48 + 4 + 4 + 2 + 2,
				b_defeats.length);
		System.arraycopy(b_draws, 0, data, 4 + 4 + 48 + 4 + 4 + 2 + 2 + 2,
				b_draws.length);
		// System.arraycopy(groupData, 0, data, 4+4+48, groupData.length);
		System.arraycopy(b_plid, 0, data, 4 + 4 + 48 + 16, b_plid.length);
		System.arraycopy(plName.getBytes(), 0, data, 4 + 4 + 64 + 4, plName
				.getBytes().length);
		//System.arraycopy(tail, 0, data, 4 + 4 + 48 + 16 + 4 + 48, tail.length);
		
		data[4 + 4 + 48 + 16 + 4 + 48 + 2 + 4] = Util.int2Word(group.getRecruiting())[1];
		System.arraycopy(group.getRecruitComment().getBytes(), 0, data, 4 + 4 + 48 + 16 + 4 + 48 + 2 + 4 + 1, group.getRecruitComment().getBytes().length);	
		
//		Arrays.fill(data, 4 + 4 + 48 + 16 + 4 + 48 + 2 + 4, 
//				4 + 4 + 48 + 16 + 4 + 48 + 2 + 4 + 1, (byte)0x01);
		
		if (!group.getComment().equals(""))
			System.arraycopy(group.getComment().getBytes(), 0, data, 4 + 4 + 48
					+ 16 + 4 + 48 + 263, group.getComment().getBytes().length);

		//Arrays.fill(data, 4 + 4 + 48 + 16 + 4 + 48 + 263 + 48, data.length, (byte)0x01);
		
		data[4 + 4 + 48 + 16 + 4 + 48] = Util.int2Word(group.getNumPlayers())[1];
		data[4 + 4 + 48 + 16 + 4 + 48 + 1] = Util.int2Word(group.getSlots())[1];
		/*
		 * Estos dos de abajo si son 3F y 37 dicen que el grupo es nivel 11
		 */

		System.arraycopy(b_level, 0, data, 4 + 4 + 48 + 16 + 4 + 48 + 2,
				b_level.length);
		
		// data[4+4+48+16+4+48+4] = (byte)0x00;
		// data[4+4+48+16+4+48+5] = (byte)0x00;

		// System.arraycopy(comment.getBytes(), 0, data, 4+4+48+4+48+30,
		// comment.getBytes().length);

		// data[4+4+48+4+48] = (byte)0x01;
		setData(data);
	}
} public class MessageBlockGameGroupList extends MessageBlockMulti {

	static final int MAX_GROUPS_PER_BLOCK = 6;
	
	public MessageBlockGameGroupList(GroupInfo[] groups, int qId) {
		super();
//		header.setQuery(qId);
//		header.setSeq(seq);

//		byte[] data = new byte[(4 + 48) * groups.length];
//		Arrays.fill(data, (byte) 0x00);

//		int idx = 0;

		int numBlocks = (groups.length / MAX_GROUPS_PER_BLOCK) + 1;
		
		for( int j = 0; j < numBlocks; j++ ) {
			int start = j*MAX_GROUPS_PER_BLOCK;
			int end = j*MAX_GROUPS_PER_BLOCK + MAX_GROUPS_PER_BLOCK;
			if( j == (numBlocks - 1) ) end = groups.length;
			byte[] data = new byte[(4 + 48) * (end - start)];
			Arrays.fill(data, (byte) 0x00);
			int idx = 0;
			for (int i = start; i < end; i++) {
				String grName = groups[i].getName();
				long grId = groups[i].getId();
				byte[] b_grid = Util.long2Word(grId);
	
				System.arraycopy(b_grid, 0, data, idx, b_grid.length);
				idx += b_grid.length;
				System.arraycopy(grName.getBytes(), 0, data, idx,
						grName.getBytes().length);
				idx += 48;
			}
			MessageBlock mb = new MessageBlock();
			mb.header.query = qId;
			mb.setData(data);
			addBlock(mb);
		}
		//setData(qId, seq, data);
	}
} public class MessageBlockGameGroupMembers extends MessageBlockMulti {

	static final int MAX_PLAYERS_PER_BLOCK = 6;
	
//	byte[] pinfo = new byte[] {
//			// --- ?? --- ---- CATEGORIA --
//			/*
//			 * Byte de invitacion, con 0x01 sale un "-" con 0x03 sale un "O"
//			 */
//			// -inv- -div-
//			(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
//			(byte) 0x30 
//	};

	public MessageBlockGameGroupMembers(PlayerInfo[] players,
			 int qId) {
		super();
//		header.setQuery(qId);
//		header.setSeq(seq);

		/*
		 * Correccion, nada de 314, son 23
		 */

//		byte[] data = new byte[(4 + 48 + 112)
//				* player.getGroupInfo().getPlayers().length];
//		byte[] tail = new byte[112];
//		try {
//			FileInputStream fi = new FileInputStream("5062tail.dec");
//			fi.read(tail);
//			fi.close();
//		} catch (Exception ex) {
//			ex.printStackTrace();
//		}

		//Arrays.fill(data, (byte) 0x00);

		int numBlocks = (players.length / MAX_PLAYERS_PER_BLOCK) + 1;
		
		for( int j = 0; j < numBlocks; j++ ) {
			int start = j*MAX_PLAYERS_PER_BLOCK;
			int end = j*MAX_PLAYERS_PER_BLOCK + MAX_PLAYERS_PER_BLOCK;
			if( j == (numBlocks - 1) ) end = players.length;
			byte[] data = new byte[(4 + 48 + 112) * (end - start)];
			Arrays.fill(data, (byte) 0x00);
			int idx = 0;
			for (int i = start; i < end; i++) {
				PlayerInfo p = players[i];
				String plName = p.getName();
				long plId = p.getId();
				int chan_id = p.getLobbyId();
				byte[] b_plid = Util.long2Word(plId);
	
				System.arraycopy(b_plid, 0, data, idx, b_plid.length);
				System.arraycopy(plName.getBytes(), 0, data, idx + 4, plName
						.getBytes().length);
				/*
				 * Esto vale 04 si soy yo, FF en caso contrario
				 * 
				 */
				//System.arraycopy(pinfo, 0, data, idx + 4 + 48, pinfo.length);
				
				/*
				 * Si pongo un 0, no carga la lista bien
				 * Si pongo un 1, no puedo invitar
				 * Si pongo un 2, puedo invitar
				 * Si pongo un 3, puedo invitar y salgo el primero de la lista.
				 * Si pongo un 4, no puedo invitar y no cargo toda la lista bien.
				 * Si pongo un 5, no puedo invitar y no cargo toda la lista bien.
				 * Si pingo un 6, no puedo invitar y no cargo toda la lista bien.
				 */
//				if( g.getLeaderId() == p.getId() )
//					data[idx + 4+ 48] = 3;
//				else
					data[idx + 4+ 48] = Util.int2Word(p.getInvitation())[1];
				
				data[idx+4+48+1] = Util.int2Word(p.getDivision())[1];
				
				System.arraycopy(Util.int2Word(p.getCategory()), 0, data, idx + 4 + 48 + 2, 2); 
				System.arraycopy(Util.int2Word(p.getServerId()), 0, data, idx + 4 + 48 + 4, 2);
				if ( chan_id != 255 ) {
					/*
					 * No se, bocachoti ten�a un 4
					 * Claro, esto dice en que salón está el tio
					 */
//					ChannelInfo chan = channels.getChannel( chan_id );
					data[idx + 4 + 48 + 6] = (byte)chan_id;
					System.arraycopy(p.getLobbyName().getBytes(), 0, data,
							idx + 4 + 48 + 7, p.getLobbyName().getBytes().length);
					data[idx + 4 + 48 + 7 + 32] = (byte) 0x03; // OJO ERA UN 3!
					data[idx + 4 + 48 + 7 + 32 + 1] = Util.int2Word(p.getLobbyType())[1];
					System.arraycopy(Util.int2Word(p.getGroupMatches()), 0, data, idx+3+48+7+32+2, 2);
					
//					data[idx + 4 + 48 + 7 + 32 + 3] = ; // Numero de
//																	// partidos de
//																	// grupo jugados
				} else {
					data[idx + 4 + 48 + 6] = (byte) 0xFF;
				}
	
				/*
				 * Aqui hay algo que para boca vale 4, marba 4, evelito 8 y fedekan
				 * 0 -- son los partidos de grupo jugados!!
				 */
				// data[idx+4+48+7+32+3] = (byte)0x04;
				idx += (4 + 48 + 112);
			}
			MessageBlock mb = new MessageBlock();
			mb.header.query = qId;
			mb.setData(data);
			addBlock(mb);
		}
		//setData(qId, seq, data);
	}
} public class MessageBlockGameIpInfo extends MessageBlock {
	static final byte[] MSG_DATA = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x80,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x38,
			(byte) 0x37, (byte) 0x2E, (byte) 0x32, (byte) 0x32, (byte) 0x30,
			(byte) 0x2E, (byte) 0x31, (byte) 0x33, (byte) 0x30, (byte) 0x2E,
			(byte) 0x33, (byte) 0x32, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x16, (byte) 0x6B, (byte) 0x31, (byte) 0x39, (byte) 0x32,
			(byte) 0x2E, (byte) 0x31, (byte) 0x36, (byte) 0x38, (byte) 0x2E,
			(byte) 0x31, (byte) 0x2E, (byte) 0x31, (byte) 0x30, (byte) 0x00,
			(byte) 0xB2, (byte) 0xAB, (byte) 0x03, (byte) 0x16, (byte) 0x6B,
			(byte) 0x00, (byte) 0x0C, (byte) 0xE1, (byte) 0xBA, (byte) 0x00,
			(byte) 0x00, (byte) 0xFF };

	public MessageBlockGameIpInfo(RoomInfo r, IpInfo ip[], int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[MSG_DATA.length * ip.length];

		// pos 32 es la ip 1 con un 0 al final
		// pos 50 ip2 con un 0 al final
		// pos 124 es el id

		// Arrays.fill( data, (byte)0x00 );
		for (int i = 0; i < ip.length; i++) {
			int idx = i * MSG_DATA.length;
			System.arraycopy(MSG_DATA, 0, data, idx, MSG_DATA.length);
			String ip1 = ip[i].getIp1();
			String ip2 = ip[i].getIp2();
			long pId = ip[i].getPlayerId();
			if (ip1.length() > 15)
				ip1 = ip1.substring(0, 15);
			if (ip2.length() > 15)
				ip2 = ip2.substring(0, 15);
			byte[] b_ip1 = ip1.getBytes();
			byte[] b_port1 = Util.int2Word(ip[i].getPort1());
			byte[] b_ip2 = ip2.getBytes();
			byte[] b_port2 = Util.int2Word(ip[i].getPort2());
			byte[] b_pid = Util.long2Word(pId);

//			System.out.println("pid vale: " + pId + " y el ultimo byte vale "
//					+ b_pid[3]);

			System.arraycopy(b_ip1, 0, data, idx + 32, b_ip1.length);
			data[idx + 32 + b_ip1.length] = (byte) 0x00;

			// Novedades!
			System.arraycopy(b_port1, 0, data, idx + 32 + 16, b_port1.length);

			System.arraycopy(b_ip2, 0, data, idx + 50, b_ip2.length);
			data[idx + 50 + b_ip2.length] = (byte) 0x00;

			// Novedades!
			System.arraycopy(b_port2, 0, data, idx + 50 + 16, b_port2.length);

			System.arraycopy(b_pid, 0, data, idx + 68, b_pid.length);
			data[idx + 68 + 4] = Util.int2Word(i)[1];
			data[idx + 68 + 5] = Util.int2Word(i)[1];
			data[idx + 68 + 6] = Util.int2Word(r.getPlayerSel(i))[1];
		}
		setData(data);
	}
} public class MessageBlockGameIpInfoShort extends MessageBlock {
	static final byte[] MSG_DATA = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x38, (byte) 0x37, (byte) 0x2E, (byte) 0x32,
			(byte) 0x32, (byte) 0x30, (byte) 0x2E, (byte) 0x31, (byte) 0x33,
			(byte) 0x30, (byte) 0x2E, (byte) 0x33, (byte) 0x32, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x16, (byte) 0x6B, (byte) 0x31,
			(byte) 0x39, (byte) 0x32, (byte) 0x2E, (byte) 0x31, (byte) 0x36,
			(byte) 0x38, (byte) 0x2E, (byte) 0x31, (byte) 0x2E, (byte) 0x31,
			(byte) 0x30, (byte) 0x00, (byte) 0xB2, (byte) 0xAB, (byte) 0x03,
			(byte) 0x16, (byte) 0x6B, (byte) 0x00, (byte) 0x0C, (byte) 0xE1,
			(byte) 0xBA, };

	public MessageBlockGameIpInfoShort(IpInfo ip, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = MSG_DATA;

		// pos 4 es la ip 1 con un 0 al final
		// pos 22 ip2 con un 0 al final
		// pos 40 es el id

		String ip1 = ip.getIp1();
		String ip2 = ip.getIp2();
		if (ip1.length() > 15)
			ip1 = ip1.substring(0, 15);
		if (ip2.length() > 15)
			ip2 = ip2.substring(0, 15);
		byte[] b_ip1 = ip1.getBytes();
		byte[] b_port1 = Util.int2Word(ip.getPort1());
		byte[] b_ip2 = ip2.getBytes();
		byte[] b_port2 = Util.int2Word(ip.getPort2());
		byte[] b_pid = Util.long2Word(ip.getPlayerId());

		System.arraycopy(b_ip1, 0, data, 4, b_ip1.length);
		data[4 + b_ip1.length] = (byte) 0x00;
		System.arraycopy(b_port1, 0, data, 4 + 16, b_port1.length);
		System.arraycopy(b_ip2, 0, data, 22, b_ip2.length);
		data[22 + b_ip2.length] = (byte) 0x00;
		System.arraycopy(b_port2, 0, data, 22 + 16, b_port2.length);
		System.arraycopy(b_pid, 0, data, 40, b_pid.length);

		setData(data);
	}
} public class MessageBlockGamePlayerGroup extends MessageBlock {
	/*
	 * HEADER - PLAYERID - 37 bytes HEADER_2 - NOMBRE GRUPO (51 bytes)
	 */
	static final byte[] HEADER = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00 };
	static final byte[] PLY_HEADER = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00 };
	static final byte[] REPEAT = { (byte) 0x00, (byte) 0x60, (byte) 0xDC,
			(byte) 0x2E, (byte) 0x34, (byte) 0xD7, (byte) 0x69, (byte) 0x64,
			(byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00 };

	// static final byte[]

	// 4 (zeros) + 48 (nombre_player) + 4 (id-grupo) + 48 (nombre grupo) + 314
	// (traperadas)
	// Es crear_r09

	public MessageBlockGamePlayerGroup(PlayerInfo player,
			int qId) {
		super();
		header.setQuery(qId);

		/*
		 * Correccion, nada de 314, son 23
		 */

		byte[] data = new byte[4 + 4 + 48 + 4 + 48 + 314];
//		byte[] tail = new byte[314];
		Arrays.fill(data, (byte) 0x00);

		String plName = player.getName();
		String grName = player.getGroupName();
		String comment = player.getComment();
		long grId = player.getGid();
		long plId = player.getId();
		byte[] b_plid = Util.long2Word(plId);
		byte[] b_grid = Util.long2Word(grId);

		System.arraycopy(b_plid, 0, data, 4, b_plid.length);
		System.arraycopy(plName.getBytes(), 0, data, 4 + 4,
				plName.getBytes().length);
		System.arraycopy(b_grid, 0, data, 4 + 4 + 48, b_grid.length);
		System.arraycopy(grName.getBytes(), 0, data, 4 + 4 + 48 + 4, grName
				.getBytes().length);
		// System.arraycopy(tail, 0, data, 4+4+48+4+48, 2 );
		/*
		 * primer byte indica si el tio pertenece a un grupo, segundo la
		 * divisi�n un 01 funciona como pertenecer al grupo un 02 permite
		 * invitar a gente al grupo, no permite cambiar el comentario del grupo
		 * un 03 peta / no carga al principio un 04 Estoy solicitando
		 * incorporacion al grupo un 05 peta al darle a "mi grupo" un 06 no
		 * salgo y peta al darle a "mi grupo"
		 */
		if (grId > 0) {
			/*
			 * Si soy el lider, aqui es un 0x03;
			 */
//			if( plId == group.getLeaderId() )
//				data[4 + 4 + 48 + 4 + 48] = (byte) 0x03;
//			else
				data[4 + 4 + 48 + 4 + 48] = Util.int2Word(player.getInvitation())[1];
		}
		// System.arraycopy(new byte[] { (byte)0x01, (byte)0x00 }, 0, data,
		// 4+4+48+4+48, 2 );
		
		/*
		 * Voy a cambiar los 30 bytes que me faltan hasta el comment
		 */
		
		/*
		 * Known score locations
		 * 
		 * Starting point = 4 + 4 + 48 + 4 + 48 = 108
		 */
		
		data[109] = Util.int2Word( player.getDivision())[1];
		
		
		System.arraycopy( Util.long2Word( player.getPoints() ), 0, data, 108 + 2, 4 );
		
		// Categoría
		System.arraycopy( Util.int2Word( player.getCategory() ), 0, data, 108 + 6, 2 );

		System.arraycopy( Util.int2Word( player.getMatchesPlayed() ), 0, data, 108 + 8, 2 );

		System.arraycopy( Util.int2Word( player.getVictories()), 0, data, 108 + 10, 2 );

		System.arraycopy( Util.int2Word( player.getDefeats()), 0, data, 108 + 12, 2 );

		System.arraycopy( Util.int2Word( player.getDraws()), 0, data, 108 + 14, 2 );

		System.arraycopy( Util.int2Word( player.getWinningStreak()), 0, data, 108 + 16, 2 );

		System.arraycopy( Util.int2Word( player.getBestStreak()), 0, data, 108 + 18, 2 );

		System.arraycopy( Util.int2Word( player.getDecos() ), 0, data, 108 + 20, 2 );
		
		System.arraycopy( Util.long2Word( player.getGoalsScored() ), 0, data, 108 + 22, 4 );
		
		System.arraycopy( Util.long2Word( player.getGoalsReceived() ), 0, data, 108 + 26, 4 );
		
		
		System.arraycopy(comment.getBytes(), 0, data, 4 + 4 + 48 + 4 + 48 + 30,
				comment.getBytes().length);
		
		/*
		 * Position field
		 */
		System.arraycopy( Util.long2Word( player.getPosition()), 0, data, 394, 4);
		
		/*
		 * Uno se los dos son medallas plata de competición.
		 */
//		System.arraycopy( Util.int2Word(0x3FFF),0, data, 398, 2);
//		System.arraycopy( Util.int2Word(0x3FFF),0, data, 400, 2);
//		System.arraycopy( Util.int2Word(0x3FFF),0, data, 402, 2);
//		System.arraycopy( Util.int2Word(0x3FFF),0, data, 404, 2);
//		System.arraycopy( Util.int2Word(0x3FFF),0, data, 406, 2);
//		System.arraycopy( Util.int2Word(0x3FFF),0, data, 408, 2);
		
		/*
		 * Lets see if this is the language:
		 */
		
		// ESTE NO ES
		//System.arraycopy( Util.int2Word(player.getLang()), 0, data, 418, 2);
		// ESTE NO ES
		//System.arraycopy( Util.int2Word(player.getLang()), 0, data, 408, 2);
		
		System.arraycopy( Util.int2Word(player.getLang()), 0, data, 410, 2);
		/*
		System.arraycopy( Util.int2Word(player.getLang()), 0, data, 412, 2);
		System.arraycopy( Util.int2Word(player.getLang()), 0, data, 414, 2);
		System.arraycopy( Util.int2Word(player.getLang()), 0, data, 416, 2);
		*/
		
		System.arraycopy( Util.int2Word( player.getTeam1()), 0, data, 420, 2 );
		System.arraycopy( Util.int2Word( player.getTeam2()), 0, data, 418, 2 );
		System.arraycopy( Util.int2Word( player.getTeam3()), 0, data, 416, 2 );
		System.arraycopy( Util.int2Word( player.getTeam4()), 0, data, 414, 2 );
		System.arraycopy( Util.int2Word( player.getTeam5()), 0, data, 412, 2 );
		
		setData(data);
	}
} public class MessageBlockGamePlayerGroupList extends MessageBlockMulti {
	/*
	 * HEADER - PLAYERID - 37 bytes HEADER_2 - NOMBRE GRUPO (51 bytes)
	 */
	static final int MAX_PLAYERS_PER_BLOCK = 8;
	static final byte[] HEADER = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00 };
	static final byte[] PLY_HEADER = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00 };

	// completamente inventado
	static final byte[] ITEM_TAIL = { (byte) 0x00, (byte) 0x60, (byte) 0xDC,
			(byte) 0x2E, (byte) 0x34, (byte) 0xD7, (byte) 0x69, (byte) 0x64,
			(byte) 0x0B, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };

	// static final byte[]

	// 4 (zeros) + 48 (nombre_player) + 4 (id-grupo) + 48 (nombre grupo) + 126
	// (traperadas)

	public MessageBlockGamePlayerGroupList(PlayerList plist,
			RoomList rlist, int qId) {
		super();
//		header.setQuery(qId);
//		header.setSeq(seq);

		PlayerInfo players[] = plist.getPlayerArray();
		
		//byte[] data = new byte[(4 + 48 + 4 + 48 + 23) * players.length];
		

		int numBlocks = (players.length / MAX_PLAYERS_PER_BLOCK) + 1;
		
		for( int j = 0; j < numBlocks; j++ ) {
			int start = j*MAX_PLAYERS_PER_BLOCK;
			int end = j*MAX_PLAYERS_PER_BLOCK + MAX_PLAYERS_PER_BLOCK;
			if( j == (numBlocks - 1) ) end = players.length;
			byte[] data = new byte[(4 + 48 + 4 + 48 + 23) * (end - start)];
			Arrays.fill(data, (byte) 0x00);
			int idx = 0;
			for (int i = start; i < end; i++) {
				String plName = players[i].getName();
				long plId = players[i].getId();
				String grName = players[i].getGroupName();
				long grId = players[i].getGid();
				byte[] b_grid = Util.long2Word(grId);
				byte[] b_plid = Util.long2Word(plId);
	
				System.arraycopy(b_plid, 0, data, idx, b_plid.length);
				idx += b_plid.length;
				System.arraycopy(plName.getBytes(), 0, data, idx,
						plName.getBytes().length);
				idx += 48;
				System.arraycopy(b_grid, 0, data, idx, b_grid.length);
				idx += b_grid.length;
				System.arraycopy(grName.getBytes(), 0, data, idx,
						grName.getBytes().length);
				idx += 48;
				/*
				 * Lo mismo de antes este byte indica si el tio pertenece a un
				 * grupo, el siguiente la divisi�n
				 */
				if (grId > 0) {
					data[idx] = Util.int2Word(players[i].getInvitation())[1];
				}
				
				data[idx+1] = Util.int2Word(players[i].getDivision())[1];
				/*
				 * System.arraycopy(ITEM_TAIL, 0, data, idx, ITEM_TAIL.length ); idx +=
				 * ITEM_TAIL.length;
				 */
				// data[idx] = (byte)0x01;
				byte[] b_rid = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00,
						(byte) 0x00 };
				RoomInfo r = rlist.searchPlayer(plId);
				if (r != null)
					b_rid = Util.long2Word(r.getId());
				System.arraycopy(b_rid, 0, data, idx + 2, b_rid.length);
				/*
				 * Despues del room_id podría venir el nivel del grupo 00 00 niv1 niv2 (4 bytes)
				 */
				System.arraycopy(Util.int2Word(0x3FFF), 0, data, idx+8, 2);
				System.arraycopy(Util.int2Word(players[i].getCategory()), 0, data, idx + 10, 2 );
				
				//System.arraycopy(Util.int2Word(1), 0, data, idx + 12, 2 );
				//Mentira es posible que el nivel del grupo vaya aqui!
				System.arraycopy(Util.int2Word(0x3FFF), 0, data, idx+12, 2);
				
				System.arraycopy(Util.int2Word(players[i].getVictories()), 0, data, idx + 14, 2 );
				System.arraycopy(Util.int2Word(players[i].getDefeats()), 0, data, idx + 16, 2 );
				System.arraycopy(Util.int2Word(players[i].getDraws()), 0, data, idx + 18, 2 );
				idx += 23;
			}
			MessageBlock mb = new MessageBlock();
			mb.header.query = qId;
			mb.setData(data);
			addBlock(mb);
		}
		//setData( qId, seq, data);
	}
} public class MessageBlockGamePlayerId extends MessageBlock {
	static final byte[] MSG_DATA = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00 };

	public MessageBlockGamePlayerId(long pid, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = MSG_DATA;
		byte[] b_pid = Util.long2Word(pid);
		System.arraycopy(b_pid, 0, data, 4, b_pid.length);

		setData(data);
	}
} public class MessageBlockGamePlayerReady extends MessageBlock {

	/*
	 * Primero van los ids de los contendientes, luego los id de grupo de cada
	 * uno
	 */
	byte[] DUNNO = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x1C,
			(byte) 0x07 };

	public MessageBlockGamePlayerReady(long pid, int ready, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[5];
		Arrays.fill(data, (byte) 0x00);
		byte[] b_pid = Util.long2Word(pid);

		System.arraycopy(b_pid, 0, data, 0, b_pid.length);
		data[4] = Util.int2Word( ready )[1];
		setData(data);
	}
} public class MessageBlockGameRoomInfoShort extends MessageBlock {

	// completamente copiado
	static final byte[] ITEM_TAIL = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xFF,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0xFF };

	// static final byte[]

	public MessageBlockGameRoomInfoShort(RoomInfo room, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[24];
		System.arraycopy(ITEM_TAIL, 0, data, 0, ITEM_TAIL.length);

		for (int j = 0; j < room.getNumPlayers(); j++) {
			long pId = room.getPlayerId(j);
			byte[] b_pid = Util.long2Word(pId);
			byte[] b_pos = Util.int2Word(j);
			System.arraycopy(b_pid, 0, data, j * 6, b_pid.length);
			// pos
			System.arraycopy(Util.int2Word(j), 1, data, (6 * j) + 4, 1);
			System.arraycopy(Util.int2Word(room.getPlayerSel(j)), 1, data,
					(6 * j) + 5, 1);
		}

		setData(data);
	}
} public class MessageBlockGameRoomList extends MessageBlockMulti {

	static final int MAX_ROOMS_PER_BLOCK = 6;
	// completamente copiado
	static final byte[] ITEM_TAIL = {
			// -------------------------------------- ALL COVERED
			// -------------------------------------------------------------------
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
			(byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0xFF,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0xFF,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0xFF,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0xFF, (byte) 0x00, (byte) 0x00, (byte) 0xFF,
			//     --- TEAM 2 ----           g1            g2           g3           g4           g5           ---- TEAM 1 ---              g1
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
			//    g2           g3            g4            g5                      PWD              -------- FOUR BYTES ------------
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };

	public MessageBlockGameRoomList(RoomInfo rooms[], int qId) {
		super();
//		header.setQuery(qId);
//		header.setSeq(seq);

		//byte[] data = new byte[(4 + 2 + 65 + 60) * rooms.length];
		//Arrays.fill(data, (byte) 0x00);

		int numBlocks = (rooms.length / MAX_ROOMS_PER_BLOCK) + 1;
		
		for( int k = 0; k < numBlocks; k++ ) {
			int start = k*MAX_ROOMS_PER_BLOCK;
			int end = k*MAX_ROOMS_PER_BLOCK + MAX_ROOMS_PER_BLOCK;
			if( k == (numBlocks - 1) ) end = rooms.length;
			
			byte[] data = new byte[(4 + 2 + 65 + 60) * (end - start)];
			Arrays.fill(data, (byte) 0x00);
			int idx = 0;
			for (int i = start; i < end; i++) {
				String rName = rooms[i].getName();
				long rId = rooms[i].getId();
				int rType = rooms[i].getType();
	
				if (rName.length() > 64) {
					rName = rName.substring(0, 64);
				}
	
				byte[] b_rid = Util.long2Word(rId);
				byte[] b_rtype = Util.int2Word(rType);
				/*
				 * Ni half ni ostias esto tiene que ser la fase!!! valgame dios
				 */
				byte[] b_half = Util.int2Word(rooms[i].getHalf());
				//byte[] b_phase = Util.int2Word(rooms[i].getGamePhase());
				//Puede haber un type especial para los kekitos? nu se.
				
				// if( rooms[i].getGamePhase() == 4 ) b_rtype =
				// Util.int2Word(0x00000703);
	
				byte[] b_rname = rName.getBytes();
	
				System.arraycopy(b_rid, 0, data, idx, b_rid.length);
				idx += b_rid.length;
	
				data[idx++] = b_rtype[1];
				data[idx++] = b_half[1];
	
				// System.arraycopy(b_rtype, 0, data, idx, b_rtype.length);
				// idx += b_rtype.length;
	
				System.arraycopy(rName.getBytes(), 0, data, idx,
						rName.getBytes().length);
				idx += 64;
	
				data[idx++] = Util.int2Word(rooms[i].getTime())[1];
	
				System.arraycopy(ITEM_TAIL, 0, data, idx, ITEM_TAIL.length);
	
				for (int j = 0; j < rooms[i].getNumPlayers(); j++) {
					long pId = rooms[i].getPlayerId(j);
					byte[] b_pid = Util.long2Word(pId);
					System.arraycopy(b_pid, 0, data, idx + (10 * j), b_pid.length);
	
					// Esto determina quien es el due�o del salon comentado por
					// efectos extra�os
					// Bastante interesante deberemos mirarlo mas
					if (j == 0)
						data[idx + (10 * j) + 4] = (byte) 0x01;
			
					/*
					 * Cojones en +5 me falta un byte? hay un 01 para el lider también.
					 * No se que es, pero si le pongo un 01 a todos la jodemos bien. Podría ser
					 * el ready status?. Dwarf lo llama "random?".
					 */
					//data[idx + (10*j) + 5] = (byte)0x01;
					
					
					data[idx + (10 * j) + 6] = Util.int2Word(rooms[i]
							.getPlayerTeam(pId))[1];
					data[idx + (10 * j) + 7] = Util
							.int2Word(rooms[i].getSpect(pId))[1];
					data[idx + (10 * j) + 8] = Util.int2Word(j)[1];
					data[idx + (10 * j) + 9] = Util.int2Word(rooms[i]
							.getPlayerSel(j))[1];
				}
				idx += 40;
				byte[] b_team2 = Util.int2Word(rooms[i].getTeam2());
				System.arraycopy(b_team2, 0, data, idx, b_team2.length);
	
				idx += 2;
				for (int j = 0; j < 5; j++) {
					data[idx++] = Util.int2Word(rooms[i].getTeam2Goals(j))[1];
				}
	
				byte[] b_team1 = Util.int2Word(rooms[i].getTeam1());
				System.arraycopy(b_team1, 0, data, idx, b_team1.length);
				idx += 2;
				for (int j = 0; j < 5; j++) {
					data[idx++] = Util.int2Word(rooms[i].getTeam1Goals(j))[1];
				}
	
				idx += 6;
	
				byte[] b_last = Util.long2Word(rooms[i].getLastBytes());
	//			System.arraycopy(b_last, 0, data, idx - 4, b_last.length);
				if( rooms[i].getMatchSettings() != null ) {
					/* 00 02 00 00 va perfecto sin cosas raras. */
					data[idx-4] = 0x00; // Primer byte a 1, a 02 y a 03 significa lo mismo también significa partido de grupos 
									    // A 0 es partido normal.
					data[idx-3] = 0x02; // Esto es si la gente puede chatear o no en el partido.
					data[idx-2] = 0x00; // No se lo que hace esto a 1 va bien. (pruebo con ceros)...
					data[idx-1] = 0x00; // No se lo que hace esto a 1 va bien. Aver 02? 00 01 y 02 parece que hagan lo mismo
					//	System.arraycopy(rooms[i].getMatchSettings(), 0, data, idx - 4, 4);
				}
				if (rooms[i].hasPassword())
					data[idx - 5] = (byte) 0x01;
				else
					data[idx - 5] = (byte) 0x00;
			}
			
			/*
			 * We don't want to add an empty 4302 block, so we check if there
			 * are rooms in this block beforehand.
			 */
			if( data.length > 0 ) {
				MessageBlock mb = new MessageBlock();
				mb.header.query = qId;
				mb.setData(data);
				addBlock(mb);
			}
		}
		//setData(qId, seq, data);
	}
} public class MessageBlockGameRoomOptionSel extends MessageBlock {
	static final byte[] HEADER = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00 };

	public MessageBlockGameRoomOptionSel(int option, int sel, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[HEADER.length + 2];

		System.arraycopy(HEADER, 0, data, 0, HEADER.length);

		data[HEADER.length] = Util.int2Word(option)[1];
		data[HEADER.length + 1] = Util.int2Word(sel)[1];

		setData(data);
	}
} public class MessageBlockGameRoomPos extends MessageBlock {
	static final byte[] MSG_DATA = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00, (byte) 0x00 };

	public MessageBlockGameRoomPos(int pos, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = MSG_DATA;
		data[4] = Util.int2Word(pos)[1];
		setData(MSG_DATA);
	}
} public class MessageBlockGameStart1 extends MessageBlock {

	/*
	 * Primero van los ids de los contendientes, luego los id de grupo de cada
	 * uno
	 */
	byte[] DUNNO = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x1C,
			(byte) 0x07 };

	public MessageBlockGameStart1(RoomInfo room, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[37];
		Arrays.fill(data, (byte) 0x00);
		data[0] = (byte) 0x02; // Fue igual en un partido de 1 contra 1 que 1
								// contra 2
		int idx = 1;
		for (int i = 0; i < room.getNumPlayersOnSel(); i++) {
			long pid = room.getPlayerIdOnSel(i);
			byte b_pid[] = Util.long2Word(pid);
			System.arraycopy(b_pid, 0, data, idx, b_pid.length);
			idx += b_pid.length;
		}
		idx = 4 * 4 + 1;
		for( int i = 0; i < room.getNumPlayersOnSel(); i++) {
			PlayerInfo p = room.getPlayerFromPid(room.getPlayerIdOnSel(i));
			byte b_level[] = Util.int2Word(p.getGroupLevel());
			System.arraycopy(b_level, 0, data, idx + 2, 2);
			idx+=4;
		}

		setData(data);
	}
} public class MessageBlockGameStart4344 extends MessageBlock {

	public MessageBlockGameStart4344(int phase, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[] { (byte) phase };

		setData(data);
	}
} public class MessageBlockLong extends MessageBlock {

	public MessageBlockLong(long value, int qId) {
		super();
		header.setQuery(qId);
		setData(Util.long2Word(value));
	}
} public class MessageBlockMenuFriends extends MessageBlock {

	public MessageBlockMenuFriends(PlayerInfo players[], int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[(5 + 48) * players.length];
		int idx = 0;
		for (int i = 0; i < players.length; i++) {
			PlayerInfo pi = players[i];
			String plName = pi.getName();
			long id = pi.getId();

			if (plName.length() > 48)
				plName = plName.substring(0, 48);

			byte[] b_header = buildPlyHeader(pi);
			byte[] b_name = new byte[48];
			Arrays.fill(b_name, (byte) 0x00);
			System.arraycopy(plName.getBytes(), 0, b_name, 0,
					plName.getBytes().length);

			System.arraycopy(b_header, 0, data, idx, b_header.length);
			idx += b_header.length;
			System.arraycopy(b_name, 0, data, idx, b_name.length);
			idx += b_name.length;
		}

		setData(data);
	}

	byte[] buildPlyHeader(PlayerInfo player) {
		byte[] ret = new byte[5];
		byte[] b_id = Util.long2Word(player.getId());
		System.arraycopy(b_id, 0, ret, 0, 4);
		ret[4] = (byte)Util.int2Word(player.getInvitation())[1];

		return (ret);
	}
} public class MessageBlockMenuGroupInfo extends MessageBlock {

	static final byte[] HEADER = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00 };
	/*
	 * Ojo modifico esto, donde hay un 1 voy a poner un 3FFF es un nivel total
	 * del grupo, no se si servirá de algo.
	 */
	static final byte[] HEADER_2 = { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0x80,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
			(byte) 0xC0, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
//			(byte) 0x00, (byte) 0x00, (byte) 0x3F, (byte) 0xFF, (byte) 0x00,		
													// Que coño será este uno?
//			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00,
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00,

											 // El ultimo byte indica slots por
											 // Habitación para mi SI!
			(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x04 };

	public MessageBlockMenuGroupInfo(PlayerInfo p, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[4 + 4 + 37 + 51];
		Arrays.fill(data, (byte) 0x00);

		String grName = p.getGroupName();

		if (grName.length() > 48) {
			grName = grName.substring(0, 48);
		}

		byte[] b_grname = grName.getBytes();
		byte[] b_playerid = Util.long2Word(p.getId());
		int idx = 0;
		System.arraycopy(HEADER, 0, data, idx, HEADER.length);
		idx += HEADER.length;
		System.arraycopy(b_playerid, 0, data, idx, b_playerid.length);
		idx += b_playerid.length;
		System.arraycopy(HEADER_2, 0, data, idx, HEADER_2.length);
		idx += HEADER_2.length;
		/*
		 * Salas de 8, pero no van igual!
		 */
	//		if( p.isAdmin() ) {
//			data[idx-1] = 0x08;
//		}
		System.arraycopy(b_grname, 0, data, idx, b_grname.length);

		/*
		 * Probamos a poner los dos ultimos bytes de los cuales no tengo información
		 * a FF FF
		 */

		data[data.length-2] = (byte)0xFF;
		data[data.length-1] = (byte)0xFF;
		
		setData(data);
	}
} public class MessageBlockMenuMenuList extends MessageBlock {

	public MessageBlockMenuMenuList(ChannelList channels, PlayerInfo player, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[2 + ((1 + 32 + 2) * channels.size())];
		Arrays.fill(data, (byte) 0x00);
		byte b_header[] = Util.int2Word(channels.size());
		System.arraycopy(b_header, 0, data, 0, 2);
		int idx = 2;
		for (int i = 0; i < channels.size(); i++) {
			String chName = channels.getChannel(i).getName();
			byte chType = channels.getChannel(i).getType();
			int numPlayers = channels.getChannel(i).getPlayerList().getNumPlayers();

			/*
			 * Useless more than 100 player is not supported by the client
			 */
//			if( numPlayers >= 100 && player.getAdmin() == 1 )
//				numPlayers = 99;
			
			data[idx] = chType;
			idx++;
			if (chName.length() > 32) {
				chName = chName.substring(0, 32);
			}
			byte[] b_chname = chName.getBytes();
			byte[] b_numplayers = Util.int2Word(numPlayers);

			System.arraycopy(b_chname, 0, data, idx, b_chname.length);
			idx += 32;
			System.arraycopy(b_numplayers, 0, data, idx, b_numplayers.length);
			idx += b_numplayers.length;
		}
		setData(data);
	}
} public class MessageBlockMenuPlayerGroup extends MessageBlock {

	// static final byte[]

	// 4 (zeros) + 48 (nombre_player) + 4 (id-grupo) + 48 (nombre grupo) + 126
	// (traperadas)

	/*
	 * ESTO SON LAS VICTORIAS EMPATES Y TODO ESE TIPO DE COSAS QUE SE ASOCIAN A
	 * UN JUGAGOR Y QUE SALEN EN UNA SALA DE JUEGO
	 */

	static final byte[] TAIL = new byte[] { 
//		              -- DIV --
		(byte) 0x01, (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, 
//			-- points??? --					-- CAT ---		  -- MATCHES PLYD --		 -- VICTORIES ---
		(byte) 0x06, (byte) 0xAA, (byte) 0x01, (byte) 0xF6, (byte) 0x00, (byte) 0x12, (byte) 0x00, (byte) 0x07, 
//		    -- DEFEATS --               ---- DRAWS ---       
		(byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x04, (byte) 0x00 };

	public MessageBlockMenuPlayerGroup(PlayerInfo player,
			long rid, int qId) {
		super();
		header.setQuery(qId);

		byte[] data = new byte[4 + 48 + 4 + 48 + 23];
		Arrays.fill(data, (byte) 0x00);

		String plName = player.getName();
		String grName = player.getGroupName();
		long plId = player.getId();
		long grId = player.getGid();
		byte[] b_plid = Util.long2Word(plId);
		byte[] b_grid = Util.long2Word(grId);
		System.arraycopy(b_plid, 0, data, 0, b_plid.length);
		System.arraycopy(plName.getBytes(), 0, data, 4,
				plName.getBytes().length);
		System.arraycopy(b_grid, 0, data, 4 + 48, b_grid.length);
		System.arraycopy(grName.getBytes(), 0, data, 4 + 48 + 4, grName
				.getBytes().length);

		data[104] = (byte) 0x01;
		
		/*
		 * primer byte indica si el tio pertenece a un grupo, segundo la
		 * divisi�n un 01 funciona como pertenecer al grupo un 02 permite
		 * invitar a gente al grupo, no permite cambiar el comentario del grupo
		 * un 03 peta / no carga al principio un 04 Estoy solicitando
		 * incorporacion al grupo un 05 peta al darle a "mi grupo" un 06 no
		 * salgo y peta al darle a "mi grupo"
		 */
//		if (grId > 0)
//			data[4 + 48 + 4 + 48] = (byte) 0x02;
		
		data[4+48+4+48] = Util.int2Word(player.getInvitation())[1];
		
		data[105] = Util.int2Word( player.getDivision())[1];
		
		System.arraycopy( Util.long2Word( player.getPoints() ), 0, data, 110, 4 );
		System.arraycopy( Util.int2Word( player.getCategory() ), 0, data, 108 + 6, 2 );

		System.arraycopy( Util.int2Word( player.getMatchesPlayed() ), 0, data, 108 + 8, 2 );

		System.arraycopy( Util.int2Word( player.getVictories()), 0, data, 108 + 10, 2 );

		System.arraycopy( Util.int2Word( player.getDefeats()), 0, data, 108 + 12, 2 );

		System.arraycopy( Util.int2Word( player.getDraws()), 0, data, 108 + 14, 2 );
		// System.arraycopy(TAIL, 0, data, 4+48+4+48, TAIL.length);
		
		System.arraycopy( Util.int2Word( player.getLang()),1, data, 108 + 16 + 1, 1);

		/*
		 * GUESSING: De lo que me queda en pos + 2 va el room_id
		 */
		
		/*
		 * Aqui hay un hueco, lo del room_id no me lo creo ni yo. Voy a rellenarlo
		 * con FF's ULTIMA PRUEBA SI NO ES ESTO ME PEGO DOS TIROS
		 */
		
		System.arraycopy(Util.long2Word(0xFFFFFFFF), 0, data, 4+48+4+48+2, 4);
		
//		byte[] b_rid = Util.long2Word(rid);
//		System.arraycopy(b_rid, 0, data, 4 + 48 + 4 + 48 + 2, b_rid.length);
		setData(data);
	}
} public class MessageBlockMenuQ1 extends MessageBlock {
	static final byte[] MSG_DATA = { (byte) 0x38, (byte) 0x2B, (byte) 0x46,
			(byte) 0x47, (byte) 0x02, (byte) 0x4B, (byte) 0x2F, (byte) 0x68,
			(byte) 0x56, (byte) 0x28, (byte) 0x3F, (byte) 0x53, (byte) 0x10,
			(byte) 0x87, (byte) 0x32, (byte) 0xA0 };

	public MessageBlockMenuQ1(int qId) {
		super();
		header.setQuery(qId);
		setData(MSG_DATA);
	}
} public class MessageBlockVoid extends MessageBlock {
	public MessageBlockVoid(int qId) {
		super();
		header.setQuery(qId);
	}
} public class MessageBlockZero extends MessageBlock {
	static final byte[] MSG_DATA = { (byte) 0x00, (byte) 0x00, (byte) 0x00,
			(byte) 0x00 };

	public MessageBlockZero(int qId) {
		super();
		header.setQuery(qId);
		setData(MSG_DATA);
	}
} public interface MetaMessageBlock {
	public byte[] getData();

	public byte[] getBytes();

	// public void setData( byte[] data );
	public int getByteSize();

	public GenericHeader getHeader();
} public class PESConnection {

	public static final int DEBUG_MESSAGES_SAVED = 4;
	
	long pid;
	SocketChannel outChannel;
	OutputStream out;
	InputStream in;
	long seq;
	Socket socket;
	Logger logger;
	long requester;
	long recipient;

	Stack<Message> lastMessages;
	PlayerInfo pInfo;
	UserInfo uInfo;
	DataCdKeyAndPass cdkey;
	long lastKATime;
	byte[] readBuffer;
	MyByteBuffer writeBuffer;
	HTTPFetchUserInfoThread uInfoThread;
	boolean logEnabled;
	Message lastReadMessage;
	int debugErrorCode = -1;
	
	public PESConnection(Socket socket, boolean debug) throws IOException {
		seq = 1;
		requester = 0L;
		this.socket = socket;

		outChannel = 
			socket.getChannel();
		
		this.out = socket.getOutputStream();
		this.in = socket.getInputStream();

		logEnabled = debug;
		
		if( logEnabled ) { 
			logger = new Logger("log/pes6j_"
					+ socket.getInetAddress().getHostAddress());
		}
		lastMessages = new Stack<Message>();
		pInfo = null;
		cdkey = null;
		uInfo = null;
		readBuffer = new byte[0];
		lastKATime = (new Date()).getTime();
		pid = 0;
		writeBuffer = new MyByteBuffer( );
		uInfoThread = null;
		lastReadMessage = null;
	}
	
	public long getRecipient() {
		return recipient;
	}

	public void setRecipient(long recipient) {
		this.recipient = recipient;
	}

	public Message getLastReadMessage() {
		return( this.lastReadMessage );
	}
	
	public int getDebugErrorCode() {
		return debugErrorCode;
	}

	public void setDebugErrorCode(int debugErrorCode) {
		this.debugErrorCode = debugErrorCode;
	}

	public void setHTTPThread( HTTPFetchUserInfoThread uInfoThread ) {
		this.uInfoThread = uInfoThread;
	}
	
	public HTTPFetchUserInfoThread getHTTPThread( ) {
		return( uInfoThread );
	}
	
	public boolean isFetchingUserInfo() {
		if( uInfoThread == null ) return false;
		if( uInfoThread.getStatus() == HTTPFetchUserInfoThread.FETCHING ) return( true );
		return( false );
	}
	
	public boolean userInfoFetched() {
		if( uInfoThread == null ) return false;
		if( uInfoThread.getStatus() == HTTPFetchUserInfoThread.FETCHED ) return( true );
		return( false );
	}
	
	public void fetchUserInfo() {
		if( uInfoThread != null && uInfoThread.getStatus() == HTTPFetchUserInfoThread.FETCHED ) 
			uInfo = uInfoThread.getUserInfo();
		uInfoThread = null;
	}
	
	public SocketChannel getSocketChannel() {
		return( this.outChannel );
	}
	
	public UserInfo getUserInfo() {
		return( uInfo );
	}
	
	public void setUserInfo( UserInfo uInfo ) {
		this.uInfo = uInfo;
	}
	
	public long getLastKATime( ) {
		return( lastKATime );
	}
	
	public void setLastKATime(long time) {
		lastKATime = time;
	}
	
	public void setPlayerInfo( PlayerInfo pg ) {
		pInfo = pg;
	}
	
	public PlayerInfo getPlayerInfo( ) {
		return( pInfo );
	}
	
	public void setCdkey( DataCdKeyAndPass cdkey ) {
		this.cdkey = cdkey;
	}
	
	public DataCdKeyAndPass getCdkey( ) {
		return( this.cdkey );
	}
	
	public synchronized Socket getSocket() {
		return( socket );
	}
	
	public synchronized void setRequester( long pid ) {
		requester = pid;
	}
	
	public synchronized long getRequester( ) {
		return( requester );
	}

	public synchronized void incSeq() {
		seq++;
	}

	public synchronized long getSeqPP() {
		return (seq++);
	}

	public synchronized OutputStream getOutputStream() {
		return (out);
	}

	public synchronized void streamWriteEncoded(Message mes) throws IOException {
		for (int i = 0; i < mes.getNumBlocks(); i++) {
			MetaMessageBlock mb = mes.getBlock(i);
			if (mb instanceof MessageBlockMulti) {
				for (int j = 0; j < ((MessageBlockMulti) mb).getNumBlocks(); j++) {
					MessageBlock b = ((MessageBlockMulti) mb).getBlock(j);
					GenericHeader h = b.getHeader();
					h.setSeq(seq++);
					b.setHeader(h);
					((MessageBlockMulti) mb).setBlock(j, b);
				}
			} else {
				GenericHeader h = ((MessageBlock) mb).getHeader();
				h.setSeq(seq++);
				((MessageBlock) mb).setHeader(h);
			}
			mes.setBlock(i, mb);
		}
		if( lastMessages.size() >= DEBUG_MESSAGES_SAVED )
			lastMessages.remove(0);
		
		lastMessages.push(mes);
		
		//Arrays.push( lastMessages = mes;
		Util.streamWriteEncoded(out, mes.getBytes());
		/*
		 * Es muy salvaje, no podemos hacer esto.
		 */
		//log( "WRITE: ", mes );
	}
	
	public boolean hasRemaining() {
		return writeBuffer.hasRemaining();
	}
	
	public boolean canWrite() {
		return( (outChannel.validOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE );
	}
	
	public int write() throws IOException {
		
		
		if( outChannel.isConnected() && canWrite() ) {	
			ByteBuffer buf = ByteBuffer.wrap( writeBuffer.get() );
			//writeBuffer.flip();
			
			/*
			 * TENGO MULTIPLES EXCEPCIONES DE BROKEN PIPE, DEBERÍA CONTROLARLAS
			 */
	
			int ret = outChannel.write( buf );
			if( ret > 0 ) writeBuffer.removeHead( ret );
			return( ret );
		} else {
			log( "Can't send data, either lost connection or just can't write to him" );
			return( 0 );
		}
	}
	
	public void writeToBuffer( byte[] data ) throws IOException {
		writeBuffer.put( data );
	}
	
	public synchronized void streamWriteEncodedNIO(Message mes) throws IOException {		
		for (int i = 0; i < mes.getNumBlocks(); i++) {
			MetaMessageBlock mb = mes.getBlock(i);
			if (mb instanceof MessageBlockMulti) {
				for (int j = 0; j < ((MessageBlockMulti) mb).getNumBlocks(); j++) {
					MessageBlock b = ((MessageBlockMulti) mb).getBlock(j);
					GenericHeader h = b.getHeader();
					h.setSeq(seq++);
					writeToBuffer( Util.konamiEncode(b.getBytes()) );
				}
			} else {
				GenericHeader h = ((MessageBlock) mb).getHeader();
				h.setSeq(seq++);

				writeToBuffer( Util.konamiEncode(mb.getBytes()) );
			}
		}
		if( lastMessages.size() >= DEBUG_MESSAGES_SAVED )
			lastMessages.remove(0);
		
		lastMessages.push(mes);
	}

	public synchronized void logLastSentMessage(String message) throws IOException {
		if( logEnabled ) logger.log( message, lastMessages );
	}
	
	public synchronized void log(String message, Message mes) throws IOException {
		if( logEnabled ) logger.log(message, mes);
	}
	
	public synchronized void log( Exception ex ) throws IOException {
		if( logEnabled ) logger.log( ex );
	}
	
	public synchronized void log(String message) throws IOException {
		if( logEnabled ) logger.log( message );
	}

	public synchronized void streamWriteBlockEncoded(MessageBlock mb) throws IOException {
		GenericHeader h = mb.getHeader();
		h.setSeq(seq++);
		mb.setHeader(h);
		Util.streamWriteEncoded(out, mb.getBytes());
	}
	
	public synchronized byte[] streamReadDecoded() throws IOException {
		ByteBuffer buf = ByteBuffer.allocate(4096);
			//byte buf[] = new byte[4096];
			int len = outChannel.read(buf);

			if (len == -1) throw new IOException( "Read -1 bytes, connection closed" );

			
			byte ret[] = new byte[len];
			System.arraycopy( buf.array(), 0, ret, 0, len);
			
			return (Util.konamiDecode(ret));
	}
	
	public synchronized byte[] streamRead() throws IOException {
		ByteBuffer buf = ByteBuffer.allocate(4096);
			//byte buf[] = new byte[4096];
			int len = outChannel.read(buf);

			if (len == -1) throw new IOException( "Read -1 bytes, connection closed" );

			
			byte ret[] = new byte[len];
			System.arraycopy( buf.array(), 0, ret, 0, len);
			
			return (ret);
	}
	
//	public synchronized Message networkReadMessage() throws IOException {
//		byte[] buf = streamReadDecoded();
//		
//		log("READ:");
//		log(Util.toHex(buf));
//		/*
//		 * Si teniamos algo leido anteriormente en el buffer interno lo
//		 * concatenamos
//		 */
//		byte[] full = new byte[readBuffer.length + buf.length];
//		System.arraycopy( readBuffer, 0, full, 0, readBuffer.length );
//		System.arraycopy( buf, 0, full, readBuffer.length, buf.length );
//		
//		Message mes = Message.getInstance(full);
//		if( mes != null ) {
//			readBuffer = new byte[0];
//		} else
//			readBuffer = full;
//		return( mes );
//	}
	
	public synchronized Message networkReadMessage() throws IOException {
		byte[] buf = streamRead();
		byte[] fullDecoded;
		

		/*
		 * Si teniamos algo leido anteriormente en el buffer interno lo
		 * concatenamos
		 */
		byte[] full = new byte[readBuffer.length + buf.length];
		System.arraycopy( readBuffer, 0, full, 0, readBuffer.length );
		System.arraycopy( buf, 0, full, readBuffer.length, buf.length );
		
		fullDecoded = Util.konamiDecode(full);
		
		Message mes = Message.getInstance(fullDecoded);
		if( mes != null ) {
			readBuffer = new byte[0];
			log("READ:");
			log(Util.toHex(fullDecoded));
			this.lastReadMessage = mes;
		} else
			readBuffer = full;
		return( mes );	
	}
	
	

	public synchronized void close() throws IOException {
		try {
			out.close();
			outChannel.close();
			if( logEnabled ) logger.close();
		} catch( Exception ex ) {
			
		}
	}

	public synchronized void setPid(long pid) {
		this.pid = pid;
	}

	public synchronized long getPid() {
		return (pid);
	}
} public class PESConnectionList {
	Vector<PESConnection> cons;

	public PESConnectionList() {
		cons = new Vector<PESConnection>();
	}
	
	public Enumeration<PESConnection> elements() {
		return(cons.elements());
	}

	public synchronized void addConnection(PESConnection con) {
		cons.add(con);
	}

	public synchronized void removeConnection(PESConnection con) {
		cons.remove(con);
	}

	public synchronized void sendToAllEncoded(Message mes) throws IOException {
		for (int i = 0; i < cons.size(); i++) {
			((PESConnection) cons.get(i)).streamWriteEncoded(mes);
		}
	}
	
	public synchronized void sendToAllEncodedNIO(Message mes) throws IOException {
		for (int i = 0; i < cons.size(); i++) {
			((PESConnection) cons.get(i)).streamWriteEncodedNIO(mes);
		}
	}
	
	public void sendToEncodedNIO( PESConnection[] conList, Message mes ) throws IOException {
		for( int i = 0; i < conList.length; i++ ) {
			conList[i].streamWriteEncodedNIO(mes);
		}
	}
	
	public void sendToChannelEncodedNIO( ChannelInfo chan, Message mes ) throws IOException {
		PlayerList players = chan.getPlayerList();
		for( int i = 0; i < players.getNumPlayers(); i++ ) {
			PESConnection c = players.getPlayer( i ).getConnection();
			try {			
				c.streamWriteEncodedNIO( mes );
			} catch( IOException ex ) {
			}
		}
	}
	
	/*
	 * No se lo enviamos a los que están ya en una sala
	 */
	public void sendToChannelNotInGameEncodedNIO( ChannelInfo chan, Message mes ) throws IOException {
		PlayerList players = chan.getPlayerList();
		RoomList rooms = chan.getRoomList();
		for( int i = 0; i < players.getNumPlayers(); i++ ) {
			PlayerInfo p = players.getPlayer(i);
			RoomInfo r = rooms.searchPlayer( p.getId() );
			if( r == null ||r.getType() <= 1 || r.getPlayerSelFromPid( p.getId() ) == (0x00FF) ) { 
				PESConnection c = p.getConnection();
				try {			
					c.streamWriteEncodedNIO( mes );
				} catch( IOException ex ) {
				}
			}
		}
	}
	
	
	/*
	 * No se lo enviamos a los que están ya en una sala
	 */
	public void sendToChannelNotInRoomEncodedNIO( ChannelInfo chan, Message mes ) throws IOException {
		PlayerList players = chan.getPlayerList();
		RoomList rooms = chan.getRoomList();
		for( int i = 0; i < players.getNumPlayers(); i++ ) {
			PlayerInfo p = players.getPlayer(i);
			RoomInfo r = rooms.searchPlayer( p.getId() );
			if( r == null ) { 
				PESConnection c = p.getConnection();
				try {			
					c.streamWriteEncodedNIO( mes );
				} catch( IOException ex ) {
				}
			}
		}
	}

	public synchronized PESConnection searchConnection(long pid) {
		for (int i = 0; i < cons.size(); i++) {
			PESConnection c = (PESConnection) cons.get(i);
			if (c.getPid() == pid)
				return (c);
		}
		return (null);
	}
	
	public synchronized PESConnection searchConnection( Socket socket ) {
		for (int i = 0; i < cons.size(); i++) {
			PESConnection c = (PESConnection) cons.get(i);
			if (c.getSocket() == socket)
				return (c);
		}
		return (null);
	}

	public void sendEncodedTo(long[] pids, Message mes) throws IOException {
		for (int i = 0; i < pids.length; i++) {
			PESConnection c = searchConnection(pids[i]);
			if (c != null) {
				c.streamWriteEncoded(mes);
			}
		}
	}
	
	public void sendEncodedToNIO(long[] pids, Message mes) throws IOException {
		for (int i = 0; i < pids.length; i++) {
			PESConnection c = searchConnection(pids[i]);
			if (c != null) {
				c.streamWriteEncodedNIO(mes);
			}
		}
	}
	
	public void sendEncodedToGroup( long gid, Message mes ) throws IOException {
		for( int i = 0; i < size(); i++ ) {
			PESConnection c = cons.get(i);
			if( c.getPlayerInfo() != null && 
				c.getPlayerInfo().getGid() != 0 && 
				c.getPlayerInfo().getGid() == gid) {
				c.streamWriteEncodedNIO(mes);
			}
		}
	}
	
	public int size() {
		return( cons.size() );
	}
} public class PlayerInfo {
	long id;
	
	int category;
	long points;
	int matchPoints;
	int matchesPlayed;
	int victories;
	int defeats;
	int draws;
	int winningStreak;
	int bestStreak;
	int decos;
	int division;
	Stack<Integer> teams;
	long goalsScored;
	long goalsReceived;
	long timePlayed;
	long lastLogin;
	long position;
	int oldCategory;
	long oldPoints;
	int lang;
	int groupMatches;
	int groupLevel;
	int invitation;
	long gid;
	String groupName;
	int serverId;
	int lobbyId;
	String lobbyName;
	int lobbyType;
	
	String name;
	String comment;
	byte[] settings;
	boolean logged;
	int admin;
	IpInfo ip;
	PESConnection con;
	
//	public PlayerInfo clone() {
//		PlayerInfo c = new PlayerInfo( id, name );
//		
//		c.category = category;
//		c.points = points;
//		c.matchPoints = matchPoints;
//		c.matchesPlayed = matchesPlayed;
//		c.victories = victories;
//		c.defeats = defeats;
//		c.draws = draws;
//		c.winningStreak = winningStreak;
//		c.bestStreak = bestStreak;
//		c.decos = decos;
//		c.division = division;
//		c.teams = teams;
//		c.goalsScored = goalsScored;
//		c.goalsReceived = goalsReceived;
//		c.timePlayed = timePlayed;
//		c.lastLogin = lastLogin;
//		c.position = position;
//		c.oldCategory = oldCategory;
//		c.oldPoints = oldPoints;
//		c.lang = lang;
//		c.groupMatches = groupMatches;
//		c.invitation = invitation;
//		c.gid = gid;
//		c.groupName = groupName;
//		c.serverId = serverId;
//		c.lobbyId = lobbyId;
//		c.lobbyName = lobbyName;
//		c.lobbyType = lobbyType;
//		c.comment = comment;
//
//		c.settings = settings;
//		//byte[] settings;
//		c.logged = logged;
//		c.admin = admin;
//		c.ip = ip;
//		c.con = con;
//		//IpInfo ip;
//		//PESConnection con;
//		
//		return( c );
//	}
	
	public PlayerInfo(long id, String name) {
		this.name = name;
		this.category = 500;
		this.oldCategory = 500;
		this.points = 0;
		this.oldPoints = 0;
		this.matchesPlayed = 0;
		this.lang = 0;
		
		victories = 0;
		defeats = 0;
		draws = 0;
		winningStreak = 0;
		bestStreak = 0;
		decos = 0;
		goalsScored = 0;
		goalsReceived = 0;
		timePlayed = 0;
		division = 2;
		invitation = 0;
		gid = 0;
		
		lastLogin = (new Date()).getTime();
		
		teams = new Stack<Integer>();
		
		this.id = id;
		this.logged = false;
		this.comment = "";
		this.admin = 0;
		
		try {
			FileInputStream fi = new FileInputStream("mis_messages.dec");
			byte[] defSettings = new byte[(int) new File("mis_messages.dec")
					.length()];
			fi.read(defSettings);
			fi.close();
			Message mes = new Message(defSettings);
			MessageBlockLoginMessagesList mb = new MessageBlockLoginMessagesList(
					mes);
			this.settings = mb.getData();
		} catch (IOException ex) {
		}
	}
	
	public long getGid() {
		return gid;
	}

	public int getGroupLevel() {
		return groupLevel;
	}

	public void setGroupLevel(int groupLevel) {
		this.groupLevel = groupLevel;
	}

	public int getLobbyType() {
		return lobbyType;
	}

	public void setLobbyType(int lobbyType) {
		this.lobbyType = lobbyType;
	}

	public int getLobbyId() {
		return lobbyId;
	}

	public void setLobbyId(int lobbyId) {
		this.lobbyId = lobbyId;
	}

	public String getLobbyName() {
		return lobbyName;
	}

	public void setLobbyName(String lobbyName) {
		this.lobbyName = lobbyName;
	}

	public int getServerId() {
		return serverId;
	}

	public void setServerId(int serverId) {
		this.serverId = serverId;
	}

	public void setGid(long gid) {
		this.gid = gid;
	}
	
	public int getGroupMatches() {
		return groupMatches;
	}

	public void setGroupMatches(int groupMatches) {
		this.groupMatches = groupMatches;
	}

	public int getInvitation() {
		return invitation;
	}

	public String getGroupName() {
		return groupName;
	}

	public void setGroupName(String groupName) {
		this.groupName = groupName;
	}

	public void setInvitation(int invitation) {
		this.invitation = invitation;
	}

	public void setLang( int lang ) {
		this.lang = lang;
	}
	
	public int getLang( ) {
		return( lang );
	}
	
	public void setConnection( PESConnection con ) {
		this.con = con;
	}
	
	public PESConnection getConnection( ) {
		return( this.con );
	}
	
	public void setIpInfo( IpInfo ipInfo ) {
		ip = ipInfo;
	}
	
	public IpInfo getIpInfo( ) {
		return( ip );
	}

	public synchronized void setAdmin( int value ) {
		admin = value;
	}
	
	public synchronized int getAdmin( ) {
		return( admin );
	}
	
	public synchronized boolean isAdmin() {
		return( admin == 1 ? true : false );
	}
	
	public synchronized void setLoggedIn() {
		this.logged = true;
	}

	public synchronized void setComment(String comment) {
		this.comment = comment;
	}

	public synchronized String getComment() {
		return (this.comment);
	}

	public synchronized boolean isLoggedIn() {
		return (this.logged);
	}

	public synchronized void setSettings(byte[] data) {
		if (data != null && data.length > 0)
			this.settings = data;
	}
	
	public synchronized void resetSettings( ) {
		this.settings = new byte[0];
	}
	
	public synchronized void insertSettings( byte[] data ) {
		byte[] newSettings = new byte[this.settings.length + data.length];
		System.arraycopy( this.settings, 0, newSettings, 0, this.settings.length);
		System.arraycopy( data, 0, newSettings, this.settings.length, data.length);
		this.settings = newSettings;
	}

	public synchronized byte[] getSettings() {
		return (this.settings);
	}

	public synchronized long getId() {
		return (id);
	}

	public synchronized String getName() {
		return (name);
	}

	public int getMatchesPlayed() {
		return matchesPlayed;
	}

	public void setMatchesPlayed(int matchesPlayed) {
		this.matchesPlayed = matchesPlayed;
	}

	public long getPoints() {
		return points;
	}

	public void setPoints(long points) {
		this.points = points;
	}

	public int getCategory() {
		return category;
	}

	public void setCategory(int category) {
		if( category < 0 ) this.category = 0;
		else this.category = category;
	}

	public int getVictories() {
		return victories;
	}

	public void setVictories(int victories) {
		this.victories = victories;
	}

	public int getDefeats() {
		return defeats;
	}

	public void setDefeats(int defeats) {
		this.defeats = defeats;
	}

	public int getDraws() {
		return draws;
	}

	public void setDraws(int draws) {
		this.draws = draws;
	}

	public int getWinningStreak() {
		return winningStreak;
	}

	public void setWinningStreak(int winningStreak) {
		this.winningStreak = winningStreak;
	}

	public int getBestStreak() {
		return bestStreak;
	}

	public void setBestStreak(int bestStreak) {
		this.bestStreak = bestStreak;
	}

	public int getDecos() {
		return decos;
	}

	public void setDecos(int decos) {
		this.decos = decos;
	}

	public long getGoalsScored() {
		return goalsScored;
	}

	public void setGoalsScored(long goalsScored) {
		this.goalsScored = goalsScored;
	}

	public long getGoalsReceived() {
		return goalsReceived;
	}

	public void setGoalsReceived(long goalsReceived) {
		this.goalsReceived = goalsReceived;
	}

	public long getTimePlayed() {
		return timePlayed;
	}

	public void setTimePlayed(long timePlayed) {
		this.timePlayed = timePlayed;
	}

	public int getDivision() {
		return division;
	}

	public void setDivision(int division) {
		this.division = division;
	}

	public int getTeam1() {
		if( teams.size() > 0 ) return( teams.get(0).intValue() );
		else return 0xFFFF;
	}

	public int getTeam2() {
		if( teams.size() > 1 ) return( teams.get(1).intValue() );
		else return 0xFFFF;
	}

	public int getTeam3() {
		if( teams.size() > 2 ) return( teams.get(2).intValue() );
		else return 0xFFFF;
	}

	public int getTeam4() {
		if( teams.size() > 3 ) return( teams.get(3).intValue() );
		else return 0xFFFF;
	}

	public int getTeam5() {
		if( teams.size() > 4 ) return( teams.get(4).intValue() );
		else return 0xFFFF;
	}

	public void pushTeam( int team ) {
		if( teams.size() >= 5 )
			teams.remove(0);
		
		teams.push(new Integer( team ));
	}
	
	public long getLastLogin() {
		return lastLogin;
	}

	public void setLastLogin(long lastLogin) {
		this.lastLogin = lastLogin;
	}

	public long getPosition() {
		return position;
	}

	public void setPosition(long position) {
		this.position = position;
	}

	public int getOldCategory() {
		return oldCategory;
	}

	public void setOldCategory(int oldCategory) {
		if( oldCategory < 0 ) this.oldCategory = 0;
		else this.oldCategory = oldCategory;
	}

	public int getMatchPoints() {
		return matchPoints;
	}

	public void setMatchPoints(int matchPoints) {
		this.matchPoints = matchPoints;
	}

	public long getOldPoints() {
		return oldPoints;
	}

	public void setOldPoints(long oldPoints) {
		this.oldPoints = oldPoints;
	}
} public class PlayerList {
	Vector<PlayerInfo> players;

	public PlayerList() {
		players = new Vector<PlayerInfo>();
	}

	public void addPlayer(PlayerInfo pl) {
		players.add(pl);
	}

	public void removePlayer(long pid) {
		for (int i = 0; i < players.size(); i++) {
			PlayerInfo p = (PlayerInfo) players.get(i);
			if (p.getId() == pid) {
				players.remove(i);
				break;
			}
		}
	}

	public PlayerInfo getPlayer( int idx ) {
		return( players.get( idx ));
	}
	
	public PlayerInfo getPlayerInfo(long pid) {
		for (int i = 0; i < players.size(); i++) {
			PlayerInfo p = (PlayerInfo) players.get(i);
			if (p.getId() == pid) {
				return (p);
			}
		}
		return (null);
	}

	public PlayerInfo[] getPlayerArray() {
		PlayerInfo pl[] = new PlayerInfo[players.size()];
		for (int i = 0; i < players.size(); i++) {
			pl[i] = (PlayerInfo) players.get(i);
		}
		return (pl);
	}
	
	public Long[] getPidsInGroup( long gid ) {
		ArrayList<Long> al = new ArrayList<Long>();
		for (int i = 0; i < players.size(); i++) {
			PlayerInfo p = (PlayerInfo) players.get(i);
			if( p.getGid() == gid ) al.add( p.getId());
		}
		return( (Long[])al.toArray() );
	}
	
	public PlayerInfo getPlayerInfo(String name) {
		for (int i = 0; i < players.size(); i++) {
			PlayerInfo p = (PlayerInfo) players.get(i);
			if (p.getName().equals(name)) {
				return (p);
			}
		}
		return (null);
	}
	
	public int getNumPlayers() {
		return (players.size());
	}

} public class QUERIES {
	// public static final int MENU_Q6_SERVER_QUERY = 0x0000
	public static final int GAME_SENDMESSAGE = 0x00004402;
	public static final int GAME_IPINFO_SERVER_QUERY = 0x00004347;
	public static final int GAME_PLYGRP_SERVER_QUERY = 0x00004222;
	public static final int GAME_CRTROOM_CLIENT_QUERY = 0x00004310;
	public static final int GAME_GETPLYANDGROUP_CLIENT_QUERY = 0x00004102;
	public static final int GAME_GETROOMLST_CLIENT_QUERY = 0x00004300;
	public static final int GAME_ROOMINFO_SERVER_QUERY = 0x00004306;
	public static final int GAME_GETPLYLST_CLIENT_QUERY = 0x00004210;
	public static final int MENU_MYPLID_CLIENT_QUERY = 0x00003080;
	public static final int MENU_PLYGRP_SERVER_QUERY = 0x00004220;
	public static final int MENU_MYIP_CLIENT_QUERY = 0x00004202;
	public static final int MENU_MENUS_CLIENT_QUERY = 0x00004200;
	public static final int MENU_GROUP_CLIENT_QUERY = 0x00004100;
	public static final int MENU_LOGIN_CLIENT_QUERY = 0x00003003;
	public static final int LOGIN_Q14_2_SERVER_QUERY = 0x00003123;
	public static final int LOGIN_Q14_1_SERVER_QUERY = 0x00003101;
	public static final int LOGIN_Q14_CLIENT_QUERY = 0x00003120; // Envio el
																	// id del
																	// player
																	// elegido
	public static final int LOGIN_Q13_CLIENT_QUERY = 0x00003100; // Envio el
																	// id del
																	// player
																	// elegido
	public static final int LOGIN_Q12_CLIENT_QUERY = 0x00003090; // Envio el
																	// id del
																	// player
																	// elegido
	public static final int LOGIN_MESSAGES_SERVER_QUERY = 0x00003087;
	public static final int LOGIN_MESSAGES_CLIENT_QUERY = 0x0000308A;
	public static final int LOGIN_Q10_CLIENT_QUERY = 0x00003070;
	public static final int LOGIN_GRPNFO_CLIENT_QUERY = 0x00003050;
	public static final int LOGIN_PLYGRP_CLIENT_QUERY = 0x00003040;
	public static final int LOGIN_CRTPLY_CLIENT_QUERY = 0x00003020;
	public static final int LOGIN_Q4_CLIENT_QUERY = 0x00003060;
	public static final int LOGIN_Q4_SERVER_QUERY = 0x00003062;
	public static final int LOGIN_PLAYERS_CLIENT_QUERY = 0x00003010;
	public static final int LOGIN_INIT_CLIENT_QUERY = 0x00003001;
	public static final int MENU_INIT_CLIENT_QUERY = 0x00003001;
	public static final int LOGIN_LOGIN_CLIENT_QUERY = 0x00003003;
	public static final int INIT_CLIENT_QUERY = 0x00002008;
	public static final int INIT_SERVER_QUERY = 0x00002009;
	public static final int Q2_CLIENT_QUERY = 0x00002006;
	public static final int Q2_SERVER_QUERY = 0x00002007;
	public static final int SRVLST_SERVER_QUERY = 0x00002003;
	public static final int END_SRVLST_SERVER_QUERY = 0x00002004;
	public static final int INFO_QUERY = 0x0000200A;
	public static final int END_INFO_QUERY = 0x0000200B;
	public static final int CLOSE_CLIENT_QUERY = 0x00000003;
	public static final int SRV_CLIENT_QUERY = 0x00002005;
	public static final int VOID_SERVER_QUERY = 0x00002002;
	public static final int RNKURL_CLIENT_QUERY = 0x00002200;
	public static final int RNKURL_SERVER_QUERY = 0x00002201;
	public static final int RNKURLLST_SERVER_QUERY = 0x00002202;
	public static final int END_RNKURLLST_SERVER_QUERY = 0x00002203;
} public class RoomInfo {
	
	public static final long TIME_TO_SEND_PIN_CODES = 1 * 60 * 1000;
	
	long id;
	String name;
	int type;
	String password;
	boolean hasPassword;
	int gamePhase;
	int team1;
	int time;
	int team2;
	int half;
	int team1goals[];
	int team2goals[];
	int team1goalsBackup[];
	int team2goalsBackup[];
	long lastBytes = 0x00020100L;
	byte[] matchSettings;
	int peopleOnMatch;
	ChannelInfo channel;
	long timestampLagged = 0;
	ArrayList<RoomPlayerData> current;
	ArrayList<RoomPlayerData> playerData;
	ArrayList<RoomPlayerData> playerDataCache;
	
	private RoomPlayerData searchByPid( long pid ) {
		for( int i = 0; i < current.size(); i++ ) {
			RoomPlayerData rpd = current.get(i);
			if( rpd.getPlayer().getId() == pid ) return( rpd );
		}
		return( null );
	}
	
	public void backupGoals() {
		team1goalsBackup = team1goals.clone();
		team2goalsBackup = team2goals.clone();
	}
	
//	public boolean isAGroupMatch() {
//		long team0gid = 0;
//		long team1gid = 0;
//		if( gamePhase == 7 ) {
//			for( int i = 0; i < current.size(); i++ ) {
//				RoomPlayerData rpd = current.get(i);
//				if( rpd.getSelOrder() != 0x00FF && rpd.getPlayer().getGid() != 0 ) {
//					if( rpd.getTeam() == 0 ) {
//						
//					}
//				}
//			}
//		}
//	}
	
	public void restoreGoals() {
		team1goals = team1goalsBackup.clone();
		team2goals = team2goalsBackup.clone();
	}
	
	public long getTimestampLagged() {
		return( timestampLagged );
	}
	
	public void setTimestamplagged( long ts ) {
		timestampLagged = ts;
	}
	
	public void restorePlayerSelsFromCache( ) {
		if( playerDataCache == null ) return;
		for( int i = 0; i < getNumPlayers(); i++ ) {
			setUseCacheData();
			RoomPlayerData rpd = searchByPid(playerData.get(i).getPlayer().getId());
			if( rpd != null ) {
				int sel = rpd.getSelOrder();
				playerData.get(i).setSelOrder(sel);
			}
			setUseRealData();
			
		}
		//setUseCacheData();
		//System.out.println( "EN CACHE TENIA " + getNumPlayersOnSel() + " PLAYERS ON SEL");
		//setUseRealData();
		//System.out.println( "EN REAL AHORA TENGO " + getNumPlayersOnSel() + " PLAYERS ON SEL");
	}
	
	public void saveCache() {
		playerDataCache = new ArrayList<RoomPlayerData>();
		for( int i = 0; i < playerData.size(); i++ ) {
			playerDataCache.add( (RoomPlayerData)playerData.get(i).clone() );
		}
	}
	
	public boolean cacheClean() {
		return( playerDataCache == null );
	}
	
	public void resetCache() {
		playerDataCache = null;
	}
	
	public void setUseCacheData() {
		current = playerDataCache;
	}
	
	public void setUseRealData() {
		current = playerData;
	}
	
	public PlayerInfo getPlayerFromPid( long pid ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getPlayer() );
		return( null );
	}
	
	public PlayerInfo getPlayerFromIdx( int idx ) {
		if(idx < current.size()) return( current.get(idx).getPlayer());
		return( null );
	}
	
	public void resetTeams() {
		this.team1 = 0x0000FFFF;
		this.team2 = 0x0000FFFF;
//		for( int i = 0; i < current.size(); i++ ) {
//			RoomPlayerData rpd = current.get(i);
//			rpd.setTeam(0x000000FF);
//		}
	}
	
	public RoomInfo(long id, ChannelInfo channel, int type, boolean hasPassword, String password,
			String name, PlayerInfo p ) {
		this.id = id;
		this.channel = channel;
		this.type = type;
		this.name = name;
		this.password = password;
		this.hasPassword = hasPassword;
		
		playerData = new ArrayList<RoomPlayerData>();
		playerData.add( new RoomPlayerData(p));
		
		this.matchSettings = null;
		this.gamePhase = 0;
		this.team1 = 0x0000FFFF;
		this.lastBytes = 0L;
		this.lastBytes = 0x0000000000020100L;
		this.time = 0;
		this.team2 = 0x0000FFFF;
		this.half = 0;
		this.team1goals = new int[5];
		Arrays.fill(team1goals, 0);
		this.team2goals = new int[5];
		this.peopleOnMatch = 0;
		this.playerDataCache = null;
		Arrays.fill(team2goals, 0);
		timestampLagged = 0;
		current = playerData;
	}
	
	public String generatePinCode( long pid ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.generatePinCode());
		return( null );
	}
	
	public void clearPinCodes( ) {
		for( int i = 0; i < getNumPlayers(); i++ ) {
			RoomPlayerData rpd = current.get(i);
			if( rpd.getSelOrder() != 0x00FF )
				rpd.setPinCode(null);
		}
	}
	
	public String getPinCode( long pid ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getPinCode());
		return( null );
	}
	
	public boolean teamSentMessage( int team ) {
		if( current == null ) return false;
		for( int i = 0; i < current.size(); i++ ) {
			if( current.get(i).getTeam() == team && !current.get(i).getCodeSent() ) return false;
		}
		return( true );
	}
	
	public boolean getCodeSent( long pid ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getCodeSent());
		return( false );
	}
	
	public void setCodeSent( long pid, boolean sent ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) rpd.setCodeSent(sent);
	}
	
	public void setTeamCodeSent( int team, boolean sent ) {
		if( current == null ) return;
		for( int i = 0; i < current.size(); i++ ) {
			if( current.get(i).getTeam() == team ) current.get(i).setCodeSent(sent);
		}
	}
	
	public ChannelInfo getChannel() {
		return( this.channel );
	}
	
	public void setPlayerTeam(long pid, int team) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) rpd.setTeam(team);
	}

	public void setSpect(long pid, int spect) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) rpd.setSpect(spect);
	}

	public int getSpect(long pid) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getSpect());
		return (0);
	}

	public  int getSpectIdx(int idx) {
		if( current == null ) return 0;
		if( idx >= current.size() ) return( 0 );
		RoomPlayerData rpd = current.get(idx);
		return (rpd.getSpect());
	}

	public void setMatchSettings(byte[] data) {
		this.matchSettings = data;
	}

	public byte[] getMatchSettings() {
		return (this.matchSettings);
	}

	public boolean allTeamsReady() {
		if (this.team1 == 0x0000FFFF || this.team2 == 0x0000FFFF)
			return false;
		return (true);
	}

	 int getGoalIdxForHalf(int half) {
		switch (half) {
		case 1:
			return (0);
		case 3:
			return (1);
		case 5:
			return (2);
		case 7:
			return (3);
		case 9:
			return (4);
		default:
			return (0);
		}
	}
	
	public int getTeam1Goals( ) {
		int ret = 0;
		/*
		 * We don't care about goals scored on penalties
		 */
		for( int i = 0; i < 4; i++ ) {
			ret += team1goals[i];
		}
		
		return( ret );
	}
	
	/*
	 * ESTA ALREVES OJO SI GANA EL EQUIPO 0 esto devuelve un 1
	 */
	public int getWinner( ) {
		int team1g = getTeam1Goals();
		int team2g = getTeam2Goals();

		if( team1goals[4] > team2goals[4] ) team1g++;
		else if( team2goals[4] > team1goals[4] ) team2g++;
		
		if( team1g > team2g ) return 1;
		else if( team2g > team1g ) return 0;
		else return( 2 );
	}
	
	public int getTeam1TotalGoals( ) {
		int ret = 0;
		/*
		 * We don't care about goals scored on penalties
		 */
		for( int i = 0; i < 5; i++ ) {
			ret += team1goals[i];
		}
		return( ret );
	}
	
	public int getTeam2Goals( ) {
		int ret = 0;
		/*
		 * We don't care about goals scored on penalties
		 */
		for( int i = 0; i < 4; i++ ) {
			ret += team2goals[i];
		}
		
		return( ret );
	}
	
	public void setTeam2Goals( int goals ) {
		/*
		 * We set the goals as if they were scored on the first half
		 * reset the rest of goals to 0
		 */
		team2goals[0] = goals;
		for( int i = 1; i < 5; i++ ) {
			team2goals[i] = 0;
		}
	}
	
	public void setTeam1Goals( int goals ) {
		/*
		 * We set the goals as if they were scored on the first half
		 * reset the rest of goals to 0
		 */
		team1goals[0] = goals;
		for( int i = 1; i < 5; i++ ) {
			team1goals[i] = 0;
		}
	}
	
	public int getTeam2TotalGoals( ) {
		int ret = 0;
		/*
		 * We don't care about goals scored on penalties
		 */
		for( int i = 0; i < 4; i++ ) {
			ret += team2goals[i];
		}
		return( ret );
	}
	
	public void addTeam1goal(int half) {
		this.team1goals[getGoalIdxForHalf(half)]++;
	}

	public void addTeam2goal(int half) {
		this.team2goals[getGoalIdxForHalf(half)]++;
	}

	public int getTeam1Goals(int idx) {
		return (this.team1goals[idx]);
	}

	public int getTeam2Goals(int idx) {
		return (this.team2goals[idx]);
	}

	public void resetGoals() {
		Arrays.fill(team2goals, 0);
		Arrays.fill(team1goals, 0);
	}

	public void setPlayerTeamPos(long pid, int teampos) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) rpd.setTeamPos(teampos);
	}

	public int getPlayerTeam(long pid) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getTeam() );
		return( 0x000000FF );
	}
	
	public long[] getPlayersFromTeam( int team ) {
		if( current == null ) return( new long[0] );
		int count = 0;
		for( int i = 0; i < current.size(); i++ ) {
			if( current.get(i).getTeam() == team ) count++;
		}
		long ret[] = new long[count];
		count = 0;
		for( int i = 0; i < current.size(); i++ ) {
			if( current.get(i).getTeam() == team ) ret[count++] = current.get(i).getPlayer().getId();
		}
		return( ret );
	}
	
	public boolean allPlayersOnSelOnline( ) {
		long pids[] = getPlayerIdsOnSel();
		for( int i = 0; i < pids.length; i++ ) {
			if( channel.getPlayerList().getPlayerInfo(pids[i]) == null ) return( false );
		}
		return( true );
	}

	public int getPlayerTeamPos(long pid) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getTeamPos() );
		return(0);
	}

	public void setTeam2(int bytes) {
		this.team2 = bytes;
	}

	public void setHalf(int half) {
		this.half = half;
	}

	public int getHalf() {
		return (this.half);
	}

	public int getTeam2() {
		return (this.team2);
	}

	public void setTime(int time) {
		this.time = time;
	}

	public int getTime() {
		return (this.time);
	}

	public void setTeam1(int time) {
		this.team1 = time;
	}

	public void setLastBytes(long value) {
		this.lastBytes = value;
	}

	public long getLastBytes() {
		return (this.lastBytes);
	}

	public int getTeam1() {
		return (this.team1);
	}

	public void setType(int type) {
		this.type = type;
	}

	public void setGamePhase(int phase) {
		this.gamePhase = phase;
	}
	
	public void setPlayersPhase( int phase ) {
		for( int i = 0; i < current.size(); i++ ) {
			current.get(i).setPlayerPhase( phase );
		}
	}
	

	
	public void setPlayerPhase( long pid, int phase ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) rpd.setPlayerPhase(phase);
	}

	public int getPlayerPhase( long pid ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return(rpd.getPlayerPhase());
		return( 0 );
	}
	
	public int getGamePhase() {
		return (this.gamePhase);
	}

	public void setPassworded(boolean pwd) {
		this.hasPassword = pwd;
	}

	public String getPassword() {
		return (password);
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public void setName(String name) {
		this.name = name;
	}

	public long getId() {
		return (id);
	}

	public int getType() {
		return (type);
	}

	public String getName() {
		return (name);
	}

	public int getPlayerIdx(long pid) {
		for (int j = 0; j < getNumPlayers(); j++) {
			if (getPlayerId(j) == pid)
				return (j);
		}
		return (-1);
	}

	public  long getPlayerIdOnSel(int sel) {
		for (int i = 0; i < getNumPlayers(); i++) {
			if (current.get(i).getSelOrder() == sel)
				return (current.get(i).getPlayer().getId());
		}
		return (0x00000000);
	}

	public  int getNumPlayersOnSel() {
		int count = 0;
		for (int i = 0; i < getNumPlayers(); i++) {
			if (current.get(i).getSelOrder() != 0x00FF)
				count++;
		}
		return (count);
	}

	/*
	 * OJO, NO SOLO SWAPEAR LOS IDS, SINO EL RESTO DE LA INFORMACION AS WELL!!!
	 */
	public  void swapPositions(long pid1, long pid2) {
		if( current == null ) return;
		int idx1 = getPlayerIdx(pid1);
		int idx2 = getPlayerIdx(pid2);
		
		/*
		 * If the we haven't that player in the room
		 * we wont swap anything.
		 */
		if( idx2 == -1 || idx1 == -1 ) return;
		
		/*
		 * Ojo con esto! no se si estoy sobreescribiendo!
		 */
		RoomPlayerData rpd1 = current.get(idx1);
		RoomPlayerData rpd2 = current.get(idx2);
		current.set( idx1, rpd2 );
		current.set( idx2, rpd1 );
	}

	public  int getNumPlayers() {
		if( current == null ) return 0;
		return (current.size() );
	}

	public long getPlayerId(int idx) {
		if( current == null ) return 0;
		if( idx >= current.size() ) return( 0 );
		RoomPlayerData rpd = current.get(idx);
		return( rpd.getPlayer().getId() );
	}

	public  int getPlayerSel(int idx) {
		if( current == null ) return 0x00FF;
		if( idx >= current.size() ) return( 0x00FF );
		return (current.get(idx).getSelOrder());
	}
	
	public int getPlayerSelFromPid( long pid ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getSelOrder());
		return(0x00FF);
	}

	public boolean getWatchedScoresFromPid( long pid ) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getWatchedScores());
		return(false);
	}
	
	public void resetWatchedScores() {
		for( int i = 0; i < getNumPlayers(); i++ ) {
			current.get(i).setWatchedScores(false);
		}
	}
	
	public  void setPlayerSel(long pid, int selOrder) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) rpd.setSelOrder(selOrder);
	}

	public  void setReadyStatus(long pid, int status) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) rpd.setReadyStatusPhase(status);
	}
	
	public void setReadyStatusOfTeam( int team, int status ) {
		for( int i = 0; i < getNumPlayers(); i++ ) {
			RoomPlayerData rpd = current.get(i);
			if( rpd != null && rpd.getSelOrder() != 0x00FF && rpd.getTeam() == team ) 
				rpd.setReadyStatusPhase( status );
		}
	}
	
	public void setPlayerPhaseOfTeam( int team, int phase ) {
		for( int i = 0; i < getNumPlayers(); i++ ) {
			RoomPlayerData rpd = current.get(i);
			if( rpd != null && rpd.getSelOrder() != 0x00FF && rpd.getTeam() == team ) 
				rpd.setPlayerPhase(phase);
		}
	}

	public  int getReadyStatus(long pid, int phase) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) return( rpd.getReadyStatusPhase( phase ));
		return(-1);
	}

	public boolean allReadyOnCacheOfSels( int phase, int sel ) {
		if( cacheClean() ) return( false );
		boolean atLeastOne = false;
		//System.out.println( "*** Comprobando si están todos listos");
		for (int i = 0; i < playerDataCache.size(); i++) {
			if (playerDataCache.get(i).getSelOrder() != 0x00FF) {
				//System.out.println( "Paso y player sel vale: " + getPlayerSel(i));
				RoomPlayerData rpd = searchByPid(playerDataCache.get(i).getPlayer().getId());
				if( rpd == null ) continue;
				if (rpd.getReadyStatusPhase(phase) != sel) {
					//System.out.println( "alguno no está listo, devuelvo false");
					return (false);
				} else
					atLeastOne = true;
			}
		}
		if( atLeastOne ) return(true);
		return (false);
	}
	
	public  boolean allReadyOn( int phase, int sel ) {
		boolean atLeastOne = false;
		//System.out.println( "*** Comprobando si están todos listos");
		for (int i = 0; i < getNumPlayers(); i++) {
			if (getPlayerSel(i) != 0x00FF) {
				//System.out.println( "Paso y player sel vale: " + getPlayerSel(i));
				long pid = getPlayerId(i);
				if (getReadyStatus(pid, phase) != sel) {
					//System.out.println( "alguno no está listo, devuelvo false");
					return (false);
				} else
					atLeastOne = true;
			}
		}
		//System.out.println( "todos listos, adelante!");
		if( atLeastOne ) return(true);
		return (false);
	}
	
	public  boolean allTeamLeadersReady( int phase, int sel ) {
		boolean atLeastOne = false;
		//System.out.println( "*** Comprobando si están todos listos");
		for (int i = 0; i < getNumPlayers(); i++) {
			if (getPlayerSel(i) != 0x00FF && current.get(i).getTeamPos() == 1) {
				//System.out.println( "Paso y player sel vale: " + getPlayerSel(i));
				long pid = getPlayerId(i);
				if (getReadyStatus(pid, phase) != sel) {
					//System.out.println( "alguno no está listo, devuelvo false");
					return (false);
				} else
					atLeastOne = true;
			}
		}
		//System.out.println( "todos listos, adelante!");
		if( atLeastOne ) return(true);
		return (false);
	}
	
	public boolean allPlayersFell() {
		for (int i = 0; i < getNumPlayers(); i++) {
			if (getPlayerSel(i) != 0x00FF) {
				if( !current.get(i).getFell() ) return( false );
			}
		}
		return( true );
	}
	
	public void setPlayerFell(long pid, boolean value) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) {
			rpd.setFell(value);
		}
	}
	
	public boolean getPlayerFell(long pid) {
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd != null ) {
			return( rpd.getFell() );
		}
		return( false );
	}

	public  void setPlayerSel(long pid) {
		if( current == null ) return;
		int ultimo = -1;
		for (int i = 0; i < current.size(); i++) {
			if (current.get(i).getSelOrder() != 0x00FF) {
				if (current.get(i).getSelOrder() > ultimo)
					ultimo = current.get(i).getSelOrder();
			}
		}
		ultimo++;
		setPlayerSel(pid, ultimo);
	}
	
	public  boolean hasPassword() {
		return (hasPassword);
	}

	public  void removePlayer(long pid) {
		if( current == null ) return;
		RoomPlayerData rpd = searchByPid(pid);
		if( rpd == null ) return;
		int selOrderGone = rpd.getSelOrder();
		int idx = getPlayerIdx(pid);
		current.remove(idx);
		/*
		 * Arreglamos el orden se nos ha ido uno que participaba
		 */
		if (selOrderGone != 0x00FF) {
			for (int i = 0; i < current.size(); i++) {
				if (current.get(i).getSelOrder() != 0x00FF && current.get(i).getSelOrder() > selOrderGone)
					current.get(i).setSelOrder(current.get(i).getSelOrder() - 1);
			}
		}
	}

	public  void removePlayerSel(long pid) {
		int sel = getPlayerSel(getPlayerIdx(pid));
		setPlayerSel(pid, 0x00FF);
		for (int i = 0; i < current.size(); i++) {
			if (current.get(i).getSelOrder() != 0x00FF) {
				if (current.get(i).getSelOrder() > sel)
					current.get(i).setSelOrder(current.get(i).getSelOrder() - 1);
			}
		}
	}

	public  void resetAll() {
		this.team1 = 0x0000FFFF;
		//this.lastBytes = 0x00000000;
		this.lastBytes = 0x0000000000020100L;
		this.time = 0;
		this.team2 = 0x0000FFFF;
		this.half = 0;
		// this.gamePhase = 0x0100;
		if( current != null )
			for (int i = 0; i < getNumPlayers(); i++) {
				current.get(i).reset();
			}
		Arrays.fill(team1goals, 0);
		Arrays.fill(team2goals, 0);
		this.matchSettings = null;
		this.playerDataCache = null;
		timestampLagged = 0;
		current = this.playerData;
	}
	
	public void resetAllButCache() {
		//this.team1 = 0x0000FFFF;
		//this.lastBytes = 0x00000000;
		this.lastBytes = 0x0000000000020100L;
		this.time = 0;
		//this.team2 = 0x0000FFFF;
		this.half = 0;
		// this.gamePhase = 0x0100;
		if( current != null )
			for (int i = 0; i < getNumPlayers(); i++) {
				current.get(i).reset();
			}
		//Arrays.fill(team1goals, 0);
		//Arrays.fill(team2goals, 0);
		this.matchSettings = null;
	}

	public  void resetAllReady( int phase ) {
		for (int i = 0; i < getNumPlayers(); i++) {
			//for( int j = phase; j >= 0; j-- )
			current.get(i).setReadyStatusPhase(phase, -1);
		}
	}
	
	public  void resetAllReady( ) {
		for (int i = 0; i < getNumPlayers(); i++) {
			current.get(i).resetAllReadyStatus();
		}
	}
	
	public void resetAllSels() {
		for (int i = 0; i < getNumPlayers(); i++) {
			current.get(i).setSelOrder(0x00FF);
		}
	}

	public  long[] getPlayerIds() {
		if( current == null ) return( new long[0]);
		long[] pids = new long[current.size()];
		for( int i = 0; i < current.size(); i++ ) {
			pids[i] = current.get(i).getPlayer().getId();
		}
		return (pids);
	}
	
	public long[] getPlayerIdsNotInMatch() {
		/*
		 * ESTO PUEDE ESTAR CAUSANDO LAS PANTALLAS NEGRAS QUE QUEDAN!!!!! PERO AL MISMO TIEMPO
		 * HAY MENSAJES QUE DEBEN RECEBIR ESTANDO EN ESTE ESTADO!.
		 */
		//if( getType() == 1 ) return( getPlayerIds() );
		if( current == null ) return( new long[0]);
		if( getGamePhase() < 2 ) return( getPlayerIds() );
		int count = 0;
		for( int i = 0; i < current.size(); i++ ) {
			if( current.get(i).getSelOrder() == (0x00FF)) count++;
		}
		long[] ret = new long[count];
		count = 0;
		for( int i = 0; i < current.size(); i++ ) {
			if( current.get(i).getSelOrder() == (0x00FF)) ret[count++] = current.get(i).getPlayer().getId();
		}
		return( ret );
	}

	public  long[] getPlayerIdsOnSel() {
		long ret[] = new long[getNumPlayersOnSel()];
		for (int i = 0; i < getNumPlayersOnSel(); i++) {
			ret[i] = getPlayerIdOnSel(i);
		}
		return (ret);
	}
	
	public  long[] getPlayerIdsOnSelExcept( long pid ) {
		long ret[] = new long[getNumPlayersOnSel()-1];
		int j = 0;
		for (int i = 0; i < getNumPlayersOnSel(); i++) {
			long id = getPlayerIdOnSel(i);
			if( pid != id && j < ret.length )
				ret[j++] = getPlayerIdOnSel(i);
		}
		return (ret);
	}

	public  long[] getPlayerIdsWatching() {

		int count = 0;
		for (int i = 0; i < getNumPlayers(); i++) {
			if (current.get(i).getSpect() == 1)
				count++;
		}

		long[] ret = new long[count];
		count = 0;
		for (int i = 0; i < getNumPlayers(); i++) {
			if (current.get(i).getSpect() == 1)
				ret[count++] = getPlayerId(i);
		}
		return (ret);
	}

	public  long[] getPlayerIdsExcept(long pid) {
		long ret[] = new long[getNumPlayers() - 1];
		int idx = 0;
		for (int i = 0; i < getNumPlayers(); i++) {
			if (getPlayerId(i) != pid)
				ret[idx++] = getPlayerId(i);
		}
		return (ret);
	}

	public  void addPlayer(PlayerInfo p) {
		if( current == null ) return;
		RoomPlayerData rpd = new RoomPlayerData(p);
		current.add( rpd );
	}

	public int getPeopleOnMatch() {
		return peopleOnMatch;
	}

	public void setPeopleOnMatch(int peopleOnMatch) {
		this.peopleOnMatch = peopleOnMatch;
	}
} public class RoomList {
	Vector<RoomInfo> v;

	public RoomList() {
		v = new Vector<RoomInfo>();
	}

	public  void addRoom(RoomInfo room) {
		v.add(room);
	}

	public  void removeRoom(long rid) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			if (p.getId() == rid) {
				v.remove(i);
				break;
			}
		}
	}

	public  void removePlayerFromRoom(long rid, long pid) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			if (p.getId() == rid) {
				p.removePlayer(pid);
				//v.set(i, p);
			}
		}
	}

//	public  void addPlayerToRoom(long rid, long pid) {
//		for (int i = 0; i < v.size(); i++) {
//			RoomInfo p = (RoomInfo) v.get(i);
//			if (p.getId() == rid) {
//				p.addPlayer(pid);
//				//v.set(i, p);
//			}
//		}
//	}

	public  void setPlayerSelInRoom(long pid, int sel) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			if (p.getPlayerIdx(pid) != -1) {
				p.setPlayerSel(pid, sel);
				//v.set(i, p);
			}
		}
	}

	public  void setPlayerSelInRoom(long pid) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			if (p.getPlayerIdx(pid) != -1) {
				p.setPlayerSel(pid);
				//v.set(i, p);
			}
		}
	}

	public  void removePlayerSelInRoom(long pid) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			if (p.getPlayerIdx(pid) != -1) {
				p.removePlayerSel(pid);
				//v.set(i, p);
			}
		}
	}

	public  RoomInfo getRoom(long rid) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			if (p.getId() == rid) {
				return (p);
			}
		}
		return (null);
	}

	public  void setRoom(long rid, RoomInfo r) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			if (p.getId() == rid) {
				v.set(i, r);
			}
		}
	}

	public  RoomInfo[] getRoomInfoArray() {
		RoomInfo pl[] = new RoomInfo[v.size()];
		for (int i = 0; i < v.size(); i++) {
			pl[i] = (RoomInfo) v.get(i);
		}
		return (pl);
	}
	
	public  RoomInfo[] getRoomsInChannelArray( ChannelInfo channel ) {
		Vector<RoomInfo> ret = new Vector<RoomInfo>();
		
		for (int i = 0; i < v.size(); i++) {
			if( v.get(i).getChannel() == channel )
				ret.add( v.get(i));
		}
		return ((RoomInfo[])ret.toArray(new RoomInfo[0]));
	}

	public  RoomInfo searchPlayer(long pid) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			for (int j = 0; j < p.getNumPlayers(); j++) {
				if (p.getPlayerId(j) == pid)
					return (p);
			}
		}
		return (null);
	}
	
	public RoomInfo searchCachedPlayer( long pid ) {
		for (int i = 0; i < v.size(); i++) {
			RoomInfo p = (RoomInfo) v.get(i);
			p.setUseCacheData();
			for (int j = 0; j < p.getNumPlayers(); j++) {
				if (p.getPlayerId(j) == pid) {
					p.setUseRealData();
					return (p);
				}
			}
			p.setUseRealData();
		}
		return (null);
	}
	
//	public RoomInfo searchRoom( int numPlayers, boolean passworded, ) {
//		
//	}
} public class ServerInfo {
	int type;
	int id;
	String name;
	String ip;
	int port;
	int wport;
	int numPlayers;

	public ServerInfo(int id, int type, String name, String ip, int port ) {
		this.type = type;
		this.name = name;
		this.ip = ip;
		this.port = port;
		this.id = id;
		this.numPlayers = 0;
		this.wport = 0;
	}

	public int getWPort() {
		return( this.wport );
	}
	
	public void setWPort( int wport ) {
		this.wport = wport;
	}
	
	public int getId() {
		return( id );
	}
	
	public int getType() {
		return (type);
	}

	public String getName() {
		return (name);
	}

	public String getIp() {
		return (ip);
	}

	public int getPort() {
		return (port);
	}
	
	public int getNumPlayers() {
		return (numPlayers);
	}
	
	public void setNumPlayers(int numPlayers) {
		this.numPlayers = numPlayers;
	}
} public class UserInfo {
	String username;
	int access;
	
	public UserInfo( String username, int access ) {
		this.username = username;
		this.access = access;
	}
	
	public String getUsername() {
		return( username );
	}
	
	public int getAccess() {
		return( access );
	}
	
	public void setUsername( String username ) {
		this.username = username;
	}
	
	public void setAccess( int access ) {
		this.access = access;
	}
} public class Util {
	
	static final byte[] xorMatrix = { (byte) 0xA6, (byte) 0x77, (byte) 0x95, (byte) 0x7c };

	public static int strlen(byte[] data, int offset) {
		int len = 0;
		for (int i = offset; i < data.length; i++) {
			if (data[i] == 0x00)
				break;
			len++;
		}
		return (len);
	}

	public static long getTimeOfToday( ) {
		Calendar cal = Calendar.getInstance();
		cal.setTime( new Date() );
		cal.set( Calendar.HOUR_OF_DAY, 0 );
		cal.set( Calendar.MINUTE, 0 );
		cal.set( Calendar.SECOND, 0 );
		return( cal.getTimeInMillis() );
	}
	
	public static int word2Int(byte h, byte l) {
		int firstByte = (0x000000FF & (h));
		int secondByte = (0x000000FF & (l));
		return ((firstByte << 8 | secondByte));
	}

	public static byte[] int2Word(int val) {
		byte[] ret = new byte[2];

		int firstByte = (0x0000FF00 & val) >> 8;
		int secondByte = (0x000000FF & val);
		ret[0] = (byte) firstByte;
		ret[1] = (byte) secondByte;
		return (ret);
	}

	public static byte[] long2Word(long val) {
		byte[] ret = new byte[4];
		int firstByte = (int) (val & 0xFF000000L) >> 24;
		int secondByte = (int) (val & 0x00FF0000L) >> 16;
		int thirdByte = (int) (val & 0x0000FF00L) >> 8;
		int fourthByte = (int) (val & 0x000000FFL);

		ret[0] = (byte) firstByte;
		ret[1] = (byte) secondByte;
		ret[2] = (byte) thirdByte;
		ret[3] = (byte) fourthByte;

		return (ret);
	}

	public static long word2Long(byte h1, byte l1, byte h2, byte l2) {
		int firstByte = (0x000000FF & (h1));
		int secondByte = (0x000000FF & (l1));
		int thirdByte = (0x000000FF & (h2));
		int fourthByte = (0x000000FF & (l2));

		return (((firstByte << 24 | secondByte << 16 | thirdByte << 8 | fourthByte)) & 0xFFFFFFFFL);

	}

	public static String toJavaHex(byte in[], int len) {

		byte ch = 0x00;
		int i = 0;

		if (in == null || in.length <= 0)
			return null;

		String pseudo[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
				"A", "B", "C", "D", "E", "F" };

		StringBuffer out = new StringBuffer(len * 12);

		while (i < len) {
			ch = (byte) (in[i] & 0xF0); // Strip off high nibble
			ch = (byte) (ch >>> 4);
			// shift the bits down
			ch = (byte) (ch & 0x0F);
			// must do this is high order bit is on!
			out.append("(byte)0x");
			out.append(pseudo[ch]); // convert the nibble to a String
			// Character

			ch = (byte) (in[i] & 0x0F); // Strip off low nibble
			out.append(pseudo[ch]); // convert the nibble to a String
			// Character
			out.append(", ");
			i++;
		}

		String rslt = new String(out);

		return rslt;

	}

	public static String toHex(byte in[]) {

		byte ch = 0x00;
		int i = 0;

		if (in == null || in.length <= 0)
			return null;

		String pseudo[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
				"A", "B", "C", "D", "E", "F" };

		StringBuffer out = new StringBuffer(in.length * 3);

		while (i < in.length) {
			ch = (byte) (in[i] & 0xF0); // Strip off high nibble
			ch = (byte) (ch >>> 4);
			// shift the bits down
			ch = (byte) (ch & 0x0F);
			// must do this is high order bit is on!
			out.append(pseudo[ch]); // convert the nibble to a String
			// Character

			ch = (byte) (in[i] & 0x0F); // Strip off low nibble
			out.append(pseudo[ch]); // convert the nibble to a String
			// Character
			out.append(" ");
			i++;
		}

		String rslt = new String(out);

		return rslt;
	}

	public static byte[] konamiEncode(byte asc[]) {
		return (konamiEncode(asc, 0));
	}

	public static byte[] konamiMaskIt(byte asc[], int maskIdx) {
		byte[] ret = new byte[asc.length];
		byte[] xorMatrix = { (byte) 0xA6, (byte) 0x77, (byte) 0x95, (byte) 0x7c };

		int xorIdx = maskIdx;

		for (int i = 0; i < asc.length; i++) {
			ret[i] = (byte) (asc[i] ^ xorMatrix[xorIdx++]);
			if (xorIdx > 3)
				xorIdx = 0;
		}

		return (ret);
	}

	public static byte[] konamiEncode(byte asc[], int maskIdx) {
		byte[] ret = new byte[asc.length];
		byte[] header = new byte[24];

		/*
		 * Hay una cabecera de 24 bytes iniciales, aparentemente los bytes en
		 * posiciones 2y3 forman un short que dice cuanto va a medir la cadena
		 * siguiente En la cadena siguiente los 6 bytes iniciales parecen datos
		 * de algún tipo antes del texto
		 */

		/*
		 * Leemos la cabecera primero
		 */
		int xorIdx;
		int i = 0;
		while (i < asc.length) {
			xorIdx = maskIdx;
			int pos = i;
			int j = 0;
			for (; i < pos + 24; i++) {
				ret[i] = (byte) (asc[i] ^ xorMatrix[xorIdx++]);
				header[j++] = asc[i];
				if (xorIdx > 3)
					xorIdx = 0;
			}


			int firstByte = (0x000000FF & (header[2]));
			int secondByte = (0x000000FF & (header[3]));

			long tamano = (firstByte << 8 | secondByte);

			pos = i;

			for (; i < (pos + tamano); i++) {
				if (i == asc.length) {
//					System.out.println("pues la longitud real era: "
//							+ asc.length + " y yo me quiero ir a: "
//							+ (pos + tamano));
					break;
				}
				ret[i] = (byte) (asc[i] ^ xorMatrix[xorIdx++]);
				if (xorIdx > 3)
					xorIdx = 0;
			}
		}

		return (ret);
	}

//	public static byte[] konamiDecode(byte kon[], int len) {
//		byte[] ret = new byte[len];
//		int xorIdx = 0;
//		
//		for (int i = 0; i < len; i++) {
//			ret[i] = (byte) (kon[i] ^ xorMatrix[xorIdx++]);
//			if (xorIdx > 3)
//				xorIdx = 0;
//		}
//		return( ret );
//	}
	
	public static byte[] konamiDecode(byte kon[], int len) {
		byte[] ret = new byte[len];
		byte[] header = new byte[24];

		/*
		 * Hay una cabecera de 24 bytes iniciales, aparentemente los bytes en
		 * posiciones 2y3 forman un short que dice cuanto va a medir la cadena
		 * siguiente En la cadena siguiente los 6 bytes iniciales parecen datos
		 * de alg�n tipo antes del texto
		 */

		/*
		 * Leemos la cabecera primero
		 */
		int xorIdx;
		int i = 0;
		while (i < len) {
			xorIdx = 0;
			int pos = i;
			int j = 0;
			
			/*
			 * Broken message, we can't fully decode it
			 */
			if( len < pos + 24 ) return( null );
			
			for (; i < pos + 24; i++) {
				ret[i] = (byte) (kon[i] ^ xorMatrix[xorIdx++]);
				header[j++] = ret[i];
				if (xorIdx > 3)
					xorIdx = 0;
			}

			/*
			 * Broken message, we can't fully decode it
			 */
			if( j < 24 ) return( null );
			
			int tamano = word2Int(header[2], header[3]);

			pos = i;

			for (; i < (pos + tamano); i++) {
				/*
				 * The message is incomplete, we can't fully decode it
				 */
				if (i == len) {
					return( null );
				}
				ret[i] = (byte) (kon[i] ^ xorMatrix[xorIdx++]);
				if (xorIdx > 3)
					xorIdx = 0;
			}
		}

		return (ret);
	}

	public static byte[] konamiDecode(byte kon[]) {
		return (konamiDecode(kon, kon.length));
	}

	public static boolean byteArrayCompare(byte a1[], byte a2[], int len) {
		for (int i = 0; i < len; i++) {
			if (a1[i] != a2[i])
				return false;
		}
		return true;
	}

	public static byte[] streamRead(InputStream in) throws IOException {
		byte buf[] = new byte[4096];
		int len = in.read(buf);

		if (len == -1)
			return null;

		byte ret[] = new byte[len];
		System.arraycopy(buf, 0, ret, 0, len);
		return (ret);
	}
	
	public static byte[] streamRead(SocketChannel in) throws IOException {
		ByteBuffer buf = ByteBuffer.allocate(4096);
		//byte buf[] = new byte[4096];
		int len = in.read(buf);

		if (len == -1)
			return null;

		byte ret[] = new byte[len];
		buf.get(ret);
		//System.arraycopy(buf, 0, ret, 0, len);
		return (ret);
	}

	public static byte[] streamReadDecoded(InputStream in) throws IOException {
		byte[] enc = streamRead(in);
		if (enc == null)
			return null;
		byte[] dec = konamiDecode(enc);
		return (dec);
	}
	
	public static byte[] streamReadDecoded(SocketChannel in) throws IOException {
		byte[] enc = streamRead(in);
		if (enc == null)
			return null;
		byte[] dec = konamiDecode(enc);
		return (dec);
	}

	public static void streamWriteEncoded(OutputStream out, byte[] data)
			throws IOException {
		out.write(konamiEncode(data));
		out.flush();
	}
	
	public static int streamWriteEncoded(SocketChannel out, byte[] data)
			throws IOException {
		//ByteBuffer buf = ByteBuffer.wrap( data );
		//int max_packet_len = 1024;
		
		return( out.write(ByteBuffer.wrap(konamiEncode(data))) );
	}
	
	public static String fetchURLContents( String url ) {
		URL u;
		try {
			u = new URL(url);
			HttpURLConnection uc = (HttpURLConnection)u.openConnection();
	        BufferedReader in = new BufferedReader(new InputStreamReader(uc
					.getInputStream()));
			String s;
			uc.setConnectTimeout(1000);
			uc.setReadTimeout(1000);
	        in = new BufferedReader(new InputStreamReader(uc
					.getInputStream()));
	        String ret = new String();
			while( (s = in.readLine()) != null ) {
				if( ret.length() > 0 ) ret += "\n";
				ret += s;
			}
			return( ret );
		} catch(ConnectException ex ) {
			//System.out.println("Connect exception");
		} catch(MalformedURLException ex ) {
		} catch(IOException ex) {
		} catch(Exception ex ) {
		}
		return( null );
	}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import pes6j.WEBMethods.PES6JWebHandler;
import pes6j.database.Db;
import pes6j.datablocks.ChannelInfo;
import pes6j.datablocks.ChannelList;
import pes6j.datablocks.DataCdKeyAndPass;
import pes6j.datablocks.DataChatMessage;
import pes6j.datablocks.DataId;
import pes6j.datablocks.DataIp;
import pes6j.datablocks.DataJoinRoom;
import pes6j.datablocks.DataPlayerTeamPositions;
import pes6j.datablocks.DataRoom;
import pes6j.datablocks.GroupInfo;
import pes6j.datablocks.IpInfo;
import pes6j.datablocks.Message;
import pes6j.datablocks.MessageBlock;
import pes6j.datablocks.MessageBlockByte;
import pes6j.datablocks.MessageBlockFriendsList;
import pes6j.datablocks.MessageBlockGameAnyId;
import pes6j.datablocks.MessageBlockGameChatMessage;
import pes6j.datablocks.MessageBlockGameEndInfo;
import pes6j.datablocks.MessageBlockGameEnterRoomFailFull;
import pes6j.datablocks.MessageBlockGameEnterRoomFailPass;
import pes6j.datablocks.MessageBlockGameGroupApplicationList;
import pes6j.datablocks.MessageBlockGameGroupInfo;
import pes6j.datablocks.MessageBlockGameGroupList;
import pes6j.datablocks.MessageBlockGameGroupMembers;
import pes6j.datablocks.MessageBlockGameIpInfo;
import pes6j.datablocks.MessageBlockGameIpInfoShort;
import pes6j.datablocks.MessageBlockGamePlayerGroup;
import pes6j.datablocks.MessageBlockGamePlayerGroupList;
import pes6j.datablocks.MessageBlockGamePlayerId;
import pes6j.datablocks.MessageBlockGamePlayerReady;
import pes6j.datablocks.MessageBlockGameRoomInfoShort;
import pes6j.datablocks.MessageBlockGameRoomList;
import pes6j.datablocks.MessageBlockGameRoomOptionSel;
import pes6j.datablocks.MessageBlockGameRoomPos;
import pes6j.datablocks.MessageBlockGameStart1;
import pes6j.datablocks.MessageBlockGameStart4344;
import pes6j.datablocks.MessageBlockLong;
import pes6j.datablocks.MessageBlockMenuFriends;
import pes6j.datablocks.MessageBlockMenuGroupInfo;
import pes6j.datablocks.MessageBlockMenuMenuList;
import pes6j.datablocks.MessageBlockMenuPlayerGroup;
import pes6j.datablocks.MessageBlockMenuQ1;
import pes6j.datablocks.MessageBlockVoid;
import pes6j.datablocks.MessageBlockZero;
import pes6j.datablocks.MetaMessageBlock;
import pes6j.datablocks.PESConnection;
import pes6j.datablocks.PESConnectionList;
import pes6j.datablocks.PlayerInfo;
import pes6j.datablocks.PlayerList;
import pes6j.datablocks.QUERIES;
import pes6j.datablocks.RoomInfo;
import pes6j.datablocks.RoomList;
import pes6j.datablocks.ServerInfo;
import pes6j.datablocks.UserInfo;
import pes6j.datablocks.Util;
import com.maxmind.geoip.LookupService;
import com.sun.net.httpserver.HttpServer;
					mes = new Message();
					mes.addBlock(new MessageBlockZero(0x000050F3));
					out.streamWriteEncodedNIO(mes);
				} catch( SQLException ex ) {
					mes = new Message();
					mes.addBlock(new MessageBlockLong(0xFFFFFFFF, 0x000050F3));
					out.streamWriteEncodedNIO(mes);
				}
			} else
			if( qId == 0x000050D0 ) {
				
				DataId did = new DataId(mb.getData());
				long pid = did.getId();
				
				out.log( "READ 0x50D0 this comes to change a player's permission to invite others into the group," +
						" player to change permission: " + pid );
				
				
				
				mes = new Message();
				mes.addBlock(new MessageBlockZero(0x000050D1));		
				out.streamWriteEncodedNIO(mes);
				
				try {
					Tools.dbChangePlayerInvitation( db, pid );
					PlayerInfo[] pl = Tools.dbLoadGroupMembers(db, player.getGid());
					
					PlayerInfo p = players.getPlayerInfo(pid);

					
					mes = new Message();
					mes.addBlock(new MessageBlockZero(0x00005061));
					out.streamWriteEncodedNIO(mes);
			
					
					mes = new Message();
					mes.addBlock(new MessageBlockGameGroupMembers(pl,
							0x00005062));
					out.streamWriteEncodedNIO(mes);
					
					mes = new Message();
					mes.addBlock(new MessageBlockZero(0x00005063));
					out.streamWriteEncodedNIO(mes);
					
					if( p != null ) {
						p.setInvitation( p.getInvitation() == 1 ? 2 : 1 );
						//sendPlayerList(channel);
					}			
					
				} catch (SQLException ex) {
					ex.printStackTrace();
				}
				
//				FileOutputStream fo = new FileOutputStream("5062_mio.dec");
//				fo.write(mes.getBytes());
//				fo.close();
				
			} else
			if (qId == 0x0000308C) {
				out.log( "READ 0x308C Leave the lobby?"); //$NON-NLS-1$
				mes = new Message();
				mes.addBlock(new MessageBlockZero(0x0000308D));
				out.streamWriteEncodedNIO(mes);
			} else

			// El jugador est� cambiando su comentario, el comentario es el
			// contenido del paquete y un 0x00 al final
			if (qId == 0x00004110) {
				out
						.log("MATCHES GAME CHANGECOMMENT CLIENT QUERY ANSWERING ZEROS:"); //$NON-NLS-1$

				/*
				 * Recuperamos el comentario
				 */

				String comment = new String(mb.getData(), 0, Util.strlen(mb
						.getData(), 0));
				player.setComment(comment);
//				try {
//					Tools.dbSavePlayerComment(db, player);
//				} catch (SQLException ex) {
//					out.log("UNABLE TO SAVE THE COMMENT!!!!");
//					ex.printStackTrace();
//				}
				mes = new Message();
				mes.addBlock(new MessageBlockZero(++qId));
				out.streamWriteEncodedNIO(mes);
			} else if (qId == 0x0000434D) {
				out
						.log("MATCHES GAME ROOM CHANGE NAME AND PASS QUERY ANSWERING WITH NEW ROOM INFO:"); //$NON-NLS-1$
				DataRoom room = new DataRoom(mb.getData());
				RoomInfo r = rooms.searchPlayer(player.getId());
				r.setPassword(room.getPassword());
				r.setName(room.getName());
				r.setPassworded(room.hasPassword());
				//rooms.setRoom(r.getId(), r);
				mes = new Message();
				mes.addBlock(new MessageBlockGameRoomList(new RoomInfo[] { r },
						QUERIES.GAME_ROOMINFO_SERVER_QUERY));
				connections.sendToChannelNotInGameEncodedNIO( channel, mes );

				mes = new Message();
				mes.addBlock(new MessageBlockZero(0x0000434E));
				out.streamWriteEncodedNIO(mes);

			} else if (qId == 0x00004380) {
				out
						.log("MATCHES GAME FORCE DISCARD PARTICIPATION QUERY ANSWERING WITH NEW ROOM INFO:"); //$NON-NLS-1$
				DataId did = new DataId(mb.getData());

				rooms.removePlayerSelInRoom(did.getId());
				RoomInfo r = rooms.searchPlayer(did.getId());

				if( r == null ) {
					out.log( "THE ROOM DOESNT EXIST!!! RETURNING"); //$NON-NLS-1$
					return;
				}
				
				mes = new Message();
				mes
						.addBlock(new MessageBlockGameRoomInfoShort(r,