public bool IsLaunchDue() { lock (_launchTimingLock) { if (_launchRequestedTimeUtc > _lastLaunchInitiatedUtc) { return(true); } else { var elapsed = DateTime.UtcNow - _lastLaunchInitiatedUtc; if (elapsed.TotalSeconds > ConfigSettings.GetConfigInt("RelaunchIntervalSeconds", 60)) { return(true); } } } return(false); }
void _worker_DoWork(object sender, DoWorkEventArgs e) { WorkerArgs args = (e.Argument as WorkerArgs); if (args == null) { return; } lock (_launchTimingLock) { _lastLaunchInitiatedUtc = DateTime.UtcNow; } _serverIndex = 0; System.Collections.Concurrent.ConcurrentQueue <LaunchItem> globalQueue = args.ConcurrentLaunchQueue; _serverTotal = globalQueue.Count; if (_serverTotal == 0) { return; } LaunchItem launchItem = null; var accountLaunchTimes = _gameSessionMap.GetLaunchAccountTimes(); while (globalQueue.TryDequeue(out launchItem)) { int threadDelayMs = ConfigSettings.GetConfigInt("ThreadGameLaunchDelayMs", 100); Thread.Sleep(threadDelayMs); new Thread((x) => { Thread.CurrentThread.IsBackground = true; launchGameFromItem(args, (LaunchItem)x, accountLaunchTimes); }).Start(launchItem); if (_worker.CancellationPending) { e.Cancel = true; return; } } }
public GameLaunchResult LaunchGameClient(string exelocation, string serverName, string accountName, string password, string ipAddress, string gameApiUrl, string loginServerUrl, string discordurl, ServerModel.ServerEmuEnum emu, string desiredCharacter, ServerModel.RodatEnum rodatSetting, ServerModel.SecureEnum secureSetting, bool simpleLaunch) { var result = new GameLaunchResult(); //-username "MyUsername" -password "MyPassword" -w "ServerName" -2 -3 if (string.IsNullOrWhiteSpace(exelocation)) { throw new Exception("Empty exelocation"); } if (!File.Exists(exelocation)) { throw new Exception("Missing exe: " + exelocation); } if (string.IsNullOrWhiteSpace(serverName)) { throw new Exception("Empty serverName"); } if (string.IsNullOrWhiteSpace(accountName)) { throw new Exception("Empty accountName"); } string genArgs = "TODO-below"; bool isGDLE = (emu == ServerModel.ServerEmuEnum.GDLE); bool isACE = (emu == ServerModel.ServerEmuEnum.ACE); if (isGDLE) { //GDL //-h [server ip] -p [server port] -a username:password -rodat off int tok = ipAddress.IndexOf(':'); if (tok < 0) { throw new Exception("GDL address missing colon in username:password specification"); } string ip = ipAddress.Substring(0, tok); string port = ipAddress.Substring(tok + 1); string genArgsGDLEServer; if (rodatSetting == ServerModel.RodatEnum.On) { genArgsGDLEServer = "-h " + ip + " -p " + port + " -a " + accountName + ":" + password + " -rodat on"; } else { genArgsGDLEServer = "-h " + ip + " -p " + port + " -a " + accountName + ":" + password + " -rodat off"; } genArgs = genArgsGDLEServer; } else if (isACE) { //ACE //acclient.exe -a testaccount -v testpassword -h 127.0.0.1:9000 //-a accountName -v password -h ipaddress string genArgsACEServer = "-a " + accountName + " -v " + password + " -h " + ipAddress; genArgs = genArgsACEServer; } /* This is currently removed, and DF is gone. Leaving this in case anyone else decides to use the secure login from DF * else if(isDF) * { * if (secureSetting == ServerModel.SecureEnum.On) * { * var loginInfo = SecureLogin(accountName: accountName, password: password, gameApiUrl: gameApiUrl, loginServerUrl: loginServerUrl); * password = loginInfo.JwtToken; * accountName = loginInfo.SubscriptionId; * * } * //DF * //acclient.exe -a testaccount -h 127.0.0.1:9000 -glsticketdirect testpassword * string genArgsACEServer = "-a " + accountName + " -h " + ipAddress + " -glsticketdirect " + password; * genArgs = genArgsACEServer; * } */ string pathToFile = exelocation; //check if we're doing a simple launch. If we are, ignore the fancy management stuff bool gameReady = false; if (simpleLaunch) { gameReady = true; } Process launcherProc = null; LaunchControl.LaunchResponse launchResponse = null; try { ProcessStartInfo startInfo = new ProcessStartInfo(); startInfo.FileName = pathToFile; startInfo.Arguments = genArgs; startInfo.CreateNoWindow = true; RecordLaunchInfo(serverName, accountName, desiredCharacter, DateTime.UtcNow); string charFilepath = SteelFilter.FileLocations.GetCharacterFilePath(ServerName: serverName, AccountName: accountName); string launchResponseFilepath = SteelFilter.FileLocations.GetCurrentLaunchResponseFilePath(ServerName: serverName, AccountName: accountName); DateTime startWait = DateTime.UtcNow; DateTime characterFileWrittenTime = DateTime.MaxValue; DateTime loginTime = DateTime.MaxValue; startInfo.WorkingDirectory = Path.GetDirectoryName(startInfo.FileName); if (ShouldWeUseDecal(simpleLaunch)) { //Start Process with Decal Injection string commandLineLaunch = startInfo.FileName + " " + startInfo.Arguments; string decalInjectPath = DecalInjection.GetDecalLocation(); string command = "DecalStartup"; string asheronFolder = startInfo.WorkingDirectory; launcherProc = Process.GetProcessById(Convert.ToInt32(LaunchInjected(commandLineLaunch, asheronFolder, decalInjectPath, command))); } else { //Start Process without Decal launcherProc = Process.Start(startInfo); } Logger.WriteInfo(string.Format("PID = {0}", launcherProc.Id)); launcherProc.EnableRaisingEvents = true; launcherProc.Exited += LauncherProc_Exited; if (!gameReady) { WaitForLauncher(launcherProc); int secondsTimeout = ConfigSettings.GetConfigInt("LauncherGameTimeoutSeconds", 120); TimeSpan timeout = new TimeSpan(0, 0, 0, secondsTimeout); while (!gameReady && (DateTime.UtcNow - startWait < timeout)) { if (CheckForStop()) { // User canceled if (!launcherProc.HasExited) { launcherProc.Kill(); } return(result); } ReportGameStatus(string.Format("Waiting for game: {0}/{1} sec", (int)((DateTime.UtcNow - startWait).TotalSeconds), secondsTimeout)); if (characterFileWrittenTime == DateTime.MaxValue) { // First we wait until DLL writes character file FileInfo fileInfo = new FileInfo(charFilepath); if (fileInfo.LastWriteTime.ToUniversalTime() >= startWait) { characterFileWrittenTime = DateTime.UtcNow; } } else if (loginTime == DateTime.MaxValue) { // Now we wait until DLL logs in or user logs in interactively FileInfo fileInfo = new FileInfo(launchResponseFilepath); if (fileInfo.LastWriteTime.ToUniversalTime() >= startWait) { loginTime = DateTime.UtcNow; TimeSpan maxLatency = DateTime.UtcNow - startWait; launchResponse = LaunchControl.GetLaunchResponse(ServerName: serverName, AccountName: accountName, maxLatency: maxLatency); } } else { // Then we give it 6 more seconds to complete login int loginTimeSeconds = ConfigSettings.GetConfigInt("LauncherGameLoginTime", 0); if (DateTime.UtcNow >= characterFileWrittenTime.AddSeconds(loginTimeSeconds)) { gameReady = true; } } System.Threading.Thread.Sleep(1000); } } } catch (Exception exc) { throw new Exception(string.Format( "Failed to launch program. Check path '{0}': {1}", exelocation, exc.Message)); } if (!gameReady) { if (launcherProc != null && !launcherProc.HasExited) { launcherProc.Kill(); } } if (launchResponse != null && launchResponse.IsValid) { result.Success = gameReady; result.ProcessId = launchResponse.ProcessId; } if (simpleLaunch) { result.Success = true; } return(result); }
/// <summary> /// Read all process files /// Check if any characters have changed /// </summary> private void PerformReadProcessFiles() { foreach (var gameSession in _map.GetAllGameSessions()) { string heartbeatFile = gameSession.ProcessStatusFilepath; if (string.IsNullOrEmpty(heartbeatFile)) { // This occurs when launching game session continue; } var response = SteelFilter.LaunchControl.GetHeartbeatStatus(heartbeatFile); if (!response.IsValid) { Logger.WriteError(string.Format("Invalid contents in heartbeat file: {0}", heartbeatFile)); continue; } var status = GetStatusFromHeartbeatFileTime(gameSession); if (!response.Status.IsOnline) { int gameInteractionTimeoutSeconds = ConfigSettings.GetConfigInt("GameInteractionTimeoutSeconds", 10); // SteelFilter reports !IsOnline if server dispatch quits firing // but that isn't reliable, as it doesn't fire when not logged in to a character if (response.Status.LastServerDispatchSecondsAgo > gameInteractionTimeoutSeconds) { status = ServerAccountStatusEnum.None; Logger.WriteInfo("Killing offline/character screen game"); } } else { _lastOnlineTimeUtc = DateTime.UtcNow; } if (status == ServerAccountStatusEnum.None) { Process p = TryGetProcessFromId(response.Status.ProcessId); if (p != null) { p.Kill(); File.Delete(heartbeatFile); _map.RemoveGameSessionByProcessId(p.Id); Logger.WriteDebug("Killing process: " + p.Id.ToString()); OnGameDied(new EventArgs()); } } bool newGame = false; bool changedGame = false; bool changedStatus = false; if (gameSession.AccountName == null) { newGame = true; } else if (gameSession.AccountName != response.Status.AccountName || gameSession.ServerName != response.Status.ServerName) { // This doesn't make sense and shouldn't happen // Account & Server should be fixed for the life of a game session Logger.WriteError(string.Format("Account/Server change in heartbeat file!: {0}", heartbeatFile)); changedGame = true; } else if (gameSession.CharacterName != response.Status.CharacterName) { changedGame = true; } else if (gameSession.Status != status) { changedStatus = true; gameSession.Status = status; } UpdateGameSessionFromHeartbeatStatus(gameSession, heartbeatFile, response); if (newGame) { NotifyGameChange(gameSession, GameChangeType.StartGame); } else if (changedGame) { NotifyGameChange(gameSession, GameChangeType.ChangeGame); } else if (changedStatus) { NotifyGameChange(gameSession, GameChangeType.ChangeStatus); } } _lastReadProcesFilesUtc = DateTime.UtcNow; }