static bool SyncAndRunApplication(string[] Args, Mutex InstanceMutex, TextWriter LogWriter) { // Try to find Perforce in the path string PerforceFileName = null; foreach (string PathDirectoryName in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(new char[] { Path.PathSeparator }, StringSplitOptions.RemoveEmptyEntries)) { try { string PossibleFileName = Path.Combine(PathDirectoryName, "p4.exe"); if (File.Exists(PossibleFileName)) { PerforceFileName = PossibleFileName; break; } } catch { } } // If it doesn't exist, don't continue if (PerforceFileName == null) { LogWriter.WriteLine("UnrealGameSync requires the Perforce command-line tools. Please download and install from http://www.perforce.com/."); return(false); } // Get the path that we're syncing bool bUnstable = Args.Contains("-unstable", StringComparer.InvariantCultureIgnoreCase); if (!bUnstable && (Control.ModifierKeys & Keys.Shift) != 0 && UnstableSyncPath != null) { if (MessageBox.Show("Use the latest unstable build of UnrealGameSync?\n\n(This message was triggered by holding down the SHIFT key on startup).", "Use unstable build?", MessageBoxButtons.YesNo) == DialogResult.Yes) { bUnstable = true; } } string SyncPath = bUnstable? UnstableSyncPath : StableSyncPath; LogWriter.WriteLine("Syncing from {0}", SyncPath); // Create the target folder string ApplicationFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "UnrealGameSync", "Latest"); if (!SafeCreateDirectory(ApplicationFolder)) { LogWriter.WriteLine("Couldn't create directory: {0}", ApplicationFolder); return(false); } // Find the most recent changelist List <string> SubmittedLines = new List <string>(); if (RunPerforceCommand(PerforceFileName, "changes -s submitted -m 1", SubmittedLines, LogWriter) != 0) { LogWriter.WriteLine("Couldn't find last changelist"); return(false); } // Split into tokens string[] ChangeTokens = SubmittedLines[0].Split(' '); // Parse the changelist number int RequiredChangeNumber; if (ChangeTokens.Length < 2 || ChangeTokens[0] != "Change" || !int.TryParse(ChangeTokens[1], out RequiredChangeNumber)) { LogWriter.WriteLine("Couldn't parse last changelist number"); return(false); } // Read the current version string SyncVersionFile = Path.Combine(ApplicationFolder, "SyncVersion.txt"); string RequiredSyncText = String.Format("{0}@{1}", SyncPath, RequiredChangeNumber.ToString()); // Check if the version has changed string SyncText; if (!File.Exists(SyncVersionFile) || !TryReadAllText(SyncVersionFile, out SyncText) || SyncText != RequiredSyncText) { // Try to delete the directory contents. Retry for a while, in case we've been spawned by an application in this folder to do an update. for (int NumRetries = 0; !SafeDeleteDirectoryContents(ApplicationFolder); NumRetries++) { if (NumRetries > 20) { LogWriter.WriteLine("Couldn't delete contents of {0} (retried {1} times).", ApplicationFolder, NumRetries); return(false); } Thread.Sleep(500); } // Find all the files in the sync path at this changelist List <string> FileLines = new List <string>(); if (RunPerforceCommand(PerforceFileName, String.Format("-z tag fstat \"{0}@{1}\"", SyncPath, RequiredChangeNumber), FileLines, LogWriter) != 0) { LogWriter.WriteLine("Couldn't find matching files."); return(false); } // Sync all the files in this list to the same directory structure under the application folder string DepotPathPrefix = SyncPath.Substring(0, SyncPath.LastIndexOf('/') + 1); foreach (string FileLine in FileLines) { const string DepotPathTag = "... depotFile "; if (FileLine.StartsWith(DepotPathTag)) { string DepotPath = FileLine.Substring(DepotPathTag.Length).Trim(); if (!DepotPath.StartsWith(DepotPathPrefix, StringComparison.InvariantCultureIgnoreCase)) { LogWriter.WriteLine("Found file {0} which did not begin with {1}", DepotPath, DepotPathPrefix); return(false); } string LocalPath = Path.Combine(ApplicationFolder, DepotPath.Substring(DepotPathPrefix.Length).Replace('/', Path.DirectorySeparatorChar)); if (!SafeCreateDirectory(Path.GetDirectoryName(LocalPath))) { LogWriter.WriteLine("Couldn't create folder {0}", Path.GetDirectoryName(LocalPath)); return(false); } if (RunPerforceCommand(PerforceFileName, String.Format("print -o \"{0}\" \"{1}@{2}\"", LocalPath, DepotPath, RequiredChangeNumber), null, LogWriter) != 0) { LogWriter.WriteLine("Couldn't sync {0} to {1}", DepotPath, LocalPath); return(false); } } } // Update the version if (!TryWriteAllText(SyncVersionFile, RequiredSyncText)) { LogWriter.WriteLine("Couldn't write sync text to {0}", SyncVersionFile); return(false); } } LogWriter.WriteLine(); // Build the command line for the synced application, including the sync path to monitor for updates StringBuilder NewCommandLine = new StringBuilder(String.Format("-updatepath=\"{0}@>{1}\" -updatespawn=\"{2}\"{3}", SyncPath, RequiredChangeNumber, Assembly.GetEntryAssembly().Location, bUnstable? " -unstable" : "")); foreach (string Arg in Args) { if (Arg.Contains(' ')) { NewCommandLine.AppendFormat("\"{0}\"", Arg); } else { NewCommandLine.AppendFormat(" {0}", Arg); } } // Release the mutex now so that the new application can start up InstanceMutex.Close(); // Check the application exists string ApplicationExe = Path.Combine(ApplicationFolder, "UnrealGameSync.exe"); if (!File.Exists(ApplicationExe)) { LogWriter.WriteLine("Application was not synced from Perforce. Check you have access to {0}", SyncPath); return(false); } // Spawn the application LogWriter.WriteLine("Spawning {0} with command line: {1}", ApplicationExe, NewCommandLine.ToString()); using (Process ChildProcess = new Process()) { ChildProcess.StartInfo.FileName = ApplicationExe; ChildProcess.StartInfo.Arguments = NewCommandLine.ToString(); ChildProcess.StartInfo.UseShellExecute = false; ChildProcess.StartInfo.CreateNoWindow = false; if (!ChildProcess.Start()) { LogWriter.WriteLine("Failed to start process"); return(false); } } return(true); }