static void RunClient() { Log.TraceMessage(Log.Nav.NavIn, "This Arena is Client.", Log.LogType.Info); Log.TraceMessage(Log.Nav.NavIn, "Starting Client FTP Server...", Log.LogType.Info); StartFTPServer(false); Log.TraceMessage(Log.Nav.NavIn, "Creating the Keep-Alive Ping that let's the host know we are here and ready to run games...", Log.LogType.Info); string resultStr = ""; using (Socket resultsSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { using (Socket ping = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { using (UdpClient ask_for_game = new UdpClient(UDP_ASK_PORT)) { ask_for_game.Client.ReceiveTimeout = 5000; Log.TraceMessage(Log.Nav.NavIn, "Only allow 5 seconds for sending and receiving...", Log.LogType.Info); while (true) { try { IPEndPoint remoteEP; if (resultStr == "") { Log.TraceMessage(Log.Nav.NavIn, "Sending Ping...", Log.LogType.Info); remoteEP = new IPEndPoint(IPAddress.Parse(HOST_ADDR), UDP_CONFIRM_PORT); ping.SendTo(new byte[] { 1 }, remoteEP); // Ping -- we are still here } else { Log.TraceMessage(Log.Nav.NavIn, "Sending Results...", Log.LogType.Info); remoteEP = new IPEndPoint(IPAddress.Parse(HOST_ADDR), UDP_CONFIRM_PORT); byte[] toSendResults = Encoding.ASCII.GetBytes(resultStr); Log.TraceMessage(Log.Nav.NavIn, "Sending toSendResults: " + resultStr + " as bytes=" + toSendResults.Length, Log.LogType.Info); resultsSocket.SendTo(toSendResults, remoteEP); // Ping -- we are still here } Log.TraceMessage(Log.Nav.NavIn, "Waiting for game...", Log.LogType.Info); byte[] data = ask_for_game.Receive(ref remoteEP); string str_data = System.Text.Encoding.Default.GetString(data); if (data != null && data.Length == 1 && data[0] == 1) { Log.TraceMessage(Log.Nav.NavIn, "Host received results-clearing results.", Log.LogType.Info); resultStr = ""; Directory.Delete(ARENA_FILES_PATH, true); Directory.CreateDirectory(ARENA_FILES_PATH); } if (data != null && data.Length == 1 && data[0] == 0) { Log.TraceMessage(Log.Nav.NavIn, "We have been told to run game--LET'S GO!", Log.LogType.Info); List <string> results = BuildAndRunGame(); Log.TraceMessage(Log.Nav.NavIn, "Results returned with size" + results.Count(), Log.LogType.Info); string status = "finished"; string winReason = ""; string loseReason = ""; string winnerName = ""; string winnerSubmissionNumber = ""; string loserName = ""; string loserSubmissionNumber = ""; string logURL = ""; foreach (string s in results) { string[] split = s.Split(Environment.NewLine); string[] splitLine = split[split.Length - 1].Split('_'); splitLine[0] = splitLine[0].Substring(splitLine[0].LastIndexOf('/') + 1); string[] reversedSplitLine = splitLine.Reverse().ToArray(); string teamName = ""; for (int i = reversedSplitLine.Length - 1; i > 1; i--) { teamName += reversedSplitLine[i] + "_"; } teamName = teamName.Substring(0, teamName.Length - 1); Log.TraceMessage(Log.Nav.NavIn, "team name" + teamName, Log.LogType.Info); string teamSubmissionNumber = reversedSplitLine[1]; Log.TraceMessage(Log.Nav.NavIn, "sub num" + teamSubmissionNumber, Log.LogType.Info); bool won = false; foreach (string i in split) { if (i.ToUpper().Contains("WON")) { Log.TraceMessage(Log.Nav.NavIn, teamName + "won", Log.LogType.Info); won = true; winReason = i; } else if (i.ToUpper().Contains("ERROR")) { Log.TraceMessage(Log.Nav.NavIn, teamName + "error", Log.LogType.Info); loseReason = "error"; } else if (i.ToUpper().Contains("LOST")) { Log.TraceMessage(Log.Nav.NavIn, teamName + "lost", Log.LogType.Info); loseReason = i; } else if (i.ToUpper().Contains("HTTP")) { logURL = i; Log.TraceMessage(Log.Nav.NavIn, teamName + "logurl" + logURL, Log.LogType.Info); } } if (won) { winnerName = teamName; winnerSubmissionNumber = teamSubmissionNumber; } else { loserName = teamName; loserSubmissionNumber = teamSubmissionNumber; } } Log.TraceMessage(Log.Nav.NavOut, status + " " + winReason + " " + loseReason + " " + logURL + " " + winnerName + " " + winnerSubmissionNumber + " " + loserName + " " + loserSubmissionNumber, Log.LogType.Info); HTTP.HTTPPost(status, winReason, loseReason, logURL, winnerName, winnerSubmissionNumber, loserName, loserSubmissionNumber); resultStr = winnerName + ";" + logURL; Thread.Sleep(8000); Directory.Delete(ARENA_FILES_PATH, true); Directory.CreateDirectory(ARENA_FILES_PATH); } } catch { Log.TraceMessage(Log.Nav.NavIn, "5 second timeout on receiving game...", Log.LogType.Info); } } } } } }
/// <summary> /// Given the file path, compile the AI and run it using C++ -- run until the results file shows a win, loss, or error /// </summary> /// <param name="file"></param> /// <returns></returns> public static bool BuildAndRun(string file, string DA_GAME, string gameSession) { bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); bool isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); if (isWindows) { Log.TraceMessage(Log.Nav.NavIn, "Is Windows...", Log.LogType.Info); Log.TraceMessage(Log.Nav.NavIn, "Starting Background Process...", Log.LogType.Info); //Unimplemented } else if (isLinux) { Log.TraceMessage(Log.Nav.NavIn, "Is Linux.", Log.LogType.Info); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); using (Process process = new Process()) { process.StartInfo.CreateNoWindow = true; process.StartInfo.RedirectStandardError = false; process.StartInfo.RedirectStandardInput = true; process.StartInfo.RedirectStandardOutput = false; process.StartInfo.UseShellExecute = false; process.StartInfo.FileName = Environment.GetEnvironmentVariable("SHELL"); Log.TraceMessage(Log.Nav.NavIn, "Grabbing Shell Process...", Log.LogType.Info); if (process.Start()) { process.StandardInput.WriteLine("cd " + file.Substring(0, file.LastIndexOf('/'))); if (File.Exists(file.Substring(0, file.LastIndexOf('/') + 1) + "testRun")) { File.Delete(file.Substring(0, file.LastIndexOf('/') + 1) + "testRun"); } using (StreamWriter sw = new StreamWriter(file.Substring(0, file.LastIndexOf('/') + 1) + "testRun")) { sw.AutoFlush = true; sw.WriteLine("#!/bin/bash"); sw.WriteLine("if [ -z \"$1\" ]"); sw.WriteLine(" then"); sw.WriteLine(" echo \"No argument(s) supplied. Please specify game session you want to join or make.\""); sw.WriteLine(" else"); sw.WriteLine(" ./run " + DA_GAME + " -s 127.0.0.1 -r \"$@\""); sw.WriteLine("fi"); } Log.TraceMessage(Log.Nav.NavIn, "Rewrote script-- running", Log.LogType.Info); process.StandardInput.WriteLine("sudo chmod 777 testRun && sudo chmod 777 run && sudo make clean && sudo make >> results.txt 2>&1 && sudo ./testRun " + gameSession); //Make the testRun file executable, clean compilation, compile, and output any bad compilation to text file. Log.TraceMessage(Log.Nav.NavIn, "Wrote Commands to process...", Log.LogType.Info); //If compile fails, return false; if (stopWatch.Elapsed.Minutes > 15) { process.Kill(); return(false); } string results = ""; do { Log.TraceMessage(Log.Nav.NavIn, "Calling HTTPPostGetStatus with " + DA_GAME + " and " + gameSession, Log.LogType.Info); //Call Status GameServer API until status == "over" var x = HTTP.HTTPPostGetStatus(DA_GAME, gameSession); Log.TraceMessage(Log.Nav.NavIn, "x returned as " + x, Log.LogType.Info); if (x != null) { Log.TraceMessage(Log.Nav.NavIn, "x.clients.count is " + x.clients.Count, Log.LogType.Info); Log.TraceMessage(Log.Nav.NavIn, "x.status is " + x.status, Log.LogType.Info); } if (x != null && x.status.Contains("over")) { Log.TraceMessage(Log.Nav.NavIn, "Returning true ", Log.LogType.Info); return(true); } //Wait 10 seconds for game to finish Thread.Sleep(10 * 1000); //Check for compile errors Log.TraceMessage(Log.Nav.NavIn, "Checking for compile errors...", Log.LogType.Info); int index = file.LastIndexOf('/') + 1; Log.TraceMessage(Log.Nav.NavIn, "Last index of slash is " + index, Log.LogType.Info); string resultsFile = file.Substring(0, index) + "results.txt"; Log.TraceMessage(Log.Nav.NavIn, "Results File=" + resultsFile, Log.LogType.Info); if (File.Exists(resultsFile)) { Log.TraceMessage(Log.Nav.NavIn, "Results file exists reading...", Log.LogType.Info); using (StreamReader sr = new StreamReader(resultsFile)) { results = sr.ReadToEnd() + Environment.NewLine + file; } Log.TraceMessage(Log.Nav.NavIn, "Results=" + results, Log.LogType.Info); if (results.ToUpper().Contains(" ERROR ") && !results.ToUpper().Contains("0 ERROR")) //C# says build succeeded 0 Errors { return(false); } } else { Log.TraceMessage(Log.Nav.NavIn, "Results file does not exist...", Log.LogType.Info); } } while (true); } } } return(false); }
static List <string> BuildAndRunGame() { try { List <string> answers = new List <string>(); Log.TraceMessage(Log.Nav.NavIn, "Building and Running Game ", Log.LogType.Info); if (!Directory.Exists(ARENA_FILES_PATH)) { Directory.CreateDirectory(ARENA_FILES_PATH); } var files = Directory.GetFiles(ARENA_FILES_PATH); Log.TraceMessage(Log.Nav.NavIn, "ARENA FILES Directory Contains " + files.Count() + " files.", Log.LogType.Info); List <Tuple <Task <bool>, string> > allGames = new List <Tuple <Task <bool>, string> >(); //First parameter is the thread, second parameter is the file being run (which has the player name) List <string> playerNames = new List <string>(); //Call SETUP GameServer API and create random game session foreach (var f in files) { var fs = f.Substring(f.LastIndexOf('/') + 1); // From C:\Users\Me\Documents\team1_1_csharp.zip to team_one_1_cs.zip var withoutZip = fs.Substring(0, fs.LastIndexOf(".zip")); //To team_one_1_cs string[] split = withoutZip.Split('_'); // ["team","one","1","cs"] var reversed = split.Reverse().ToArray(); // ["cs","1","one","team"] string lang = reversed[0]; //"cs" string submission = reversed[1]; //"1" string teamName = ""; for (int j = reversed.Length - 1; j > 1; j--) { teamName += reversed[j] + "_"; // "team_one_" } teamName = teamName.Substring(0, teamName.Length - 1); //"team_one" playerNames.Add(teamName); } string gameSession = "seth" + DateTime.Now.Ticks; gameSettings gSettings = new gameSettings() { playerNames = playerNames.ToArray() }; while (true) { if (HTTP.HTTPPostStartGame(DA_GAME, gameSession, gSettings)) { Log.TraceMessage(Log.Nav.NavIn, "Posted.", Log.LogType.Info); break; } Thread.Sleep(100); Log.TraceMessage(Log.Nav.NavIn, "Connecting...", Log.LogType.Info); } foreach (var file in files) { Log.TraceMessage(Log.Nav.NavIn, "Creating Thread for file " + file, Log.LogType.Info); Task <bool> t = Task.Run(() => RunGame(file, gameSession)); var fs = file.Substring(file.LastIndexOf('/') + 1); // From C:\Users\Me\Documents\team1_1_csharp.zip to team_one_1_cs.zip var withoutZip = fs.Substring(0, fs.LastIndexOf(".zip")); //To team_one_1_cs allGames.Add(new Tuple <Task <bool>, string>(t, withoutZip)); } Log.TraceMessage(Log.Nav.NavIn, "Starting WaitAny ", Log.LogType.Info); Task.WaitAny(allGames.Select(_ => _.Item1).ToArray()); //Wait for all the threads to finish Log.TraceMessage(Log.Nav.NavIn, "Finished WaitAny", Log.LogType.Info); for (int i = 0; i < allGames.Count; i++) { if (allGames[i].Item1.IsCompleted && allGames[i].Item1.Result == false) //Error on compilation { for (int j = 0; j < allGames.Count; j++) { try { allGames[j].Item1.Dispose(); } catch (Exception exA) { Log.TraceMessage(Log.Nav.NavOut, "Failed to abort thread..." + exA.Message, Log.LogType.Error); } } Log.TraceMessage(Log.Nav.NavIn, "At least one client failed to compile.", Log.LogType.Info); answers.Add("Other player did not compile"); //winReason answers.Add("You did not compile"); // loseReason if (i == 0) //If the first thread errored, set the winner as the second thread { Log.TraceMessage(Log.Nav.NavIn, "First Thread did not compile.", Log.LogType.Info); string[] reversed = allGames[1].Item2.Split('_').Reverse().ToArray(); // ["cs","1","one","team"] string teamName = ""; for (int j = reversed.Length - 1; j > 1; j--) { teamName += reversed[j] + "_"; // "team_one_" } teamName = teamName.Substring(0, teamName.Length - 1); //"team_one" answers.Add(teamName); //winnerName --- May not be 0 -- its in the file somewhere answers.Add(reversed[1]); //winnerSubmissionNumber --- May not be 1 -- its in the file somewhere reversed = allGames[0].Item2.Split('_').Reverse().ToArray(); // ["cs","1","one","team"] teamName = ""; for (int j = reversed.Length - 1; j > 1; j--) { teamName += reversed[j] + "_"; // "team_one_" } teamName = teamName.Substring(0, teamName.Length - 1); //"team_one" answers.Add(teamName); //loserName --- May not be 0 -- its in the file somewhere answers.Add(reversed[1]); //loserSubmissionNumber --- May not be 1 -- its in the file somewhere } else //If any other thread errored, set the winner as the first thread { Log.TraceMessage(Log.Nav.NavIn, "Other Thread did not compile.", Log.LogType.Info); string[] reversed = allGames[0].Item2.Split('_').Reverse().ToArray(); // ["cs","1","one","team"] string teamName = ""; for (int j = reversed.Length - 1; j > 1; j--) { teamName += reversed[j] + "_"; // "team_one_" } teamName = teamName.Substring(0, teamName.Length - 1); //"team_one" answers.Add(teamName); //winnerName --- May not be 0 -- its in the file somewhere answers.Add(reversed[1]); //winnerSubmissionNumber --- May not be 1 -- its in the file somewhere reversed = allGames[1].Item2.Split('_').Reverse().ToArray(); // ["cs","1","one","team"] teamName = ""; for (int j = reversed.Length - 1; j > 1; j--) { teamName += reversed[j] + "_"; // "team_one_" } teamName = teamName.Substring(0, teamName.Length - 1); //"team_one" answers.Add(teamName); //loserName --- May not be 0 -- its in the file somewhere answers.Add(reversed[1]); //loserSubmissionNumber --- May not be 1 -- its in the file somewhere } answers.Add("not created -- compilation failed"); //logURL return(answers); //return early } } Log.TraceMessage(Log.Nav.NavIn, "All threads successfully compiled.", Log.LogType.Info); //Get Final Status from GameServer API Result status = HTTP.HTTPPostGetStatus(DA_GAME, gameSession); string winReason = status.clients[0].won ? status.clients[0].reason : status.clients[1].reason; answers.Add(winReason); string loseReason = status.clients[0].lost ? status.clients[0].reason : status.clients[1].reason; answers.Add(loseReason); string winnerName = status.clients[0].won ? status.clients[0].name : status.clients[1].name; string loserName = status.clients[0].lost ? status.clients[0].name : status.clients[1].name; answers.Add(winnerName); string winnerSubmissionNumber = ""; string loserSubmissionNumber = ""; foreach (var file in files) { Log.TraceMessage(Log.Nav.NavIn, "checking file - " + file, Log.LogType.Info); if (file.Contains(winnerName)) { winnerSubmissionNumber = file.Split('_').Reverse().ToArray()[1]; //Maybe not 1 -- its in the file somewhere } if (file.Contains(loserName)) { loserSubmissionNumber = file.Split('_').Reverse().ToArray()[1]; //Maybe not 1 -- its in the file somewhere } } answers.Add(winnerSubmissionNumber); answers.Add(loserName); answers.Add(loserSubmissionNumber); string logURL = status.gamelogFilename; answers.Add(logURL); return(answers); } catch (Exception ex) { Log.TraceMessage(Log.Nav.NavIn, ex); return(new List <string>()); } }
static void RunClient() { Log.TraceMessage(Log.Nav.NavIn, "This Arena is Client.", Log.LogType.Info); Log.TraceMessage(Log.Nav.NavIn, "Clearing arena files for start client.", Log.LogType.Info); if (Directory.Exists(ARENA_FILES_PATH)) { Directory.Delete(ARENA_FILES_PATH, true); //RESET by deleting all files in the arena directories. Directory.CreateDirectory(ARENA_FILES_PATH); } Log.TraceMessage(Log.Nav.NavIn, "Starting Client FTP Server...", Log.LogType.Info); //FTP server receives files from host to build and run games. If this fails you will not receive files. StartFTPServer(false); Log.TraceMessage(Log.Nav.NavIn, "Creating the Keep-Alive Ping that let's the host know we are here and ready to run games...", Log.LogType.Info); string resultStr = ""; using (Socket resultsSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) //Create a SENDER UDP connection where we can send/receive results from games to host { using (Socket ping = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) //Create a SENDER UDP connection where we can tell the host we are available to run games. { using (UdpClient ask_for_game = new UdpClient(UDP_ASK_PORT)) //Create a RECEIVER UDP connection where we hear from the host to run games, or that our results have been recorded. { ask_for_game.Client.ReceiveTimeout = 5000; Log.TraceMessage(Log.Nav.NavIn, "Only allow 5 seconds for sending and receiving...", Log.LogType.Info); while (true) //Forever wait for a trigger from host to run games or run games. { try { IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(HOST_ADDR), UDP_CONFIRM_PORT); if (resultStr == "") //Not told to run game yet, so just send ping that we are here { Log.TraceMessage(Log.Nav.NavIn, "Sending Ping...", Log.LogType.Info); //remoteEP = new IPEndPoint(IPAddress.Parse(HOST_ADDR), UDP_CONFIRM_PORT); ping.SendTo(new byte[] { 1 }, remoteEP); // Ping -- we are still here } /*else //We finished running a game, send the host the results * { * Log.TraceMessage(Log.Nav.NavIn, "Sending Results...", Log.LogType.Info); * * remoteEP = new IPEndPoint(IPAddress.Parse(HOST_ADDR), UDP_CONFIRM_PORT); * byte[] toSendResults = Encoding.ASCII.GetBytes(resultStr); * * Log.TraceMessage(Log.Nav.NavIn, "Sending toSendResults: " + resultStr + " as bytes=" + toSendResults.Length, Log.LogType.Info); * * resultsSocket.SendTo(toSendResults, remoteEP); // Send Results * }*/ Log.TraceMessage(Log.Nav.NavIn, "Waiting for game...", Log.LogType.Info); byte[] data = ask_for_game.Receive(ref remoteEP); //Will wait for 5 seconds for data from host before returning to the top of the while loop because of try{}catch{TIMEOUTEXCEPTION} string str_data = System.Text.Encoding.Default.GetString(data); //If we do get data within 5 seconds it's first byte will either be 1 or 0 if (data != null && data.Length == 1 && data[0] == 1) //If it's a 1, our results have been recorded and we can reset/move on to the next game { Log.TraceMessage(Log.Nav.NavIn, "Host received results-clearing results.", Log.LogType.Info); resultStr = ""; Directory.Delete(ARENA_FILES_PATH, true); //RESET by deleting all files in the arena directories. Directory.CreateDirectory(ARENA_FILES_PATH); } if (data != null && data.Length == 1 && data[0] == 0) //If it's a 0, the host has finished sending us files and wants us to run them { Log.TraceMessage(Log.Nav.NavIn, "We have been told to run game--LET'S GO!", Log.LogType.Info); List <string> results = BuildAndRunGame(); //All of the hard stuff happens in this function all of the winning info is returned as a string array Log.TraceMessage(Log.Nav.NavIn, "Results returned with size" + results.Count(), Log.LogType.Info); string status = "finished"; string winReason = results[0]; string loseReason = results[1]; string winnerName = results[2]; string winnerSubmissionNumber = results[3]; string loserName = results[4]; string loserSubmissionNumber = results[5]; string logURL = "http://vis.siggame.io/?log=http://35.222.122.90:3080/gamelog/" + results[6]; Log.TraceMessage(Log.Nav.NavOut, status + " " + winReason + " " + loseReason + " " + logURL + " " + winnerName + " " + winnerSubmissionNumber + " " + loserName + " " + loserSubmissionNumber, Log.LogType.Info); HTTP.HTTPPostSendToWeb(status, winReason, loseReason, logURL, winnerName, winnerSubmissionNumber, loserName, loserSubmissionNumber); //Send Info To Webserver resultStr = winnerName + ";" + logURL; //This will be sent to Host on next while(true) loop iteration Log.TraceMessage(Log.Nav.NavIn, "End Game Sending Results...", Log.LogType.Info); remoteEP = new IPEndPoint(IPAddress.Parse(HOST_ADDR), UDP_CONFIRM_PORT); byte[] toSendResults = Encoding.ASCII.GetBytes(resultStr); Log.TraceMessage(Log.Nav.NavIn, "End Game Sending toSendResults: " + resultStr + " as bytes=" + toSendResults.Length, Log.LogType.Info); resultsSocket.SendTo(toSendResults, remoteEP); // Send Results Thread.Sleep(3000); //Wait 3 seconds for post to go through Directory.Delete(ARENA_FILES_PATH, true); Directory.CreateDirectory(ARENA_FILES_PATH); } } catch (Exception e) { Log.TraceMessage(Log.Nav.NavIn, "5 second timeout on receiving game...", Log.LogType.Info); Log.TraceMessage(Log.Nav.NavIn, "Error: " + e.Message, Log.LogType.Info); } } } } } }