public RequestResult <string> Play() { List <PlayerInfo> players = profile.PlayerData; for (int i = 0; i < players.Count; i++) { players[i].PlayerID = i; } RequestResult <String> result = new RequestResult <String>(); List <HandlerContext> contexts = new List <HandlerContext>(); for (int i = 0; i < players.Count; i++) { PlayerInfo player = players[i]; HandlerContext context = handlerManager.HandlerData.CreateContext(profile, player, hasKeyboardPlayer); contexts.Add(context); context.PlayerID = player.PlayerID; List <HandlerModule> modules = player.Modules; for (int j = 0; j < modules.Count; j++) { modules[j].PrePlayPlayer(i, context); } } // get all the SymlinkData from each players IOModule // and start the StartGame application to symlink them List <SymlinkGameData> symData = new List <SymlinkGameData>(); for (int i = 0; i < players.Count; i++) { PlayerInfo player = players[i]; IOModule mod = GetModule <IOModule>(player); SymlinkGameData data = mod.SymlinkData; symData.Add(data); } StartGameData symStartData = StartGameUtil.BuildSymlinkGameData(symData.ToArray()); // check if we need to kill mutexes bool killingMutexes = false; if (handlerManager.HandlerData.KillMutex?.Length > 0) { // need to also kill mutexes killingMutexes = true; StartGameData scanKillData = StartGameUtil.BuildScanKillMutexData(Path.GetFileNameWithoutExtension(userGame.ExePath), players.Count, handlerManager.HandlerData.KillMutex); StartGameUtil.RunMultipleTasks(new StartGameData[] { symStartData, scanKillData }, true); // wait for confirmation file that the folders were symlinked // (RunMultipleTasks doesnt wait for process to finish) string dataFile = StartGameUtil.GetStartDataPath(); for (; ;) { if (File.Exists(dataFile)) { try { string txt = File.ReadAllText(dataFile); bool res; if (bool.TryParse(txt, out res)) { break; } } catch { } } Thread.Sleep(250); } File.Delete(dataFile); } else { // just symlink the folders StartGameUtil.RunPreBuiltData(symStartData, true); } for (int i = 0; i < players.Count; i++) { PlayerInfo player = players[i]; HandlerContext context = contexts[i]; handlerManager.Play(context, player); List <HandlerModule> modules = player.Modules; for (int j = 0; j < modules.Count; j++) { modules[j].PlayPlayer(i, context); } Thread.Sleep(TimeSpan.FromMilliseconds(handlerManager.HandlerData.PauseBetweenStarts)); IGameProcessModule procModule = GetModule <IGameProcessModule>(player); if (handlerManager.HandlerData.WaitFullWindowLoad) { for (; ;) { Thread.Sleep(100); // check for window status if (procModule.HasWindowSetup(player)) { break; } } } if (killingMutexes) { // wait for StartGame confirmation that mutexes were killed string dataFile = StartGameUtil.GetStartDataPath(); for (; ;) { if (File.Exists(dataFile)) { try { string txt = File.ReadAllText(dataFile); bool res; if (bool.TryParse(txt, out res)) { break; } } catch { } } Thread.Sleep(250); } File.Delete(dataFile); } } return(result); }
public override void PrePlayPlayer(int index, HandlerContext context) { nucleusRootFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); tempDir = GameManager.GetTempFolder(handlerData); exeFolder = Path.GetDirectoryName(userGame.ExePath); rootFolder = exeFolder; workingFolder = exeFolder; if (!string.IsNullOrEmpty(handlerData.ExecutablePath)) { rootFolder = StringUtil.ReplaceCaseInsensitive(exeFolder, handlerData.ExecutablePath.ToLower(), ""); } if (!string.IsNullOrEmpty(handlerData.WorkingFolder)) { rootFolder = StringUtil.ReplaceCaseInsensitive(exeFolder, handlerData.WorkingFolder.ToLower(), ""); workingFolder = Path.Combine(exeFolder, handlerData.WorkingFolder.ToLower()); } if (handlerData.SymlinkGame || handlerData.HardcopyGame) { List <string> dirExclusions = new List <string>(); List <string> fileExclusions = new List <string>(); List <string> fileCopies = new List <string>(); // symlink the game folder (and not the bin folder, if we have one) linkFolder = Path.Combine(tempDir, "Instance" + index); try { if (Directory.Exists(linkFolder)) { Directory.Delete(linkFolder, true); } } catch { } Directory.CreateDirectory(linkFolder); linkWorkingDir = linkFolder; if (string.IsNullOrEmpty(handlerData.ExecutablePath)) { linkedExePath = Path.Combine(linkWorkingDir, Path.GetFileName(this.userGame.ExePath)); if (!string.IsNullOrEmpty(handlerData.WorkingFolder)) { linkWorkingDir = Path.Combine(linkFolder, handlerData.WorkingFolder); dirExclusions.Add(handlerData.WorkingFolder); } } else { dirExclusions.Add(handlerData.ExecutablePath); linkedExePath = Path.Combine(linkWorkingDir, handlerData.ExecutablePath, Path.GetFileName(this.userGame.ExePath)); if (!string.IsNullOrEmpty(handlerData.WorkingFolder)) { linkWorkingDir = Path.Combine(linkFolder, handlerData.WorkingFolder); dirExclusions.Add(handlerData.WorkingFolder); } else { linkWorkingDir = Path.Combine(linkFolder, handlerData.ExecutablePath); } } // some games have save files inside their game folder, so we need to access them inside the loop handlerData.RegisterAdditional(Folder.InstancedGameFolder.ToString(), linkFolder); if (handlerData.Hook.CustomDllEnabled) { fileExclusions.Add("xinput1_3.dll"); fileExclusions.Add("ncoop.ini"); } if (!handlerData.SymlinkExe) { fileCopies.Add(handlerData.ExecutableName.ToLower()); } // additional ignored files by the generic info if (handlerData.FileSymlinkExclusions != null) { string[] symlinkExclusions = handlerData.FileSymlinkExclusions; for (int k = 0; k < symlinkExclusions.Length; k++) { string s = symlinkExclusions[k]; // make sure it's lower case fileExclusions.Add(s.ToLower()); } } if (handlerData.FileSymlinkCopyInstead != null) { string[] fileSymlinkCopyInstead = handlerData.FileSymlinkCopyInstead; for (int k = 0; k < fileSymlinkCopyInstead.Length; k++) { string s = fileSymlinkCopyInstead[k]; // make sure it's lower case fileCopies.Add(s.ToLower()); } } if (handlerData.DirSymlinkExclusions != null) { string[] symlinkExclusions = handlerData.DirSymlinkExclusions; for (int k = 0; k < symlinkExclusions.Length; k++) { string s = symlinkExclusions[k]; // make sure it's lower case dirExclusions.Add(s.ToLower()); } } string[] fileExclusionsArr = fileExclusions.ToArray(); string[] fileCopiesArr = fileCopies.ToArray(); if (handlerData.HardcopyGame) { // copy the directory int exitCode; FileUtil.CopyDirectory(rootFolder, new DirectoryInfo(rootFolder), linkFolder, out exitCode, dirExclusions.ToArray(), fileExclusionsArr, true); } else { SymlinkGameData symData = new SymlinkGameData(); symData.SourcePath = rootFolder; symData.DestinationPath = linkFolder; symData.DirExclusions = dirExclusions.ToArray(); symData.FileExclusions = fileExclusionsArr; symData.FileCopies = fileCopiesArr; SymlinkData = symData; //int exitCode; //WinDirectoryUtil.LinkDirectory(rootFolder, new DirectoryInfo(rootFolder), linkFolder, out exitCode, dirExclusions.ToArray(), fileExclusionsArr, fileCopiesArr, true); } } else { linkedExePath = userGame.ExePath; linkWorkingDir = rootFolder; linkFolder = workingFolder; } context.InstancedExePath = linkedExePath; context.InstallFolder = exeFolder; context.InstanceFolder = linkFolder; context.InstancedWorkingPath = linkWorkingDir; }
private static void ExecuteTask(StartGameData data) { switch (data.Task) { case GameStarterTask.StartGame: { string gamePath = data.Parameters[0]; string gameArgs = data.Parameters[1]; string gameWorkingDir = data.Parameters[2]; ConsoleU.WriteLine($"Start game: EXE: {gamePath} ARGS: {gameArgs} WORKDIR: {gameWorkingDir}", Palette.Feedback); StartGame(gamePath, gameArgs, gameWorkingDir); } break; case GameStarterTask.KillMutex: { Log.WriteLine($"Kill Mutex Task"); string procId = data.Parameters[0]; string[] mutexes = new string[data.Parameters.Length - 1]; for (int j = 1; j < data.Parameters.Length; j++) { string m = data.Parameters[j]; mutexes[j - 1] = m; } KillMutex(procId, mutexes); WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); } break; case GameStarterTask.RenameMutex: { Log.WriteLine($"Rename Mutex Task"); string procId = data.Parameters[0]; string[] mutexes = new string[data.Parameters.Length - 1]; for (int j = 1; j < data.Parameters.Length; j++) { string m = data.Parameters[j]; mutexes[j - 1] = m; } KillMutex(procId, mutexes); WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); } break; case GameStarterTask.ScanKillMutex: { Log.WriteLine($"Scan Kill Mutex"); List <int> processIds = new List <int>(); for (int j = 0; j < data.Parameters.Length; j++) { string scanMutexDataRaw = data.Parameters[j]; ScanMutexData scanMutex = JsonConvert.DeserializeObject <ScanMutexData>(scanMutexDataRaw); Log.WriteLine($"Kill Mutex for process {scanMutex.ProcessName}"); for (; ;) { Process[] procs = Process.GetProcessesByName(scanMutex.ProcessName); if (procs == null || procs.Length == 0) { Thread.Sleep(250); } else { // kill mutexes bool killedMutexes = false; for (int k = 0; k < procs.Length; k++) { Process p = procs[k]; if (processIds.Contains(p.Id)) { continue; } // start other process, as the mutexes are only truly killed // when the process is ended if (scanMutex.ShouldRename) { StartGameUtil.RenameMutex(p, scanMutex.Mutexes); } else { StartGameUtil.KillMutex(p, scanMutex.Mutexes); } //KillMutex(p.Id.ToString(), scanMutex.Mutexes); processIds.Add(p.Id); killedMutexes = true; break; } if (killedMutexes) { Log.WriteLine($"Killed all mutexes for process {scanMutex.ProcessName}"); WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); break; } } } } } break; case GameStarterTask.MultipleTasks: { Log.WriteLine($"Multiple tasks"); for (int j = 0; j < data.Parameters.Length; j++) { string taskDataRaw = data.Parameters[j]; StartGameData taskData = JsonConvert.DeserializeObject <StartGameData>(taskDataRaw); Log.WriteLine($"Executing task {j + 1}"); ExecuteTask(taskData); } } break; case GameStarterTask.QueryMutex: { string procId = data.Parameters[0]; string[] mutexes = new string[data.Parameters.Length - 1]; for (int j = 1; j < data.Parameters.Length; j++) { string m = data.Parameters[j]; mutexes[j - 1] = m; } } break; case GameStarterTask.ListMonitors: break; case GameStarterTask.ScanGames: { // initialize game manager to read available handlers GameManager gameManager = new GameManager(); List <string> games = new List <string>(); for (int j = 0; j < data.Parameters.Length; j++) { string driveName = data.Parameters[j]; //SearchStorageInfo info = JsonConvert.DeserializeObject<SearchStorageInfo>(storageData); DriveInfo drive = new DriveInfo(driveName); if (!drive.IsReady) { continue; } Log.WriteLine($"> Searching drive {drive.Name} for game executables"); Dictionary <ulong, FileNameAndParentFrn> allExes = new Dictionary <ulong, FileNameAndParentFrn>(); MFTReader mft = new MFTReader(); mft.Drive = drive.RootDirectory.FullName; // TODO: search only for specific games? mft.EnumerateVolume(out allExes, new string[] { ".exe" }); foreach (KeyValuePair <UInt64, FileNameAndParentFrn> entry in allExes) { FileNameAndParentFrn file = (FileNameAndParentFrn)entry.Value; string name = file.Name; string lower = name.ToLower(); string path = mft.GetFullPath(file); if (path.Contains("$Recycle.Bin") || path.Contains(@"\Instance")) { // noope continue; } if (GameManager.Instance.AnyGame(lower)) { Log.WriteLine($"Found game at path: {path}"); games.Add(path); } } } WriteToDataFile(Assembly.GetEntryAssembly().Location, JsonConvert.SerializeObject(games)); } break; case GameStarterTask.SymlinkFolders: for (int j = 0; j < data.Parameters.Length; j++) { string symData = data.Parameters[j]; Log.WriteLine($"Symlink game instance {j + 1}"); SymlinkGameData gameData = JsonConvert.DeserializeObject <SymlinkGameData>(symData); int exitCode; WinDirectoryUtil.LinkDirectory(gameData.SourcePath, new DirectoryInfo(gameData.SourcePath), gameData.DestinationPath, out exitCode, gameData.DirExclusions, gameData.FileExclusions, gameData.FileCopies, true); } WriteToDataFile(Assembly.GetEntryAssembly().Location, true.ToString()); break; } }