package ww10; import game.PublicPlayerInfo; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.HashMap; import org.apache.commons.io.IOUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.biotools.meerkat.GameInfo; import ww10.exception.InvalidInputException; import ww10.exception.AuthorizationException; import ww10.exception.TableFullException; import ww10.DataModel; import bots.prologbot.PrologBot; public class RequestHandler implements Runnable { private Socket clientSocket; private PrologBotServer server; public RequestHandler(Socket clientSocket, PrologBotServer server) { this.clientSocket = clientSocket; this.server = server; } @Override public void run() { try { InputStream inputStream = clientSocket.getInputStream(); OutputStream outputStream = clientSocket.getOutputStream(); handleRequest(inputStream, outputStream); inputStream.close(); outputStream.close(); clientSocket.close(); } catch (IOException e) { System.err.println(e.getMessage()); e.printStackTrace(); System.err.println("Accept failed: 4444"); //System.exit(-1); } } private void handleRequest(InputStream inputStream, OutputStream outputStream) { DataOutputStream out = new DataOutputStream(outputStream); try { String request = IOUtils.toString(inputStream); // console feedback //System.out.println("Incoming request: " + request); JSONObject requestO = new JSONObject(request).getJSONObject("request"); String requestType = requestO.getString("type"); String reply = null; //XXX: filtered console feedback if(!requestType.equals( "fetchData") && !requestType.equals("listTables")){ System.out.println("Incoming request: " + request); } // handle request if(requestType.equals( "startTable")){ reply = handleStartTableRequest(requestO); } else if(requestType.equals( "joinTable")){ reply = handleJoinTableRequest(requestO); } else if(requestType.equals( "fetchData")){ reply = handleFetchDataRequest(requestO); } else if(requestType.equals("listTables")){ reply = handleListTablesRequest(); } // Admin functionality else if(requestType.equals("stopTable")){ reply = handleStopTableRequest(requestO); } else if(requestType.equals("killTable")){ reply = handleKillTableRequest(requestO); } else if(requestType.equals("killTestTable")){ reply = handleKillTestTableRequest(requestO); } else if(requestType.equals("resumeTable")){ reply = handleResumeTableRequest(requestO); } else if(requestType.equals("leaveTable")){ reply = handleLeaveTableRequest(requestO); } else if(requestType.equals("kickPlayer")){ reply = handleKickTableRequest(requestO); } else{ //requestType does not match any known request -> Error throw new InvalidInputException("RequestType " + requestType + " does not match any known request"); } if(reply != null){ out.writeBytes(reply); } } catch (IOException e) { System.err.println(e.getMessage()); e.printStackTrace(); System.err.println("Could not parse from inputstream."); //System.exit(-1); } catch (JSONException e) { System.err.println(e.getMessage()); e.printStackTrace(); try { out.writeBytes(createError("JSON", "Could not parse from JSON")); } catch (IOException e1) { // Can not reach the webserver - not much can be done about this... e1.printStackTrace(); } } catch (InvalidInputException e){ System.err.println("Invalid input: "+e.getMessage()); try { out.writeBytes(createError("InvalidInput", e.getMessage())); } catch (IOException e1) { // Can not reach the webserver - not much can be done about this... e1.printStackTrace(); } } catch (TableFullException e){ System.err.println("Table is full: "+e.getMessage()); try { out.writeBytes(createError("TableFull", e.getMessage())); } catch (IOException e1) { // Can not reach the webserver - not much can be done about this... e1.printStackTrace(); } } catch (AuthorizationException e) { System.err.println("Authorization failure: "+e.getMessage()); try { out.writeBytes(createError("Authorization", e.getMessage())); } catch (IOException e1) { // Can not reach the webserver - not much can be done about this... e1.printStackTrace(); } } try { inputStream.close(); } catch (IOException e) { System.err.println("Could not close inputstream"); e.printStackTrace(); } } private String handleFetchDataRequest(JSONObject requestO) throws JSONException, IOException, InvalidInputException { String tableName = requestO.getString("tableName"); JSONObject results = new JSONObject(); JSONArray players = new JSONArray(); Table table = server.getTable(tableName); if(table == null){ throw new InvalidInputException("Table " +tableName + " does not exist"); } server.tableReceivedRequest(tableName); DataModel tableResults = table.getDataModel(); for(String player: tableResults.getPlayerNames()) { players.put(getPlayerStats(player, tableResults)); } JSONObject player = new JSONObject(); player.put("player", players); results.put("result", player); if(table.isRunning()) { results.put("gamesPerSec", tableResults.getGamesPerSecond()); } else { results.put("gamesPerSec", 0); } results.put("bigBlind", server.getGameparameters().getBigBlind()); results.put("smallBlind", server.getGameparameters().getSmallBlind()); results.put("minimumRaise", server.getGameparameters().getBigBlind()); results.put("startMoney", server.getGameparameters().getInitialBankroll()); String reply = results.toString(); return reply; } private String handleStartTableRequest(JSONObject requestO) throws JSONException, InvalidInputException{ String tableName = requestO.getString("tableName"); int nbPlayers = requestO.getInt("nbPlayers"); if(server.getTable(tableName) != null){ throw new InvalidInputException("Table " + tableName + " already exists."); } if(nbPlayers <= 1 || nbPlayers >= 24){ throw new InvalidInputException("Can not support a table with " + nbPlayers + " players." + "Only values between 2 and 23 are accepted."); } String pw = requestO.getString("password"); Table currTable = new Table(tableName, nbPlayers, pw, server.getGameparameters()); currTable.run(); server.addTable(tableName, currTable); System.out.println("Created table " + tableName + " for " + nbPlayers + " players."); return createReply("Created table " + tableName + " for " + nbPlayers + " players."); } private String handleJoinTableRequest(JSONObject requestO) throws JSONException, InvalidInputException, TableFullException{ String tableName = requestO.getString("tableName"); Table table = server.getTable(tableName); if(table == null){ throw new InvalidInputException("Table " + tableName + " does not exist."); } server.tableReceivedRequest(tableName); String playerName = requestO.getString("playerName"); onNewBot(requestO, table); table.resume(); System.out.println("Player " + playerName + " has joined table " + tableName + "."); return createReply("Player " + playerName + " has joined table " + tableName + "."); } private String handleLeaveTableRequest(JSONObject requestO) throws JSONException, InvalidInputException{ String tableName = requestO.getString("tableName"); Table table = server.getTable(tableName); if(table == null) { if(table == null){ throw new InvalidInputException("Table " + tableName + " does not exist."); } } server.tableReceivedRequest(tableName); String playerName = requestO.getString("playerName"); onLeavingTable(playerName, table); table.resume(); return createReply("Player " + playerName + " has left table " + tableName + "."); } private String handleKickTableRequest(JSONObject requestO) throws JSONException, InvalidInputException, AuthorizationException{ String tableName = requestO.getString("tableName"); String pw = requestO.getString("password"); Table table = server.getTable(tableName); if(table == null) { if(table == null){ throw new InvalidInputException("Table " + tableName + " does not exist."); } } if(table.passwordValid(pw)){ return handleLeaveTableRequest(requestO); } else{ throw new AuthorizationException("Password is incorrect."); } } private String handleListTablesRequest() throws JSONException{ JSONObject result = new JSONObject(); JSONArray tables = new JSONArray(); for(String tableName: server.listTableNames()){ Table currTable = server.getTable(tableName); JSONObject currTableJSON = new JSONObject(); currTableJSON.put("name", tableName); currTableJSON.put("nbPlayers", currTable.getGameInfo().getNumPlayers()); currTableJSON.put("running", currTable.isRunning()); tables.put(currTableJSON); } result.put("tables", tables); return result.toString(); } private String handleStopTableRequest(JSONObject requestO) throws JSONException, InvalidInputException, AuthorizationException{ String tableName = requestO.getString("tableName"); Table table = server.getTable(tableName); if(table==null){ throw new InvalidInputException("Simulation of table " + tableName + " cannot be stopped as " + "it does not exist"); } if(table.passwordValid(requestO.getString("password"))){ table.stop(); } else { throw new AuthorizationException("Password is incorrect."); } return createReply("Stopped simulation of table " + tableName + ". Table data are still available"); } private String handleKillTableRequest(JSONObject requestO) throws JSONException, InvalidInputException, AuthorizationException{ String tableName = requestO.getString("tableName"); Table table = server.getTable(tableName); if(table==null){ throw new InvalidInputException("Data of table " + tableName + " cannot be removed as " + "it does not exist"); } if(table.passwordValid(requestO.getString("password"))){ table.terminate(); server.removeTable(tableName); } else { throw new AuthorizationException("Password is incorrect."); } return createReply("Removed table " + tableName + " from records and stopped its simulation"); } private String handleKillTestTableRequest(JSONObject requestO) throws InvalidInputException, JSONException{ String tableName = requestO.getString("tableName"); Table table = server.getTable(tableName); if(table==null){ throw new InvalidInputException("Data of table " + tableName + " cannot be removed as " + "it does not exist"); } table.terminate(); server.removeTable(tableName); return createReply("Removed test table " + tableName + " from records and stopped its simulation"); } private String handleResumeTableRequest(JSONObject requestO) throws JSONException, InvalidInputException, AuthorizationException{ String tableName = requestO.getString("tableName"); Table table = server.getTable(tableName); if(table==null){ throw new InvalidInputException("Data of table " + tableName + " cannot be removed as " + "it does not exist"); } if(table.passwordValid(requestO.getString("password"))){ table.resume(); } else { throw new AuthorizationException("Password is incorrect."); } return createReply("Resumed simulation of table " + tableName + "."); } private JSONObject getPlayerStats(String player, DataModel tableResults) throws JSONException{ JSONObject currPlayer = new JSONObject(); currPlayer.put("name", tableResults.getChosenName(player)); //Put average profit double avg = tableResults.getAvgProfit(player); currPlayer.put("avg profit", avg); //Put actions int nbFold = tableResults.getNbFolds(player); int nbRaise = tableResults.getNbRaises(player); int nbCall = tableResults.getNbCalls(player); currPlayer.put("nbFolds", nbFold); currPlayer.put("nbRaises", nbRaise); currPlayer.put("nbCalls", nbCall); //Put thinking time long thinkingTime = tableResults.getRunTime(player); currPlayer.put("time used", thinkingTime); //Put rules used HashMap<String, Integer> rulesUsed = tableResults.getRulesUsed(player); JSONArray rulesJSON = new JSONArray(); for(String ruleName: rulesUsed.keySet()){ JSONObject rule = new JSONObject(); rule.put("name", ruleName); rule.put("times", rulesUsed.get(ruleName)); rulesJSON.put(rule); } currPlayer.put("rulesUsed", rulesJSON); //Put last submit time currPlayer.put("lastSubmit", tableResults.getLastSubmit(player)); return currPlayer; } protected void onNewBot(JSONObject botDescr, Table table) throws JSONException, TableFullException { String playerName = botDescr.getString("playerName"); String fixedName = table.getDataModel().getFixedName(playerName); PublicPlayerInfo playerInfo = table.getGameInfo().getPlayer(playerName); if(fixedName != null) { //player exists -- overwrite profile playerInfo = table.getGameInfo().getPlayer(fixedName); } else { //player does not yet exist -- find and replace a callbot int i=0; boolean found = false; while(!found && i< table.getGameInfo().getNumPlayers()) { playerInfo = table.getGameInfo().getPlayer(i); if(playerInfo.isCallbot()){ found = true; } i++; } if(!found){ //If neither, error, no player profile can be used throw new TableFullException("There is no more room on the table"); } } PrologBot bot = (PrologBot) playerInfo.getBot(); //playerInfo.setName(playerName); playerInfo.setCallbot(false); bot.setProlog(botDescr.getString("description")); table.getDataModel().onSubmit(bot.getName(), truncate(playerName)); } protected void onLeavingTable(String playerName, Table table) throws InvalidInputException{ String fixedName = table.getDataModel().getFixedName(playerName); if(fixedName == null){ throw new InvalidInputException("Player " + playerName + " is not playing at table " + table.getTableName()); } PublicPlayerInfo playerInfo = table.getGameInfo().getPlayer(fixedName); GameInfo gameInfo = playerInfo.getGameInfo(); PrologBot bot = (PrologBot) table.getBotRepository().createBot("PrologBot/PrologBot"); bot.setGameInfo(gameInfo); bot.setIngameName(fixedName); playerInfo.setBot(bot); playerInfo.setCallbot(true); table.getDataModel().onSubmit(fixedName, fixedName); } private String truncate(String name) { if (name.length() > 20) return name.substring(0, 20); else return name; } private String createError(String name, String message){ JSONObject error = new JSONObject(); try { error.put("type", "error"); error.put("name", name); error.put("message", message); } catch (JSONException e) { // Should not occur! e.printStackTrace(); } return error.toString(); } private String createReply(String message){ JSONObject reply = new JSONObject(); try { reply.put("type", "Acknowledge"); reply.put("message", message); } catch (JSONException e) { // Should not occur! e.printStackTrace(); } return reply.toString(); } }