package org.dipgame.gameManager;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
/**
* Runs games as GameManager does but without a GUI. It is very useful for running experiments that consist on several game executions.
*
* @author Angela Fabregues, IIIA-CSIC, fabregues@iiia.csic.es
*/
public class ConsoleGameManager {
private HashMap<String, String> loadedPaths;
private HashMap<String, String> loadedPlayers;
private List<String[]> players;
private List<Process> processes;
private List<String> alerts;
private boolean canRun = true;
private boolean isPlaying;
private static final int GAME_PORT = 16713;
private static final int MTL = 300;
private static final int RTL = 120;
private static final int BTL = 120;
private String gameId = "ConsoleGameManager";
private int time = 12000;
public ConsoleGameManager(List<String[]> players) throws IOException {
this.players = players;
alerts = new Vector<String>();
loadPaths();
checkPaths();
loadPlayers();
}
private void loadPaths() throws IOException {
loadedPaths = new HashMap<String, String>();
FileReader fr = new FileReader(new File("files/paths.txt"));
LineNumberReader ln = new LineNumberReader(fr);
String linea = ln.readLine();
while (linea != null) {
String[] words = linea.split(";");
if(words[0].equals("JAVA_ENV")){
loadedPaths.put("JAVA_ENV", words[1]);
}else if(words[0].equals("PARLANCE_PATH")){
loadedPaths.put("PARLANCE_PATH", words[1]);
}else if(words[0].equals("AISERVER_PATH")){
loadedPaths.put("AISERVER_PATH", words[1]);
}else if(words[0].equals("AIMAPPER_PATH")){
loadedPaths.put("AIMAPPER_PATH", words[1]);
}
linea = ln.readLine();
}
}
private void checkPaths() {
List<String> pathAlerts = new Vector<String>();
if(!loadedPaths.containsKey("JAVA_ENV")){
loadedPaths.put("JAVA_ENV", "java");
pathAlerts.add("'JAVA_ENV' is set to 'java'");
}
if(!loadedPaths.containsKey("PARLANCE_PATH")){
loadedPaths.put("PARLANCE_PATH", "parlance-server");
pathAlerts.add("'PARLANCE_PATH' is set to 'parlance-server'");
}
if(pathAlerts.size()>0){
String alertMessage = "Paths to the java and parlance-server should be provided in the file 'paths.txt'. The application is being executed with the following paths set to default:\n";
for(String pathAlert: pathAlerts){
alertMessage+=pathAlert+"\n";
}
alertMessage+="If Game Manager cannot find java and parlance-server, the execution of the application fails.";
alerts.add(alertMessage);
}
String vers = System.getProperty("os.name").toLowerCase();
if(vers.contains("windows")){
//Path checking work for linux and unix. TODO implement a path checking for windows.
}else{
for(String key: loadedPaths.keySet()){
File path = new File(loadedPaths.get(key));
if(!path.exists() || !path.canExecute()){
alerts.add("Game Manager cannot find '"+key+"' that is set to '"+path+"'. This should be fixed in order to be able to run games.");
canRun = false;
}
}
}
}
private void loadPlayers() throws IOException {
loadedPlayers = new HashMap<String, String>();
FileReader fr = new FileReader(new File("files/availablePlayers.txt"));
LineNumberReader ln = new LineNumberReader(fr);
String line = ln.readLine();
try{
while (line != null) {
line = line.trim();
if(line.length()!=0 && !line.startsWith("//")){ //ignoring empty lines and comments
String key = line.substring(0, line.indexOf(';'));
String value = line.substring(line.indexOf(';'+1));
loadedPlayers.put(key, value);
}
line = ln.readLine();
}
}catch (StringIndexOutOfBoundsException e) {
alerts.add("Syntax error in file 'files/availablePlayers.txt' line: '"+line+"'.");
canRun = false;
}
}
private void run() {
System.out.println();
for(String alert: alerts){
System.out.println(alert);
}
System.out.println();
if(!canRun){
System.out.println("It is not possible to run ConsoleGameManager. You should set it up first.");
return;
}
//If the application is killed, it stopes all processes belonging to it.
Runtime.getRuntime().addShutdownHook(new Thread(){
public void run() {
destroyProcesses();
}
});
processes = new Vector<Process>(9);
try{
if(loadedPaths.containsKey("AISERVER_PATH")){
//Runs AiServer
String[] cmd = {"cmd", "/c", loadedPaths.get("AISERVER_PATH")+"//AiServer", "-var=standard", "-port="+GAME_PORT,
"-start", "-exit=60", "-h", "-lvl=0", "-mtl="+MTL, "-rtl="+RTL, "-btl="+BTL, "-npr", "-npb", "-ptl=0"};
ProcessBuilder processBuilder = new ProcessBuilder(cmd);
processBuilder.directory(new File(loadedPaths.get("AISERVER_PATH")));
processes.add(processBuilder.start());
}else{
//Runs parlance-server
String[] cmd = { loadedPaths.get("PARLANCE_PATH"), "-g1","standard" };
processes.add(Runtime.getRuntime().exec(cmd));
}
String call = processProgramCall(loadedPaths, "<JAVA_ENV> -jar programs/negoServer-2.0.3-full.jar <nego_port> <ip> <port> <game_id> STDOUT <time>", "negoServer");
Process observer = Runtime.getRuntime().exec(call);
processes.add(observer);
isPlaying = true;
boolean isFinished = false;
boolean isNegoStarted = false;
InputStreamReader reader = new InputStreamReader(observer.getInputStream());
int n;
String readText="";
char[] buffer;
do{
buffer = new char[512];
n = reader.read(buffer);
if(n>0){
readText = new String(buffer,0,n);
//An observer has connected. Have 0 players and 1 observer. Need 7 to start
if(!isNegoStarted && (readText.contains("An observer has connected.") || readText.contains("1 observer."))){
isNegoStarted = true;
//Run players
for (String[] player : players) {
if (!player[0].equals(Utils.LEAVE_EMPTY)) {
String programCall = processProgramCall(loadedPaths,
loadedPlayers.get(player[0]), player[1]);
processes.add(Runtime.getRuntime()
.exec(programCall));
}
}
}else{
if(!isNegoStarted && (readText.contains(" YES ( MAP (") || readText.contains(" 'standard' ) )"))){
isFinished = true;
}
}
System.out.println(readText);
}
Thread.currentThread().sleep(100);
}while(isPlaying && (n==buffer.length || !isFinished)); //Reads until cancel button is pressed or incomplete buffer is read after finding a "Winner" string.
reader.close();
if(isFinished){
destroyProcesses();
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* Looks or tags and translates them to their values
* @param loadedPaths
* @param programCallTemplate
* @param playerName
* @return
*/
private String processProgramCall(HashMap<String, String> loadedPaths, String programCallTemplate, String playerName) {
String programCall = "";
String tag = "";
int i=0;
try{
boolean isTranslating = false;
for(; i<programCallTemplate.length(); i++){
char c = programCallTemplate.charAt(i);
if(c=='<'){
if(!isTranslating){
isTranslating = true;
}else{
System.err.println("Syntax error in "+programCallTemplate.substring(0, i));
}
}else if(c=='>'){
if(isTranslating){
programCall += translateTag(loadedPaths, tag, playerName);
tag = "";
isTranslating = false;
}else{
System.err.println("Syntax error in "+programCallTemplate.substring(0, i));
}
}else{
if(isTranslating){
tag += c;
}else{
programCall += c;
}
}
}
}catch (Exception e) {
System.err.println("Syntax error in "+programCallTemplate.substring(0, i)+". "+e.getMessage());
}
return programCall;
}
/**
* Translates tags to their value
* @param loadedPaths
* @param tag
* @param playerName
* @return
* @throws ParseException
*/
private String translateTag(HashMap<String,String> loadedPaths, String tag, String playerName) throws Exception{
if(tag.equals("ip")){
return "localhost";
}else if(tag.equals("port")){
return GAME_PORT+"";
}else if(tag.equals("name")){
return playerName;
}else if(tag.equals("log_folder")){
return Utils.LOG_FOLDER;
}else if(tag.equals("nego_ip")){
return "localhost";
}else if(tag.equals("nego_port")){
return (GAME_PORT+1)+"";
}else if(tag.equals("game_id")){
return gameId;
}else if(tag.equals("time")){
return time+"";
}else if(loadedPaths.containsKey(tag)){
return loadedPaths.get(tag);
}else{
throw new Exception("Unknown tag '"+tag+"'.");
}
}
private void destroyProcesses() {
isPlaying = false;
for (Process process: processes) {
process.destroy();
}
}
static public void main(String argv[]) throws Exception {
if(argv.length == 0){
System.out.println("Usage:\n ConsoleGameManager [<player> <player> <player> <player> <player> <player> <player>]");
System.out.println("By default, ConsoleGameManager runs a game with 7 dumbbots.");
List<String[]> players = new Vector<String[]>();
for(int i=0; i<7; i++){
players.add(new String[]{"dumbbot","player_"+i});
}
ConsoleGameManager a = new ConsoleGameManager(players);
a.run();
}else if(argv.length==7){
List<String[]> players = new Vector<String[]>();
for(int i=0; i<7; i++){
players.add(new String[]{argv[i],"player_"+i});
}
ConsoleGameManager a = new ConsoleGameManager(players);
a.run();
}else{
System.err.println("Usage:\n ConsoleGameManager [<player> <player> <player> <player> <player> <player> <player>]");
}
}
}