public static string SubmitSpringBattleResult(Spring.SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server)
        {
            try
            {
                if (!result.GameEndedOk) return "Game didn't end properly";
                if (result.IsCheating) return "Cheats were enabled during this game";

                var db = new ZkDataContext();
                var text = new StringBuilder();

                var sb = SaveSpringBattle(result, db);

                ProcessExtras(result.OutputExtras, sb, db);

                if (result.LobbyStartContext.Mode == AutohostMode.Planetwars) ProcessPlanetWars(result, server, sb, db, text);

                ProcessElos(result, server, db, sb);

                text.AppendLine(string.Format("BATTLE DETAILS AND REPLAY ----> {1}/Battles/Detail/{0} <-----", sb.SpringBattleID, GlobalConst.BaseSiteUrl));
                return text.ToString();
            }
            catch (Exception ex)
            {
                var data = JsonConvert.SerializeObject(result);
                Trace.TraceError($"{ex}\nData:\n{data}");
                return $"{ex}\nData:\n{data}";
            }
        }
        public void StartMission(string missionName)
        {
            var down = Program.Downloader.GetResource(DownloadType.MISSION, missionName);
            if (down == null)
            {
                //okay Mission exist, but lets check for dependency!
                down = Program.Downloader.GetDependenciesOnly(missionName);
            }

            var engine = Program.Downloader.GetResource(DownloadType.ENGINE, Program.TasClient.ServerWelcome.Engine ?? GlobalConst.DefaultEngineOverride);

            var metaWait = new EventWaitHandle(false, EventResetMode.ManualReset);
            Mod modInfo = null;
            Program.MetaData.GetModAsync(missionName,
                mod =>
                {
                    if (!mod.IsMission)
                    {
                        Program.MainWindow.InvokeFunc(() => { WarningBar.DisplayWarning(string.Format("{0} is not a valid mission", missionName)); });
                    }

                    else modInfo = mod;

                    metaWait.Set();
                },
                error =>
                {
                    Program.MainWindow.InvokeFunc(() =>
                    {
                        WarningBar.DisplayWarning(string.Format("Download of metadata failed: {0}", error.Message));
                        //container.btnStop.Enabled = true;
                    });
                    metaWait.Set();
                });

            var downloads = new List<Download>() { down, engine }.Where(x => x != null).ToList();
            if (downloads.Count > 0)
            {
                var dd = new WaitDownloadDialog(downloads);
                if (dd.ShowDialog(Program.MainWindow) == DialogResult.Cancel)
                {
                    Program.MainWindow.InvokeFunc(() => Program.NotifySection.RemoveBar(this));
                    return;
                }
            }
            metaWait.WaitOne();

            var spring = new Spring(Program.SpringPaths);
            spring.RunLocalScriptGame(modInfo.MissionScript, Program.TasClient.ServerWelcome.Engine ?? GlobalConst.DefaultEngineOverride);
            var cs = GlobalConst.GetContentService();
            cs.NotifyMissionRun(Program.Conf.LobbyPlayerName, missionName);
            spring.SpringExited += (o, args) => RecordMissionResult(spring, modInfo);
            Program.MainWindow.InvokeFunc(() => Program.NotifySection.RemoveBar(this));
        }
 /// <summary>
 /// GEnerates script for connecting to game
 /// </summary>
 /// <returns></returns>
 public static string GenerateConnectScript(Spring.SpringBattleContext context)
 {
     var sb = new StringBuilder();
     sb.AppendLine("[GAME]");
     sb.AppendLine("{");
     sb.AppendFormat("HostIP={0};\n", context.IpAddress);
     sb.AppendFormat("HostPort={0};\n", context.Port);
     sb.AppendLine("IsHost=0;");
     sb.AppendFormat("MyPlayerName={0};\n", context.MyUserName);
     sb.AppendFormat("MyPasswd={0};\n", context.MyPassword ?? context.MyPassword);
     sb.AppendLine("}");
     return sb.ToString();
 }
		public static void StartDownloadedMission(ScriptMissionData profile, string modInternalName)
		{
			var spring = new Spring(Program.SpringPaths);
			var name = Program.Conf.LobbyPlayerName;
			if (string.IsNullOrEmpty(name)) name = "Player";

			if (Utils.VerifySpringInstalled())
			{
				spring.RunLocalScriptGame(profile.StartScript.Replace("%MOD%", modInternalName).Replace("%MAP%", profile.MapName).Replace("%NAME%", name));
                var serv = GlobalConst.GetContentService();
				serv.NotifyMissionRun(Program.Conf.LobbyPlayerName, profile.Name);
			}
		}
 public ChatToSpeech(Spring spring)
 {
     try
     {
         speechSynthesizer = new SpeechSynthesizer();
         voices = speechSynthesizer.GetInstalledVoices();
         spring.LogLineAdded += spring_LogLineAdded;
     }
     catch (Exception ex)
     {
         Trace.TraceError("voice synthetizer failed: {0}", ex);
     }
 }
        private static void ProcessElos(Spring.SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, ZkDataContext db, SpringBattle sb)
        {
            bool noElo = result.OutputExtras.Any(x => x?.StartsWith("noElo", true, System.Globalization.CultureInfo.CurrentCulture) == true);

            Dictionary<int, int> orgLevels = sb.SpringBattlePlayers.Select(x => x.Account).ToDictionary(x => x.AccountID, x => x.Level);

            sb.CalculateAllElo(noElo);
            foreach (var u in sb.SpringBattlePlayers.Where(x => !x.IsSpectator)) u.Account.CheckLevelUp();

            db.SaveChanges();

            try
            {
                foreach (Account a in sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account)) server.PublishAccountUpdate(a);
            }
            catch (Exception ex)
            {
                Trace.TraceError("error updating extension data: {0}", ex);
            }

            foreach (Account account in sb.SpringBattlePlayers.Select(x => x.Account))
            {
                if (account.Level > orgLevels[account.AccountID])
                {
                    try
                    {
                        string message = string.Format("Congratulations {0}! You just leveled up to level {1}. {3}/Users/Detail/{2}",
                            account.Name,
                            account.Level,
                            account.AccountID,
                            GlobalConst.BaseSiteUrl);
                        //text.AppendLine(message);
                        server.GhostPm(account.Name, message);
                    }
                    catch (Exception ex)
                    {
                        Trace.TraceError("Error sending level up lobby message: {0}", ex);
                    }
                }
            }
        }
        /// <summary>
        /// Generates script for hosting a game
        /// </summary>
        public static string GenerateHostScript(Spring.SpringBattleContext context, int loopbackListenPort)
        {
            var previousCulture = Thread.CurrentThread.CurrentCulture;
            try {
                Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

                var script = new StringBuilder();

                script.AppendLine("[GAME]");
                script.AppendLine("{");

                script.AppendFormat("  ZkSearchTag={0};\n", Guid.NewGuid());
                script.AppendFormat("  Mapname={0};\n", context.LobbyStartContext.Map);

                script.AppendFormat("  StartPosType={0};\n", context.LobbyStartContext.IsMission ? 3 : 2);

                script.AppendFormat("  GameType={0};\n", context.LobbyStartContext.Mod);
                script.AppendFormat("  ModHash=1;\n");
                script.AppendFormat("  MapHash=1;\n");

                script.AppendFormat("  AutohostPort={0};\n", loopbackListenPort);
                script.AppendLine();
                script.AppendFormat("  HostIP={0};\n", context.IpAddress);
                script.AppendFormat("  HostPort={0};\n", context.Port);
                //script.AppendFormat("  SourcePort={0};\n", 8300);
                script.AppendFormat("  IsHost=1;\n");
                script.AppendLine();

                if (!string.IsNullOrEmpty(context.MyUserName)) script.AppendFormat("  MyPlayerName={0};\n", context.MyUserName);
                if (!string.IsNullOrEmpty(context.MyPassword) || !string.IsNullOrEmpty(context.MyUserName)) script.AppendFormat("  MyPasswd={0};\n", context.MyPassword??context.MyUserName);

                GeneratePlayerSection(script, context);

                return script.ToString();
            } finally {
                Thread.CurrentThread.CurrentCulture = previousCulture;
            }
        }
        private void MissionBar_Load(object sender, EventArgs e)
        {
            label1.Text = string.Format("Starting mission {0} - please wait", missionName);

            var down = Program.Downloader.GetResource(DownloadType.MOD, missionName);
            if (down==null)
            {   //okay Mission exist, but lets check for dependency!
                down = Program.Downloader.GetDependenciesOnly(missionName);
            }

            var engine = Program.Downloader.GetAndSwitchEngine(Program.SpringPaths.SpringVersion);

            ZkData.Utils.StartAsync(() =>
            {
                var metaWait = new EventWaitHandle(false, EventResetMode.ManualReset);
                Mod modInfo = null;
                Program.SpringScanner.MetaData.GetModAsync(missionName,
                                                           mod =>
                                                           {
                                                               if (!mod.IsMission)
                                                               {
                                                                   Program.MainWindow.InvokeFunc(() =>
                                                                   {
                                                                       label1.Text = string.Format("{0} is not a valid mission", missionName);
                                                                       container.btnStop.Enabled = true;
                                                                   });
                                                               }

                                                               else modInfo = mod;

                                                               metaWait.Set();
                                                           },
                                                           error =>
                                                           {
                                                               Program.MainWindow.InvokeFunc(() =>
                                                               {
                                                                   label1.Text = string.Format("Download of metadata failed: {0}", error.Message);
                                                                   container.btnStop.Enabled = true;
                                                               });
                                                               metaWait.Set();
                                                           });
                //if (down != null) WaitHandle.WaitAll(new WaitHandle[] { down.WaitHandle, metaWait });
                //else metaWait.WaitOne();
                
                var waitHandles = new List<EventWaitHandle>();
                
                waitHandles.Add(metaWait);
                if (down != null)  waitHandles.Add(down.WaitHandle);
                if (engine != null) waitHandles.Add(engine.WaitHandle);
                
                if (waitHandles.Any()) WaitHandle.WaitAll(waitHandles.ToArray());

                if ((down != null && down.IsComplete == false) || (engine != null && engine.IsComplete == false) || modInfo==null)
                {
                    Program.MainWindow.InvokeFunc(() =>
                    {
                        label1.Text = string.Format("Download of {0} failed", missionName);

                        container.btnStop.Enabled = true;
                    });
                }

                if (modInfo != null && (down == null || down.IsComplete == true) && (engine == null || engine.IsComplete == true))
                {
                    if (Utils.VerifySpringInstalled())
                    {
                        var spring = new Spring(Program.SpringPaths);
                        spring.StartGame(Program.TasClient,
                                         null,
                                         null,
                                         modInfo.MissionScript, Program.Conf.UseSafeMode);

                        var cs = GlobalConst.GetContentService();
                        cs.NotifyMissionRun(Program.Conf.LobbyPlayerName, missionName);
                    }
                    Program.MainWindow.InvokeFunc(() => Program.NotifySection.RemoveBar(this));
                }
            });
        }
        /// <summary>
        /// singleton, dont use, internal for designer
        /// </summary>
        internal BattleBar()
        {
            InitializeComponent();

            Program.ToolTip.SetText(btnLeave, "Leave this battle");

            picoChat.ChatBackgroundColor = TextColor.background; //same color as Program.Conf.BgColor
            picoChat.IRCForeColor = 14; //mirc grey. Unknown use
            picoChat.DefaultTooltip = "Last lines from room chat, click to enter full screen chat";

            gameBox.BackColor = Color.Transparent;
            btnStart.Image = Buttons.fight.GetResizedWithCache(38, 38);
            btnStart.ImageAlign = ContentAlignment.MiddleCenter;
            btnStart.TextImageRelation = TextImageRelation.ImageAboveText;
            btnStart.Text = "Play";

            btnLeave.Image = Buttons.exit.GetResizedWithCache(38, 38);
            btnLeave.ImageAlign = ContentAlignment.MiddleCenter;
            btnLeave.TextImageRelation = TextImageRelation.ImageAboveText;
            btnLeave.Text = "Leave";

            Program.ToolTip?.SetText(btnStart, "Start battle");
            Program.ToolTip?.SetText(btnLeave, "Quit battle");

            picoChat.Visible = false;


            btnStart.Click += btnStart_Click;
            btnLeave.Click += BtnLeaveClick;

            client = Program.TasClient;
            spring = new Spring(Program.SpringPaths);


            try
            {
                // silly way to create speech and voice engines on runtime - needed due to mono crash
                speech = Activator.CreateInstance(Type.GetType("ZeroKLobby.ChatToSpeech"), spring);
            }
            catch (Exception ex)
            {
                Trace.TraceWarning("Failed to init VoiceCommands:{0}", ex.Message);
            }

            spring.SpringExited += (s, e) =>
                {
                    client.ChangeMyUserStatus(isInGame: false);

                    if (e.IsCrash)
                    {
                        Program.MainWindow.InvokeFunc(() =>
                            {
                                var defaultButton = MessageBoxDefaultButton.Button2;
                                var icon = MessageBoxIcon.None;
                                if (
                                    MessageBox.Show(this, "Do you want me to set Low details?\n(will effect: lups.cfg and springsettings.cfg)\n\nIf you wish to file a bug report, please include a copy of infolog.txt in your game data folder (accessible through Settings).\nUpload it to a text sharing site such as pastebin.com.",
                                                    "Spring engine has crashed, update your video and audio drivers please!",
                                                    MessageBoxButtons.YesNo,
                                                    icon,
                                                    defaultButton) == DialogResult.Yes)
                                {
                                    Program.Conf.UseSafeMode = true;
                                    Program.EngineConfigurator.Configure(true, 0);
                                }
                            });
                    }
                };

            spring.SpringStarted += (s, e) =>
            {
                Program.MainWindow.SwitchMusicOnOff(false);
                client.ChangeMyUserStatus(isInGame: true);
            };

            client.Rang += (s, e) =>
                {
                    if (e.Place == SayPlace.Channel)
                        MainWindow.Instance.NotifyUser($"chat/{e.Target}", $"Attention needed in {e.Target} channel", true, true);
                    else 
                    {
                        MainWindow.Instance.NotifyUser("chat/battle", "Someone demands your attention in battle room!", true, true);
                        AutoRespond();
                    }
                };



            client.BattleJoined += (s, e) =>
                {
                    if (!isVisible) ManualBattleStarted();
                    if (IsHostGameRunning()) btnStart.Text = "Rejoin";
                    else btnStart.Text = "Start";


                    //client.ChangeMyUserStatus(false, false);
                    var battle = client.MyBattle;
                    lastBattleFounder = battle.FounderName;

                    //Title = string.Format("Joined battle room hosted by {0}", battle.Founder.Name);
                    //TitleTooltip = "Use button on the left side to start a game";
                    radioPlay.Visible = true;
                    radioSpec.Visible = true;
                    btnStart.Visible = true;

                    Program.Downloader.GetResource(DownloadType.MAP, battle.MapName);
                    Program.Downloader.GetResource(DownloadType.MOD, battle.ModName);
                    Program.Downloader.GetResource(DownloadType.ENGINE, battle.EngineVersion);

                    if (gameBox.Image != null) gameBox.Image.Dispose();
                    CreateBattleIcon(Program.BattleIconManager.GetBattleIcon(battle.BattleID));

                    RefreshTooltip();

                    if (Program.TasClient.MyBattle != null) NavigationControl.Instance.Path = "chat/battle";

                    client.ChangeMyBattleStatus(desiredSpectatorState, HasAllResources() ? SyncStatuses.Synced : SyncStatuses.Unsynced, 0);
                };


            client.MyBattleMapChanged += (s, e) =>
                {
                    if (client.MyBattle != null && !Program.SpringScanner.HasResource(client.MyBattle.MapName))
                    {
                        client.ChangeMyBattleStatus(syncStatus: SyncStatuses.Unsynced);
                        Program.Downloader.GetResource(DownloadType.MAP, client.MyBattle.MapName);
                    }
                    RefreshTooltip();
                };

            client.MyBattleHostExited += (s, e) => { btnStart.Text = "Start"; };

            client.ConnectSpringReceived += (s, e) =>
            {
                try
                {
                    btnStart.Text = "Rejoin";

                    List<Download> downloads = new List<Download>();
                    downloads.Add(Program.Downloader.GetResource(DownloadType.ENGINE, e.Engine));
                    downloads.Add(Program.Downloader.GetResource(DownloadType.MOD, e.Game));
                    downloads.Add(Program.Downloader.GetResource(DownloadType.MAP, e.Map));
                    downloads = downloads.Where(x => x != null).ToList();
                    if (downloads.Count > 0)
                    {
                        var dd = new WaitDownloadDialog(downloads);
                        if (dd.ShowDialog(Program.MainWindow) == DialogResult.Cancel) return;
                    }
                    
                    if (spring.IsRunning) spring.ExitGame();
                    spring.ConnectGame(e.Ip, e.Port, client.UserName, e.ScriptPassword, e.Engine);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(this, "Error starting spring: " + ex.Message);
                }
                RefreshTooltip();
            };

            client.BattleMyUserStatusChanged += (s, e) =>
                {
                    if (client.MyBattleStatus != null)
                    {
                        //btnStart.Enabled = client.MyBattleStatus.SyncStatus == SyncStatuses.Synced;

                        if (client.MyBattleStatus.IsSpectator && radioPlay.Checked) ChangeGuiSpectatorWithoutEvent(false); // i was spectated
                        if (!client.MyBattleStatus.IsSpectator && radioSpec.Checked) ChangeGuiSpectatorWithoutEvent(true); //i was unspectated
                    }
                };

            client.BattleClosed += (s, e) =>
                {
                    btnStart.Text = "Start";
                    if (gameBox.Image != null) gameBox.Image.Dispose();
                    gameBox.Image = null;
                    RefreshTooltip();
                    Stop();
                };

            client.MyBattleRemoved += (s, e) =>
                {
                    var t = new Timer();
                    var tryCount = 0;
                    t.Interval = 1000;
                    t.Tick += (s2, e2) =>
                        {
                            tryCount++;
                            if (tryCount > 15)
                            {
                                t.Stop();
                                t.Dispose();
                            }
                            else if (client.IsLoggedIn && client.MyBattle == null)
                            {
                                var bat = client.ExistingBattles.Values.FirstOrDefault(x => x.FounderName == lastBattleFounder && !x.IsPassworded);
                                if (bat != null)
                                {
                                    ActionHandler.JoinBattle(bat.BattleID, null);
                                    t.Stop();
                                    t.Dispose();
                                }
                            }
                        };
                    t.Start();
                };

            client.ConnectionLost += (s, e) =>
                {
                    if (gameBox.Image != null) gameBox.Image.Dispose();
                    gameBox.Image = null;
                    RefreshTooltip();
                    Stop();
                };



            timer.Tick += (s, e) =>
                {
                    if (client.IsLoggedIn)
                    {
                        if (WindowsApi.IdleTime.TotalMinutes > Program.Conf.IdleTime)
                        {
                            if (!client.MyUser.IsAway) client.ChangeMyUserStatus(isAway: true);
                        }
                        else
                        {
                            if (client.MyUser.IsAway) client.ChangeMyUserStatus(isAway: false);
                        }
                        CheckMyBattle();
                    }
                };


            Program.MainWindow.navigationControl.PageChanged += s =>
            {
                if (s != "chat/battle")
                {
                    picoChat.Visible = true;
                }
                else picoChat.Visible = false;
            };

            timer.Interval = 1000;
            timer.Start();

            Program.BattleIconManager.BattleChanged += BattleIconManager_BattleChanged;

            //picoChat.Font = new Font(Program.Conf.ChatFont.FontFamily, Program.Conf.ChatFont.Size*0.8f);
            picoChat.ShowHistory = false;
            picoChat.ShowJoinLeave = false;
            //picoChat.HideScroll = true;

            BattleChatControl.BattleLine += (s, e) => picoChat.AddLine(e.Data);

            picoChat.MouseClick += (s, e) => NavigationControl.Instance.Path = "chat/battle";
        }
 internal static int FilterUsers(string[] words, TasClient tas, Spring spring, out string[] vals, out int[] indexes)
 {
     var b = tas.MyBattle;
     var i = 0;
     var temp = b.Users.Values.Select(u => u.Name).ToList();
     if (spring.IsRunning) foreach (var u in spring.StartContext.Players)
         {
             if (!temp.Contains(u.Name)) temp.Add(u.Name);
         }
     return Filter(temp.ToArray(), words, out vals, out indexes);
 }
 public VoteSplitPlayers(TasClient tas, Spring spring, AutoHost ah): base(tas, spring, ah) {}
 public VoteResign(TasClient tas, Spring spring, AutoHost ah): base(tas, spring, ah) {}
 public static void SayBattle(TasClient tas, Spring spring, string text, bool ingame) {
     tas.Say(SayPlace.Battle, "", text, true);
     if (spring.IsRunning && ingame) spring.SayGame(text);
 }
 public VoteMove(TasClient tas, Spring spring, AutoHost ah): base(tas, spring, ah) {}
        private static void ProcessPlanetWars(Spring.SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, SpringBattle sb, ZkDataContext db, StringBuilder text)
        {
            if (result.LobbyStartContext.Mode != AutohostMode.Planetwars || sb.PlayerCount < 2 || sb.Duration >= GlobalConst.MinDurationForPlanetwars) return;

            List<int> winnerTeams = sb.SpringBattlePlayers.Where(x => x.IsInVictoryTeam && !x.IsSpectator).Select(x => x.AllyNumber).Distinct().ToList();
            int? winNum = null;
            if (winnerTeams.Count == 1)
            {
                winNum = winnerTeams[0];
                if (winNum > 1) winNum = null;
            }

            PlanetWarsTurnHandler.EndTurn(result.LobbyStartContext.Map,
                result.OutputExtras,
                db,
                winNum,
                sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account).ToList(),
                text,
                sb,
                sb.SpringBattlePlayers.Where(x => !x.IsSpectator && x.AllyNumber == 0).Select(x => x.Account).ToList(),
                server.PlanetWarsEventCreator);

            // TODO HACK Global.PlanetWarsMatchMaker.RemoveFromRunningBattles(context.AutohostName);
        }
        public static void JoinBattleSpec(int battleId)
        {
            Battle bat;
            if (!Program.TasClient.ExistingBattles.TryGetValue(battleId, out bat)) return;
            Program.TasClient.Say(SayPlace.User, bat.Founder.Name, string.Format("!adduser {0}", Program.TasClient.UserName),false);

            var de = Program.Downloader.GetAndSwitchEngine(bat.EngineVersion);
            var dm = Program.Downloader.GetResource(DownloadType.MAP, bat.MapName);
            var dg = Program.Downloader.GetResource(DownloadType.MOD, bat.ModName);
        
            ZkData.Utils.StartAsync(() =>
            {
                if (de != null)
                {
                    de.WaitHandle.WaitOne();
                    if (de.IsComplete == false) return;
                }
                if (dm != null)
                {
                    dm.WaitHandle.WaitOne();
                    if (dm.IsComplete == false) return;
                }
                if (dg != null)
                {
                    dg.WaitHandle.WaitOne();
                    if (dg.IsComplete == false) return;
                }


                var spring = new Spring(Program.SpringPaths);
                
                Thread.Sleep(200);// give host time to adduser
                spring.StartGame(Program.TasClient, null, null, null, Program.Conf.UseSafeMode, battleOverride: bat);
            });
           


        }
 public VoteSetOptions(TasClient tas, Spring spring, AutoHost ah): base(tas, spring, ah) {}
        public void StartScriptMission(string missionName)
        {
            var serv = GlobalConst.GetContentService();
            var profile = serv.GetScriptMissionData(missionName);
            var downloads = new List<Download>();
            downloads.Add(Program.Downloader.GetResource(DownloadType.RAPID, profile.ModName));
            downloads.Add(Program.Downloader.GetResource(DownloadType.MAP, profile.MapName));
            downloads.Add(Program.Downloader.GetResource(DownloadType.ENGINE, Program.TasClient.ServerWelcome.Engine ?? GlobalConst.DefaultEngineOverride));
            if (profile.ManualDependencies != null) foreach (var entry in profile.ManualDependencies) if (!string.IsNullOrEmpty(entry)) downloads.Add(Program.Downloader.GetResource(DownloadType.NOTKNOWN, entry));

            downloads = downloads.Where(x => x != null).ToList();

            if (downloads.Count > 0)
            {
                var dd = new WaitDownloadDialog(downloads);
                if (dd.ShowDialog(Program.MainWindow) == DialogResult.Cancel) return;
            }

            var spring = new Spring(Program.SpringPaths);
            var name = Program.Conf.LobbyPlayerName;
            if (string.IsNullOrEmpty(name)) name = "Player";

            spring.RunLocalScriptGame(
                profile.StartScript.Replace("%MOD%", profile.ModName).Replace("%MAP%", profile.MapName).Replace("%NAME%", name),
                Program.TasClient.ServerWelcome.Engine ?? GlobalConst.DefaultEngineOverride);
            serv.NotifyMissionRun(name, profile.Name);
        }
        //[STAThread]
        private static void Main(params string[] args) {
            var startupPath = Path.GetDirectoryName(Path.GetFullPath(Application.ExecutablePath));
            var springPaths = new SpringPaths(null, startupPath);
            Spring runningSpring = null;
            springPaths.MakeFolders();
            TcpTransport connection = null;

            // speed up spring start
            springPaths.SpringVersionChanged += (sender, eventArgs) =>
            {
                Utils.StartAsync(
                    () =>
                    {
                        UnitSync unitSync = null;
                        try
                        {
                            unitSync = new UnitSync(springPaths); // initialize unitsync to avoid slowdowns when starting

                            if (unitSync.UnitsyncWritableFolder != springPaths.WritableDirectory)
                            {
                                // unitsync created its cache in different folder than is used to start spring -> move it
                                var fi = ArchiveCache.GetCacheFile(unitSync.UnitsyncWritableFolder);
                                if (fi != null) File.Copy(fi.FullName, Path.Combine(springPaths.WritableDirectory, "cache", fi.Name), true);
                            }
                        }
                        finally
                        {
                            unitSync?.Dispose();
                        }
                    });
            };

            Config config = null;
            try { config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(startupPath + "/config.json")); } catch (Exception) { }

            CefWrapper.Initialize(startupPath + "/render", args);


            var springScanner = new SpringScanner(springPaths);
            springScanner.Start();

            EventHandler<ProgressEventArgs> workHandler =
                (s, e) => { CefWrapper.ExecuteJavascript("on_spring_scanner_work(" + JsonConvert.SerializeObject(e) + ");"); };
            springScanner.WorkStarted += workHandler;
            springScanner.WorkProgressChanged += workHandler;
            springScanner.WorkStopped += (s, e) => { CefWrapper.ExecuteJavascript("on_spring_scanner_work(null);"); };
            springScanner.LocalResourceAdded +=
                (s, e) => { CefWrapper.ExecuteJavascript("on_spring_scanner_add(" + JsonConvert.SerializeObject(e.Item) + ")"); };
            springScanner.LocalResourceRemoved +=
                (s, e) => { CefWrapper.ExecuteJavascript("on_spring_scanner_remove(" + JsonConvert.SerializeObject(e.Item) + ")"); };


            var downloader = new PlasmaDownloader.PlasmaDownloader(springScanner, springPaths); //rapid
            downloader.GetAndSwitchEngine(GlobalConst.DefaultEngineOverride);

            // ZKL's downloader doesn't send events to monitor download progress, so we have to poll it.
            Timer pollDownloads = new Timer();
            pollDownloads.Interval = 250;
            pollDownloads.Tick += (s, e) => {
                CefWrapper.ExecuteJavascript("on_downloads_change(" + JsonConvert.SerializeObject(downloader.Downloads) + ")");
            };
            // Through some WinAPI dark magic it manages to use the message pump in the window that is run by CEF.
            // Can this be dangerous?
            pollDownloads.Start();


            CefWrapper.RegisterApiFunction(
                "getEngines",
                () => {
                    return new List<string> { "100.0" }; // TODO: stub
                });
            CefWrapper.RegisterApiFunction("getMods", () => { return springScanner.GetAllModResource(); });
            CefWrapper.RegisterApiFunction("getMaps", () => { return springScanner.GetAllMapResource(); });

            CefWrapper.RegisterApiFunction(
                "downloadEngine",
                (string engine) =>
                {
                    // Don't let GetAndSwitchEngine() touch the main SpringPaths.
                    var path = new SpringPaths(springPaths.GetEngineFolderByVersion(engine), springPaths.WritableDirectory);
                    downloader.GetAndSwitchEngine(engine, path);
                });
            CefWrapper.RegisterApiFunction("downloadMod", (string game) => { downloader.GetResource(DownloadType.MOD, game); });
            CefWrapper.RegisterApiFunction("downloadMap", (string map) => { downloader.GetResource(DownloadType.MAP, map); });
            CefWrapper.RegisterApiFunction(
                "abortDownload",
                (string name) =>
                {
                    downloader.Downloads.FirstOrDefault(d => d.Name == name)?.Abort();
                });

            CefWrapper.RegisterApiFunction(
                "startSpringScript",
                (string engineVer, string script) =>
                {
                    if (runningSpring != null) return null;
                    // Ultimately we should get rid of the concept of a "current set engine", but for now let's work around it.
                    var path = new SpringPaths(springPaths.GetEngineFolderByVersion(engineVer), springPaths.WritableDirectory);
                    runningSpring = new Spring(path);
                    runningSpring.SpringExited += (obj, evt) =>
                    {
                        CefWrapper.ExecuteJavascript("on_spring_exit(" + (evt.Data ? "true" : "false") + ");");
                        runningSpring = null;
                    };
                    try
                    {
                        runningSpring.StartSpring(script);
                        return null;
                    }
                    catch (Exception e)
                    {
                        runningSpring = null;
                        return e.Message;
                    }
                });

            CefWrapper.RegisterApiFunction(
                "connect",
                (string host, int port) =>
                {
                    if (connection != null) connection.RequestClose();
                    connection = new TcpTransport(host, port);
                    connection.ConnectAndRun(
                        async (s) => CefWrapper.ExecuteJavascript(
                            $"on_lobby_message({CefWrapper.mangleUtf8(JsonConvert.SerializeObject(s))})"),
                        async () => { },
                        async (requested) => CefWrapper.ExecuteJavascript(
                            $"on_connection_closed({CefWrapper.mangleUtf8(JsonConvert.SerializeObject(requested))})")
                        );
                });
            CefWrapper.RegisterApiFunction("disconnect", () => connection?.RequestClose());
            CefWrapper.RegisterApiFunction("sendLobbyMessage", (string msg) => connection?.SendLine(CefWrapper.unmangleUtf8(msg) + '\n'));

            CefWrapper.RegisterApiFunction(
                "readConfig",
                () =>
                {
                    try { return JsonConvert.DeserializeObject(File.ReadAllText(startupPath + "/config.json")); }
                    catch(FileNotFoundException) { return null; }
                });
            CefWrapper.RegisterApiFunction("saveConfig", (object conf) => File.WriteAllText(startupPath + "/config.json",
                JsonConvert.SerializeObject(conf, Formatting.Indented)));

            CefWrapper.RegisterApiFunction("setFullscreen", (bool fullscreen) => CefWrapper.SetFullscreen(fullscreen));

            var fileUrl = new Uri(startupPath + "/zkwl/index.html");
            CefWrapper.StartMessageLoop(fileUrl.AbsoluteUri, "black", !config?.lobbyWindowed ?? true);
            CefWrapper.Deinitialize();

            downloader.Dispose();
            springScanner.Dispose();
        }
 private static void RecordMissionResult(Spring spring, Mod modInfo)
 {
     if (spring.Context.GameEndedOk && !spring.Context.IsCheating)
     {
         Trace.TraceInformation("Submitting score for mission " + modInfo.Name);
         try
         {
             var service = GlobalConst.GetContentService();
             Task.Factory.StartNew(() =>
             {
                 try
                 {
                     service.SubmitMissionScore(Program.Conf.LobbyPlayerName,
                         ZkData.Utils.HashLobbyPassword(Program.Conf.LobbyPlayerPassword),
                         modInfo.Name,
                         spring.Context.MissionScore ?? 0,
                         spring.Context.MissionFrame/30,
                         spring.Context.MissionVars);
                 }
                 catch (Exception ex)
                 {
                     Trace.TraceError("Error sending score: {0}", ex);
                 }
             });
         }
         catch (Exception ex)
         {
             Trace.TraceError($"Error sending mission score: {ex}");
         }
     }
 }
 public AbstractPoll(TasClient tas, Spring spring, AutoHost ah) {
     this.tas = tas;
     this.spring = spring;
     this.ah = ah;
 }
 void spring_SpringExited(object sender, Spring.SpringBattleContext springBattleContext)
 {
     if (speechSynthesizer!=null) speechSynthesizer.SpeakAsyncCancelAll();
 }
        /// <summary>
        /// singleton, dont use, internal for designer
        /// </summary>
        internal BattleBar()
        {
            InitializeComponent();

            picoChat.ChatBackgroundColor = TextColor.background; //same color as Program.Conf.BgColor
            picoChat.IRCForeColor = 14; //mirc grey. Unknown use

            picoChat.DefaultTooltip = "Last lines from room chat, click to enter full screen chat";

            client = Program.TasClient;
            spring = new Spring(Program.SpringPaths);


            try
            {
                // silly way to create speech and voice engines on runtime - needed due to mono crash
                speech = Activator.CreateInstance(Type.GetType("ZeroKLobby.ChatToSpeech"), spring);
            }
            catch (Exception ex)
            {
                Trace.TraceWarning("Failed to init VoiceCommands:{0}", ex.Message);
            }

            spring.SpringExited += (s, e) =>
                {
                    client.ChangeMyUserStatus(isInGame: false);

                    if (e.Data)
                    {
                        Program.MainWindow.InvokeFunc(() =>
                            {
                                var defaultButton = MessageBoxDefaultButton.Button2;
                                var icon = MessageBoxIcon.None;
                                if (
                                    MessageBox.Show("Do you want me to set Low details?\n(will effect: lups.cfg and springsettings.cfg)\n\nIf you wish to file a bug report, please include a copy of infolog.txt in your game data folder (accessible through Settings).\nUpload it to a text sharing site such as pastebin.com.",
                                                    "Spring engine has crashed, update your video and audio drivers please!",
                                                    MessageBoxButtons.YesNo,
                                                    icon,
                                                    defaultButton) == DialogResult.Yes)
                                {
                                    Program.Conf.UseSafeMode = true;
                                    Program.EngineConfigurator.Configure(true, 0);
                                }
                            });
                    }
                };

            spring.SpringStarted += (s, e) =>
            {
                Program.MainWindow.SwitchMusicOnOff(false);
                client.ChangeMyUserStatus(isInGame: true);
            };

            client.Rang += (s, e) =>
                {
                    if (e.User == GlobalConst.NightwatchName)
                        //Nightwatch RING is from UserController.cs (website code)
                        MainWindow.Instance.NotifyUser("chat/zkadmin", "New report arrive at zkadmin channel", true, true);
                    else
                    {
                        MainWindow.Instance.NotifyUser("chat/battle", "Someone demands your attention in battle room!", true, true);
                        AutoRespond();
                    }
                };

            client.BattleJoined += (s, e) =>
                {
                    if (!isVisible) ManualBattleStarted();
                    if (IsHostGameRunning()) barContainer.btnDetail.Text = "Rejoin";
                    else barContainer.btnDetail.Text = "Start";
                    //client.ChangeMyUserStatus(false, false);
                    var battle = client.MyBattle;
                    lastBattleFounder = battle.Founder.Name;

                    if (battle.Founder.Name.StartsWith("PlanetWars") || battle.Founder.Name.StartsWith("Zk")) ChangeDesiredSpectatorState(false); // TODO pw unpsec hack, remove later

                    if (battle.IsQueue)
                    {
                        barContainer.Title = string.Format("Joined {0} Quick Match Queue", battle.QueueName);
                        barContainer.TitleTooltip = "Please await people, game will start automatically";
                        lbQueue.Visible = true;
                        radioPlay.Visible = false;
                        radioSpec.Visible = false;
                        barContainer.btnDetail.Visible = false;
                    }
                    else
                    {
                        barContainer.Title = string.Format("Joined battle room hosted by {0}", battle.Founder.Name);
                        barContainer.TitleTooltip = "Use button on the left side to start a game";
                        lbQueue.Visible = false;
                        radioPlay.Visible = true;
                        radioSpec.Visible = true;
                        barContainer.btnDetail.Visible = true;
                    }

                    Program.Downloader.GetResource(DownloadType.MAP, battle.MapName);
                    Program.Downloader.GetResource(DownloadType.MOD, battle.ModName);
                    engineVersionNeeded = battle.EngineVersion;
                    if (engineVersionNeeded != Program.SpringPaths.SpringVersion) Program.Downloader.GetAndSwitchEngine(engineVersionNeeded);
                    else engineVersionNeeded = null;

                    if (gameBox.Image != null) gameBox.Image.Dispose();
                    DpiMeasurement.DpiXYMeasurement(this);
                    int scaledIconHeight = DpiMeasurement.ScaleValueY(BattleIcon.Height);
                    int scaledIconWidth = DpiMeasurement.ScaleValueX(BattleIcon.Width);
                    gameBox.Image = new Bitmap(scaledIconWidth, scaledIconHeight);
                    using (var g = Graphics.FromImage(gameBox.Image))
                    {
                        g.FillRectangle(Brushes.White, 0, 0, scaledIconWidth, scaledIconHeight);
                        var bi = Program.BattleIconManager.GetBattleIcon(battle.BattleID);
                        g.DrawImageUnscaled(bi.Image, 0, 0);
                    }
                    gameBox.Invalidate();

                    RefreshTooltip();


                    var team = battle.GetFreeTeamID(client.UserName);

                    client.ChangeMyBattleStatus(desiredSpectatorState, HasAllResources() ? SyncStatuses.Synced : SyncStatuses.Unsynced, 0, team);
                };


            client.MyBattleMapChanged += (s, e) =>
                {
                    if (client.MyBattle != null && !Program.SpringScanner.HasResource(client.MyBattle.MapName))
                    {
                        client.ChangeMyBattleStatus(syncStatus: SyncStatuses.Unsynced);
                        Program.Downloader.GetResource(DownloadType.MAP, client.MyBattle.MapName);
                    }
                    RefreshTooltip();
                };

            client.MyBattleHostExited += (s, e) => { barContainer.btnDetail.Text = "Start"; };

            client.MyBattleStarted += (s, e) =>
                {
                    try
                    {
                        barContainer.btnDetail.Text = "Rejoin";
                        if (client.MyBattleStatus.SyncStatus == SyncStatuses.Synced)
                        {
                            if (Utils.VerifySpringInstalled())
                            {
                                if (spring.IsRunning) spring.ExitGame();
                                lastScript = spring.ConnectGame(client.MyBattle.Ip, client.MyBattle.HostPort, client.UserName,
                                    client.MyBattle.Users[client.UserName].ScriptPassword); //use MT tag when in spectator slot
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Error starting spring: " + ex.Message);
                    }
                    RefreshTooltip();
                };

            client.BattleMyUserStatusChanged += (s, e) =>
                {
                    if (client.MyBattleStatus != null)
                    {
                        barContainer.btnDetail.Enabled = client.MyBattleStatus.SyncStatus == SyncStatuses.Synced;

                        if (client.MyBattleStatus.IsSpectator && radioPlay.Checked) ChangeGuiSpectatorWithoutEvent(false); // i was spectated
                        if (!client.MyBattleStatus.IsSpectator && radioSpec.Checked) ChangeGuiSpectatorWithoutEvent(true); //i was unspectated
                    }
                };

            client.BattleClosed += (s, e) =>
                {
                    barContainer.btnDetail.Text = "Start";
                    if (gameBox.Image != null) gameBox.Image.Dispose();
                    gameBox.Image = null;
                    RefreshTooltip();
                    Stop();
                };

            client.MyBattleRemoved += (s, e) =>
                {
                    var t = new Timer();
                    var tryCount = 0;
                    t.Interval = 1000;
                    t.Tick += (s2, e2) =>
                        {
                            tryCount++;
                            if (tryCount > 15)
                            {
                                t.Stop();
                                t.Dispose();
                            }
                            else if (client.IsLoggedIn && client.MyBattle == null)
                            {
                                var bat = client.ExistingBattles.Values.FirstOrDefault(x => x.Founder.Name == lastBattleFounder && !x.IsPassworded);
                                if (bat != null)
                                {
                                    ActionHandler.JoinBattle(bat.BattleID, null);
                                    t.Stop();
                                    t.Dispose();
                                }
                            }
                        };
                    t.Start();
                };

            client.ConnectionLost += (s, e) =>
                {
                    if (gameBox.Image != null) gameBox.Image.Dispose();
                    gameBox.Image = null;
                    RefreshTooltip();
                    Stop();
                };


            // process special queue message to display in label
            client.Said += (s, e) =>
            {
                if (e.Place == SayPlace.Battle && client.MyBattle != null && client.MyBattle.Founder.Name == e.UserName && e.Text.StartsWith("Queue"))
                {
                    var t = e.Text.Substring(6);
                    queueLabelFormatter = Regex.Replace(t,
                        "([0-9]+)s",
                        m =>
                        {
                            var queueSeconds = int.Parse(m.Groups[1].Value);
                            queueTarget = DateTime.Now.AddSeconds(queueSeconds);
                            return "{0}s";
                        });
                    lbQueue.Text = string.Format(queueLabelFormatter, Math.Round(queueTarget.Subtract(DateTime.Now).TotalSeconds));
                }
            };


            timer.Tick += (s, e) =>
                {
                    if (client.IsLoggedIn)
                    {
                        if (WindowsApi.IdleTime.TotalMinutes > Program.Conf.IdleTime) {
                            if (!client.MyUser.IsAway) client.ChangeMyUserStatus(isAway: true);
                        } else {
                            if (client.MyUser.IsAway) client.ChangeMyUserStatus(isAway: false);
                        }
                        CheckMyBattle();
                    }
                    if (client.MyBattle != null && client.MyBattle.IsQueue)
                    {
                        lbQueue.Text = string.Format(queueLabelFormatter, Math.Round(queueTarget.Subtract(DateTime.Now).TotalSeconds));
                    }
                };
            timer.Interval = 1000;
            timer.Start();

            Program.BattleIconManager.BattleChanged += BattleIconManager_BattleChanged;

            //picoChat.Font = new Font(Program.Conf.ChatFont.FontFamily, Program.Conf.ChatFont.Size*0.8f);
            picoChat.ShowHistory = false;
            picoChat.ShowJoinLeave = false;
            //picoChat.HideScroll = true;

            BattleChatControl.BattleLine += (s, e) => picoChat.AddLine(e.Data);

            picoChat.MouseClick += (s, e) => NavigationControl.Instance.Path = "chat/battle";
        }
        public AutoHost(MetaDataCache cache, AhConfig config, int hostingPort, SpawnConfig spawn) {
            this.config = config;
            Commands = new CommandList(config);
            this.cache = cache;
            SpawnConfig = spawn;
            this.hostingPort = hostingPort;

            
            string version = config.SpringVersion ?? Program.main.Config.SpringVersion ?? GlobalConst.DefaultEngineOverride;
            springPaths = new SpringPaths(Program.main.paths.GetEngineFolderByVersion(version), Program.main.Config.DataDir);
            
            springPaths.SpringVersionChanged += (s, e) =>
                {
                    if (!String.IsNullOrEmpty(requestedEngineChange) && requestedEngineChange == springPaths.SpringVersion) {
                        config.SpringVersion = requestedEngineChange;
                        springPaths.SetEnginePath(Program.main.paths.GetEngineFolderByVersion(requestedEngineChange));
                        requestedEngineChange = null;

                        tas.Say(SayPlace.Battle, "", "rehosting to engine version " + springPaths.SpringVersion, true);
                        ComRehost(TasSayEventArgs.Default, new string[] { });
                    }
                };

            spring = new Spring(springPaths) { UseDedicatedServer = true };
            bool isManaged = SpawnConfig == null && config.Mode != AutohostMode.None;

            tas = new TasClient(MainConfig.SpringieVersion,
                                isManaged ? Login.ClientTypes.SpringieManaged : Login.ClientTypes.Springie,
                                Program.main.Config.IpOverride);

            pollTimer = new Timer(PollTimeout*1000);
            pollTimer.Enabled = false;
            pollTimer.AutoReset = false;
            pollTimer.Elapsed += pollTimer_Elapsed;

            spring.SpringExited += spring_SpringExited;
            spring.GameOver += spring_GameOver;

            spring.SpringExited += spring_SpringExited;
            spring.SpringStarted += spring_SpringStarted;
            spring.PlayerSaid += spring_PlayerSaid;
            spring.BattleStarted += spring_BattleStarted;

            tas.BattleUserLeft += tas_BattleUserLeft;
            tas.UserStatusChanged += tas_UserStatusChanged;
            tas.BattleUserJoined += tas_BattleUserJoined;
            tas.MyBattleMapChanged += tas_MyBattleMapChanged;
            tas.BattleOpened += tas_BattleOpened;
            tas.UserAdded += (o, u) => { if (u.Name == GetAccountName()) OpenBattleRoom(null, null); };

            tas.RegistrationDenied += (s, e) =>
                {
                    Trace.TraceWarning("Registration denied: {0} {1}", e.ResultCode.Description(), e.Reason);
                    CloneNumber++;
                    tas.Login(GetAccountName(), config.Password);
                };

            tas.RegistrationAccepted += (s, e) => tas.Login(GetAccountName(), config.Password);

            tas.ConnectionLost += tas_ConnectionLost;
            tas.Connected += tas_Connected;
            tas.LoginDenied += tas_LoginDenied;
            tas.LoginAccepted += tas_LoginAccepted;
            tas.Said += tas_Said;
            tas.MyBattleStarted += tas_MyStatusChangedToInGame;

            linkSpringieClient = new ResourceLinkSpringieClient(this);

            // queue autohost
            if (config != null && config.MinToJuggle != null && SpawnConfig == null)
            {
                queue = new MatchMakerQueue(this);
            }

            
            Program.main.Downloader.PackagesChanged += Downloader_PackagesChanged;

            timer = new Timer(15000);
            timer.Elapsed += (s, e) =>
                {
                    try {
                        timer.Stop();
                        timerTick++;

                        // auto update engine branch
                        if (!String.IsNullOrEmpty(config.AutoUpdateSpringBranch) && timerTick%4 == 0) CheckEngineBranch();

                        // auto verify pw map
                        if (!spring.IsRunning && config.Mode != AutohostMode.None) if (SpawnConfig == null && config.Mode == AutohostMode.Planetwars) ServerVerifyMap(false);

                        // auto start split vote
                        if (!spring.IsRunning && config.SplitBiggerThan != null && tas.MyBattle != null && config.SplitBiggerThan < tas.MyBattle.NonSpectatorCount) {
                            if (DateTime.Now.Subtract(spring.GameExited).TotalSeconds >= GameExitSplitDelay) ComSplitPlayers(TasSayEventArgs.Default, new string[]{});
                            /*
                            int cnt = tas.MyBattle.NonSpectatorCount;
                            if (cnt > lastSplitPlayersCountCalled && cnt%2 == 0) {
                                StartVote(new VoteSplitPlayers(tas, spring, this), TasSayEventArgs.Default, new string[] { });
                                lastSplitPlayersCountCalled = cnt;
                            }*/
                        }

                        // auto rehost to latest mod version
                        if (!string.IsNullOrEmpty(config.AutoUpdateRapidTag) && SpawnConfig == null) UpdateRapidMod(config.AutoUpdateRapidTag);


                    } catch (Exception ex) {
                        Trace.TraceError(ex.ToString());
                    } finally {
                        timer.Start();
                    }
                };
            timer.Start();
           
        }
        private void Event_ComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (suppressEvent_SelectedIndexChanged) return;
            if ((sender as Control).Name == "map_comboBox" && map_comboBox.SelectedItem!= null)
            {
                string mapName = (string)map_comboBox.SelectedItem;
                int selectedView = normalRadioButton.Checked ? 0 : (elevationRadioButton.Checked ? 1 : 2);
                Set_MapImages(mapName, selectedView);
                
                if (infoLabel.Text.StartsWith("Select map"))
                    infoLabel.Text = "";

                Set_InfoLabel();
                Program.Conf.SkirmisherMap = mapName;
            }
            else if ((sender as Control).Name == "game_comboBox" && game_comboBox.SelectedItem != null)
            {
                string gameName = (string)game_comboBox.SelectedItem;
                
                bool foundLocally=false;
                for(int i=0; i<modCache_folder.Count;i++)
                {
                    var mod = modCache_folder[i];
                    var version = modCache_folder[i].PrimaryModVersion;
                    version = string.IsNullOrWhiteSpace(version) ? "" : " " + version;
                    var modName = mod.Name + version;
                    if (gameName == modName)
                    {

                        modCache_folder[i] = SkirmishControlTool.GetOneSddMod(mod);
                        CallBack_Mod(modCache_folder[i]);
                        foundLocally = true;
                        break;
                    }
                }

                if (!foundLocally)
                {
                    //run GetMod() in new thread, then call "CallBack_Mod()" in current thread when finish(?). 
                    //TODO: GetModAsync() is not an offline method, it rely on downloading server generated mod/map information to work. This can cause (minor) error if user downloaded a unique map or mod that server haven't process yet!
                    Program.SpringScanner.MetaData.GetModAsync(
                        gameName,
                        mod =>
                        Invoke(new Action(() => {
                            try {
                                CallBack_Mod(mod);
                            } catch (Exception ex) {
                                Trace.TraceError("CallBack_Mod(mod) error: {0}", ex.ToString());
                            }
                        })), 
                        exception => Trace.TraceError("CallBack_Mod(mod) error: {0}", exception.ToString()));
                    //Program.SpringScanner.MetaData.GetModAsync(
                    //   (string)game_comboBox.SelectedItem,
                    //   mod=>{
                    //       try { CallBack_Mod(mod); }
                    //       catch (Exception ex) { Trace.TraceError("CallBack_Mod(mod) error: {0}", ex.ToString()); }
                    //       },
                    //   exception => { Trace.TraceError("CallBack_Mod(mod) error: {0}", exception.ToString()); },
                    //   (string)engine_comboBox.SelectedItem);
                }
                
                if (infoLabel.Text.StartsWith("Select game"))
                    infoLabel.Text = "";

                Set_InfoLabel();
                
                var defGameVer = Program.Downloader.PackageDownloader.GetByTag(KnownGames.GetDefaultGame().RapidTag);
                if (defGameVer!=null && gameName == defGameVer.InternalName)
                    Program.Conf.SkirmisherGame = null; //tell Skirmisher to use default in next startup
                else
                    Program.Conf.SkirmisherGame = gameName;

            }
            else if ((sender as Control).Name == "engine_comboBox" && engine_comboBox.SelectedItem != null)
            {
                string springVersion = (string)engine_comboBox.SelectedItem;
                string engineFolder = ZkData.Utils.MakePath(Program.SpringPaths.WritableDirectory, "engine");
                if (Environment.OSVersion.Platform != PlatformID.Unix)
                    engineFolder = engineFolder + "\\" + springVersion;
                else
                    engineFolder = engineFolder + "/" + springVersion;

                if (Program.SpringPaths.HasEngineVersion(springVersion))
                    Program.SpringPaths.SetEnginePath (engineFolder);
                spring = new Spring(Program.SpringPaths);
                

                if (infoLabel.Text.StartsWith("Select engine"))
                    infoLabel.Text = "";

                Set_InfoLabel();
                
                if ((string)engine_comboBox.SelectedItem == (GlobalConst.DefaultEngineOverride ?? Program.TasClient.ServerSpringVersion))
                    Program.Conf.SkirmisherEngine = null; //tell Skirmihser to use default in next run
                else
                    Program.Conf.SkirmisherEngine = (string)engine_comboBox.SelectedItem;

                if (Program.SpringPaths.HasEngineVersion(springVersion))
                    springAi = SkirmishControlTool.GetSpringAIs(engineFolder);
                else
                    springAi.Clear();
            }
            //check if we have entered game, map and engine value so that we can update the Sync icon.
            bool missingEntry = (game_comboBox.SelectedItem == null || engine_comboBox.SelectedItem == null || map_comboBox.SelectedItem == null);
            if (missingEntry != wasMissingEntry) Refresh_PlayerBox();
            wasMissingEntry = missingEntry;
        }
 public VotePlanet(TasClient tas, Spring spring, AutoHost ah): base(tas, spring, ah) {}
        static void GeneratePlayerSection(StringBuilder script, Spring.SpringBattleContext setup)
        {
            // ordinary battle stuff

            var userNum = 0;
            var teamNum = 0;
            var aiNum = 0;

            foreach (var u in setup.LobbyStartContext.Players)
            {
                Dictionary<string, string> parameters = new Dictionary<string, string>();
                setup?.LobbyStartContext?.UserParameters.TryGetValue(u.Name, out parameters);

                ScriptAddUser(script, userNum, u, teamNum, parameters);

                if (!u.IsSpectator) {
                    ScriptAddTeam(script, teamNum, userNum, u.AllyID);
                    teamNum++;
                }

                foreach (var b in setup.LobbyStartContext.Bots.Where(x => x.Owner == u.Name)) {
                    ScriptAddBot(script, aiNum, teamNum, userNum, b.BotAI, b.BotName);
                    aiNum++;
                    ScriptAddTeam(script, teamNum, userNum, b.AllyID);
                    teamNum++;
                }
                userNum++;
            }

            // add unowned bots to last player
            foreach (var b in setup.LobbyStartContext.Bots.Where(x => !setup.LobbyStartContext.Players.Any(y=>y.Name == x.Owner)))
            {
                ScriptAddBot(script, aiNum, teamNum, userNum-1, b.BotAI, b.BotName);
                aiNum++;
                ScriptAddTeam(script, teamNum, userNum-1, b.AllyID);
                teamNum++;
            }


            // ALLIANCES AND START BOXES
            var startboxes = new StringBuilder();
            startboxes.Append("return { ");
            script.AppendLine();
            for (var allyNumber = 0; allyNumber < MaxAllies; allyNumber++) {
                script.AppendFormat("[ALLYTEAM{0}]\n", allyNumber);
                script.AppendLine("{");
                script.AppendLine("     NumAllies=0;");
                script.AppendLine("}");
            }

            startboxes.Append("}");
            script.AppendLine();

            script.AppendLine("  [MODOPTIONS]");
            script.AppendLine("  {");

            script.AppendFormat("    startboxes={0};\n", startboxes.ToString());


            // write final options to script
            foreach (var kvp in setup?.LobbyStartContext?.ModOptions) script.AppendFormat("    {0}={1};\n", kvp.Key, kvp.Value);

            script.AppendLine("  }");

            script.AppendLine("}");
        }
 public static void Respond(TasClient tas, Spring spring, TasSayEventArgs e, string text) {
     var p = SayPlace.User;
     bool emote = false;
     if (e.Place == SayPlace.Battle) {
         p = SayPlace.BattlePrivate;
         emote = true;
     }
     if (e.Place == SayPlace.Game && spring.IsRunning) spring.SayGame(text);
     else tas.Say(p, e.UserName, text, emote);
 }
 public VoteForceStart(TasClient tas, Spring spring, AutoHost ah): base(tas, spring, ah) {}
        private static SpringBattle SaveSpringBattle(Spring.SpringBattleContext result, ZkDataContext db)
        {
            var sb = new SpringBattle
            {
                HostAccountID = Account.AccountByName(db, result.LobbyStartContext.FounderName)?.AccountID,
                Mode = result.LobbyStartContext.Mode,
                Duration = result.Duration,
                EngineGameID = result.EngineBattleID,
                MapResourceID = db.Resources.Single(x => x.InternalName == result.LobbyStartContext.Map).ResourceID,
                ModResourceID = db.Resources.Single(x => x.InternalName == result.LobbyStartContext.Mod).ResourceID,
                HasBots = result.LobbyStartContext.Bots.Any(),
                IsMission = result.LobbyStartContext.IsMission,
                PlayerCount = result.ActualPlayers.Count(x => !x.IsSpectator),
                StartTime = result.StartTime,
                Title = result.LobbyStartContext.Title,
                ReplayFileName = result.ReplayName,
                EngineVersion = result.LobbyStartContext.EngineVersion,
                IsMatchMaker = result.LobbyStartContext.IsMatchMakerGame
            };
            db.SpringBattles.InsertOnSubmit(sb);

            foreach (BattlePlayerResult p in result.ActualPlayers)
            {
                var account = Account.AccountByName(db, p.Name);
                if (account != null)
                {
                    sb.SpringBattlePlayers.Add(new SpringBattlePlayer
                    {
                        Account = account,
                        AccountID = account.AccountID,
                        AllyNumber = p.AllyNumber,
                        IsInVictoryTeam = p.IsVictoryTeam,
                        IsSpectator = p.IsSpectator,
                        LoseTime = p.LoseTime
                    });
                }
            }

            db.SaveChanges();

            return db.SpringBattles.FirstOrDefault(x => x.SpringBattleID == sb.SpringBattleID); // reselect from db to get proper lazy proxies
        }