using BNEBotCore;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BNEBotLauncher
{
public class BotProcess
{
public Process Proc
{
get;
private set;
}
public BotInfo Bot
{
get;
private set;
}
static BotProcess()
{
m_BotCounter = LaunchSettings.FirstBotIndex;
}
// Start a new bot using the specified .exe and command line arguments
public static BotProcess StartNew(string exePath, string strategy = "", int targetLevel = 0, params string[] args)
{
// Generate a unique name for the bot
string botName = "";
if (LaunchSettings.UniqueBots)
{
botName = DateTime.Now.ToFileTimeUtc().ToString() + (m_BotCounter++).ToString("D5") + "-" + exePath;
}
else
{
botName = (m_BotCounter++).ToString("D5") + "-" + exePath;
}
// Crate data directory for the bot
string sourceDataPath = Path.Combine(Environment.CurrentDirectory, "bot");
string destinationDataPath = Path.Combine(Path.Combine(Environment.CurrentDirectory, "bot-run-" + m_RunId.ToString()), botName);
if (Directory.Exists(destinationDataPath))
{
Directory.Delete(destinationDataPath, true);
}
// Copy bot data to its directory
IOUtils.DirCopy(sourceDataPath, destinationDataPath, false);
// Prepare the bot info
BotInfo botInfo = new BotInfo()
{
Name = botName,
State = BotState.Idle
};
// Prepare the command line
string cmdLine = string.Join(" ", args);
cmdLine += "--datapath " + "\"" + destinationDataPath + "\"";
cmdLine += " !bot.enabled";
cmdLine += " !bot.online=" + (LaunchSettings.LocalBot ? "false" : "true");
cmdLine += " !bot.strategy=" + (string.IsNullOrEmpty(strategy) ? LaunchSettings.DefaultBotStrategy : strategy);
cmdLine += " !bot.target=" + (targetLevel == 0 ? LaunchSettings.DefaultBotTargetLevel : targetLevel);
cmdLine += " !bot.user=" + botName;
// Spawn the process
Process proc = new Process();
proc.EnableRaisingEvents = true;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
// If we are running under *nix, we should use the wine executable as file name and the actual bot exe as the first argument
if (LaunchSettings.UseWine)
{
proc.StartInfo.FileName = "wine";
cmdLine = Path.Combine("bin", exePath) + " " + cmdLine;
}
else
{
proc.StartInfo.FileName = Path.Combine("bin", exePath);
}
proc.StartInfo.Arguments = cmdLine;
proc.StartInfo.WorkingDirectory = Environment.CurrentDirectory; // Let the bot execute where we've invoked the launcher from
//proc.StartInfo.RedirectStandardOutput = true;
//proc.StartInfo.RedirectStandardError = true;
BotProcess bot = new BotProcess()
{
Proc = proc,
Bot = botInfo
};
// Listen for exit and update state
proc.Exited += (o, a) =>
{
if (proc.ExitCode != 0)
{
// Check for the bot success flag file to determine if the bot has crashed during runtime or not
if (File.Exists(Path.Combine(destinationDataPath, "SUCCESS")))
{
// Normal exit
botInfo.State = BotState.Stopped;
}
else
{
// Crash
botInfo.State = BotState.Crashed;
}
}
else
{
// Normal exit
botInfo.State = BotState.Stopped;
}
};
if (proc.Start())
{
botInfo.State = BotState.Running;
// TEST: perf stats
//PerformanceCounterCategory cat = new PerformanceCounterCategory("Process");
//string[] instances = cat.GetInstanceNames();
//string instanceName = string.Empty;
//foreach (string instance in instances)
//{
// using (PerformanceCounter cnt = new PerformanceCounter("Process",
// "ID Process", instance, true))
// {
// int val = (int)cnt.RawValue;
// if (val == proc.Id)
// {
// instanceName = instance;
// break;
// }
// }
//}
//bot.m_CpuCounter = new PerformanceCounter("Process", "% Processor Time", instanceName);
//bot.m_MemCounter = new PerformanceCounter("Process", "Working Set", instanceName);
return bot;
}
else
{
// Failed to start!
throw new Exception("Failed to start bot from executable " + exePath);
}
}
// Restart the bot
public bool Restart()
{
if (Proc.Start())
{
Bot.ForceState(BotState.Running);
Bot.LaunchTimestamp = DateTime.Now;
return true;
}
else
{
return false;
}
}
// Kill the bot
public void Kill()
{
if (!Proc.HasExited)
{
Proc.Kill();
if (Bot.State == BotState.Running)
{
Bot.State = BotState.Killed;
}
}
}
// Update performance stats
public void UpdatePerfStats()
{
if (m_CpuCounter != null && m_MemCounter != null)
{
try
{
Bot.CPUUsage = m_CpuCounter.NextValue();
Bot.MemoryUsage = m_MemCounter.NextValue() / 1024 / 1024; // to MB
}
catch (Exception)
{
// Process no longer available, clear the counters
m_CpuCounter = null;
m_MemCounter = null;
}
}
}
// Unique ID of this run (will be shared by all bots spawned by this process instance)
private static Guid m_RunId = Guid.NewGuid();
// Bot counter for a single process run
private static int m_BotCounter;
// Load counters
private PerformanceCounter m_CpuCounter;
private PerformanceCounter m_MemCounter;
}
}