using BNEBotCore;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace BNEBotManager
{
public class LauncherConnection : ViewModelBase
{
public LauncherConnection(IPAddress addr, int port)
{
Bots = new ObservableCollection<BotInfo>();
SelectedBots = new ObservableCollection<BotInfo>();
SelectedBots.CollectionChanged += (s, e) =>
{
HasSelectedBots = Bots.Count > 0;
};
m_LastIncomingMessage = DateTime.Now;
RemoteEndpoint = new IPEndPoint(addr, port);
// Default name
ID = RemoteEndpoint.ToString();
m_UpdateThread = new Thread(UpdateProc);
m_UpdateThread.Start();
}
public string ID
{
get;
set;
}
public bool Connected
{
get { return m_Connected; }
private set
{
m_Connected = value;
OnPropertyChanged();
OnPropertyChanged("Disconnected");
}
}
private bool m_Connected;
public bool Disconnected
{
get { return !m_Connected; }
}
public IPEndPoint RemoteEndpoint
{
get;
private set;
}
public TcpClient Client
{
get;
private set;
}
public NetworkStream Stream
{
get;
private set;
}
public BinaryWriter Writer
{
get;
private set;
}
public BinaryReader Reader
{
get;
private set;
}
public ObservableCollection<BotInfo> Bots
{
get;
private set;
}
public ObservableCollection<BotInfo> SelectedBots
{
get;
private set;
}
public bool HasSelectedBots
{
get { return m_HasRunningBots; }
set
{
m_HasRunningBots = value;
OnPropertyChanged();
}
}
private bool m_HasRunningBots;
public int RunningBotCount
{
get { return m_RunningBotCount; }
set
{
m_RunningBotCount = value;
OnPropertyChanged();
}
}
private int m_RunningBotCount;
public int FinishedBotCount
{
get { return m_FinishedBotCount; }
set
{
m_FinishedBotCount = value;
OnPropertyChanged();
}
}
private int m_FinishedBotCount;
public int KilledBotCount
{
get { return m_KilledBotCount; }
set
{
m_KilledBotCount = value;
OnPropertyChanged();
}
}
private int m_KilledBotCount;
public int CrashedBotCount
{
get { return m_CrashedBotCount; }
set
{
m_CrashedBotCount = value;
OnPropertyChanged();
}
}
private int m_CrashedBotCount;
public int BotSpawnCount
{
get { return m_BotSpawnCount; }
set
{
m_BotSpawnCount = value;
OnPropertyChanged();
}
}
private int m_BotSpawnCount = 16;
public int BotSpawnTarget
{
get { return m_BotSpawnTarget; }
set
{
m_BotSpawnTarget = value;
OnPropertyChanged();
}
}
private int m_BotSpawnTarget = 10;
public string BotSpawnStrategy
{
get { return m_BotSpawnStrategy; }
set
{
m_BotSpawnStrategy = value;
OnPropertyChanged();
}
}
private string m_BotSpawnStrategy = "cheater";
public List<string> AvailableBotStrategies
{
get { return m_AvailableBotStrategies; }
}
private List<string> m_AvailableBotStrategies = new List<string>()
{
"slow",
"free",
"builder",
"updater",
"cheater"
};
public ICommand KillSelectedBots
{
get
{
return m_KillSelectedBots ?? (m_KillSelectedBots = new CommandHandler(() => KillBots(SelectedBots)));
}
}
private ICommand m_KillSelectedBots;
public ICommand KillAllBots
{
get
{
return m_KillAllBots ?? (m_KillAllBots = new CommandHandler(() => KillBots(Bots)));
}
}
private ICommand m_KillAllBots;
public ICommand TerminateServer
{
get
{
return m_TerminateServer ?? (m_TerminateServer = new CommandHandler(() => OnTerminateServer()));
}
}
private ICommand m_TerminateServer;
public ICommand SpawnBots
{
get
{
return m_SpawnBots ?? (m_SpawnBots = new CommandHandler(() => OnSpawnBots()));
}
}
private ICommand m_SpawnBots;
public void Close()
{
m_Exit = true;
//m_UpdateThread.Join();
m_UpdateThread.Abort();
}
public void KillBots(IList<BotInfo> bots)
{
List<string> botNames = new List<string>();
KillBotCommand cmd = new KillBotCommand()
{
BotsToKill = botNames
};
// TEST: optimization: if all bots are selected, just send an empty list
if (bots.Count != Bots.Count)
{
foreach (BotInfo bot in bots)
{
botNames.Add(bot.Name);
}
}
cmd.Finalize();
// TODO: move this to a shared location
Writer.Write((int)cmd.Type);
if (cmd.CompressedData == null)
{
Writer.Write(0);
}
else
{
Writer.Write(cmd.CompressedData.Length);
Writer.Write(cmd.CompressedData);
}
}
public void OnTerminateServer()
{
TerminateServerCommand cmd = new TerminateServerCommand();
cmd.Finalize();
// TODO: move this to a shared location
Writer.Write((int)cmd.Type);
if (cmd.CompressedData == null)
{
Writer.Write(0);
}
else
{
Writer.Write(cmd.CompressedData.Length);
Writer.Write(cmd.CompressedData);
}
}
public void OnSpawnBots()
{
SpawnBotsCommand.SpawnInfo info = new SpawnBotsCommand.SpawnInfo()
{
NumBots = BotSpawnCount,
Strategy = BotSpawnStrategy,
TargetLevel = BotSpawnTarget
};
SpawnBotsCommand cmd = new SpawnBotsCommand()
{
Info = info
};
cmd.Finalize();
// TODO: move this to a shared location
Writer.Write((int)cmd.Type);
if (cmd.CompressedData == null)
{
Writer.Write(0);
}
else
{
Writer.Write(cmd.CompressedData.Length);
Writer.Write(cmd.CompressedData);
}
}
async private void AttemptConnection()
{
Client = new TcpClient();
try
{
await Client.ConnectAsync(RemoteEndpoint.Address, RemoteEndpoint.Port);
// Success!
await Application.Current.Dispatcher.BeginInvoke(new Action(() => { Connected = true; }));
Stream = Client.GetStream();
Writer = new BinaryWriter(Stream);
Reader = new BinaryReader(Stream);
}
catch (Exception)
{
// Cannot connect yet...
}
}
private void UpdateProc()
{
while (!m_Exit)
{
if (Connected)
{
if (Client.Available > 0)
{
// We've got a message from the launcher
m_LastIncomingMessage = DateTime.Now;
NetCommandType type = (NetCommandType)Reader.ReadInt32();
int dataSize = Reader.ReadInt32();
if (dataSize > 0)
{
byte[] data = Reader.ReadBytes(dataSize);
switch (type)
{
case NetCommandType.BotInfo:
OnBotInfoCommand(data);
break;
}
}
}
else
{
// Check for disconnect
if ((DateTime.Now - m_LastIncomingMessage).TotalSeconds > 5)
{
// Let's try to send few bytes to the server. If the call fails, we know it has disconnected
try
{
Writer.Write((int)NetCommandType.Ping);
Writer.Write(0);
m_LastIncomingMessage = DateTime.Now;
}
catch (Exception)
{
// Clear data
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
Reset();
}));
}
}
}
Thread.Sleep(100);
}
else
{
// Try reconnecting
AttemptConnection();
if (!Connected)
{
Thread.Sleep(5000);
}
}
}
}
private void OnBotInfoCommand(byte[] data)
{
BotInfoCommand cmd = new BotInfoCommand()
{
CompressedData = data
};
cmd.Restore();
if (cmd.UpdatedBots != null)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
// TEST: we never remove bots, we always keep the record, unless it is replaced by a bot with the same name
foreach (BotInfo bot in cmd.UpdatedBots)
{
// Find an existing bot
BotInfo existingBot = Bots.FirstOrDefault((b) => { return b.Name == bot.Name; });
if (existingBot == null)
{
// New one
Bots.Add(bot);
}
else
{
// Update
existingBot.ForceState(bot.State);
existingBot.CPUUsage = bot.CPUUsage;
existingBot.MemoryUsage = bot.MemoryUsage;
existingBot.LaunchTimestamp = bot.LaunchTimestamp;
existingBot.Uptime = bot.Uptime;
}
}
// Update stats
int running = 0, finished = 0, crashed = 0, killed = 0;
foreach (BotInfo bot in Bots)
{
switch (bot.State)
{
case BotState.Running:
running++;
break;
case BotState.Stopped:
finished++;
break;
case BotState.Killed:
killed++;
break;
case BotState.Crashed:
crashed++;
break;
}
}
RunningBotCount = running;
FinishedBotCount = finished;
CrashedBotCount = crashed;
KilledBotCount = killed;
}));
}
}
private void Reset()
{
Bots.Clear();
RunningBotCount = FinishedBotCount = CrashedBotCount = KilledBotCount = 0;
Connected = false;
//Client = null;
Stream = null;
Writer = null;
Reader = null;
}
private Thread m_UpdateThread;
private bool m_Exit;
private DateTime m_LastIncomingMessage;
}
}