/// <summary> /// Run the command, which should open Minecraft. Assuming everything else is filled in as intended. /// </summary> /// <returns>Status of exceptions or success</returns> private Process RunCommand(MinecraftSession session, IGameProcessMonitor monitor) { string args = FormatArguments(session); string javaFile = FileUtility.FindOnPath(JavaFile); if (javaFile == null) javaFile = JavaFile; Logger.Debug("Starting minecraft:"); Logger.Debug ("{0} {1}", javaFile, args); monitor.OutputLine(GameMessageType.Output, "Starting minecraft:"); monitor.OutputLine(GameMessageType.Output, string.Format("{0} {1}", javaFile, args)); Process mcProc = new Process(); mcProc.StartInfo.UseShellExecute = false; mcProc.StartInfo.WorkingDirectory = GameLocation; mcProc.StartInfo.FileName = javaFile; mcProc.StartInfo.Arguments = args; mcProc.StartInfo.RedirectStandardOutput = true; mcProc.StartInfo.RedirectStandardError = true; try { mcProc.Start(); if (CPUPriority == "Realtime") { mcProc.PriorityClass = ProcessPriorityClass.RealTime; } else if (CPUPriority == "High") { mcProc.PriorityClass = ProcessPriorityClass.High; } else if (CPUPriority == "Above Normal") { mcProc.PriorityClass = ProcessPriorityClass.AboveNormal; } else if (CPUPriority == "Below Normal") { mcProc.PriorityClass = ProcessPriorityClass.BelowNormal; } return mcProc; } catch (Exception e) { Logger.Debug(e); throw e; } }
/// <summary> /// Starts Minecraft. This method will call Setup() to ensure that all needed assets, libraries, and /// the needed game version are downloaded into the various cache directories, and installed into /// the folder listed at GameFolder. If you are using a verison of Minecraft prior to 1.7.4, we will /// verify the contents of assets/legacy/virtual are exactly the same as those listed in the /// Minecraft asset manifest associated to the version being launched. Any mismatches will be replaced /// from the asset cache. /// </summary> /// <param name="authSession"> /// The Minecraft authentication session to use while launching the game. To obtain one of these, /// construct a MinecraftAuthentication instance and call the Login() method, giving a valid Minecraft /// username and password. /// </param> /// <param name="monitor"> /// Receives events related to the Minecraft game process. Events will be received on a foreign thread, /// You as the caller MUST ensure that you perform any operations within the correct thread. Many user /// interface frameworks are single-threaded by default, and thus require you to pass a message to the /// UI thread from the game monitor thread which calls your IGameMonitor instance. Please do not report /// problems to us or your UI framework because you aren't doing threading correctly. We cannot stress /// enough to learn the basics of the C# lock() statement and also System.Collections.Generic.Queue<T>. /// </param> /// <returns></returns> public Process Start(MinecraftSession authSession, IGameProcessMonitor monitor) { Username = authSession.OriginalUsername; Setup(); GetNatives(); // Run Minecraft, with a pair of threads for pumping messages out of StandardError/Output and into // a queue, which we pick up with a third "mediator" thread, which handles calling the IGameMonitor passed // by the caller. var proc = RunCommand(authSession, monitor); Queue<GameMessage> messages = new Queue<GameMessage>(); var waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset); var stdErrThread = new Thread(GenerateGameMessagePumper(proc.StandardError, GameMessageType.Error, messages, waitHandle)); var stdOutThread = new Thread(GenerateGameMessagePumper(proc.StandardOutput, GameMessageType.Output, messages, waitHandle)); var monitorThread = new Thread(delegate (object state) { while (true) { waitHandle.WaitOne(new TimeSpan(0, 0, 0, 0, 100)); if (proc.HasExited && !stdErrThread.IsAlive && !stdOutThread.IsAlive) { Console.WriteLine("Minecraft appears to have exited ({0}) and all thread activity is completed.", proc.ExitCode); break; } lock (messages) { if (messages.Count == 0) continue; // Call the monitor to report this output line. // Note that this occurs in OUR thread. You, as the receiver of this // event, need to make sure you perform operations therein in the proper // thread. Learn to use Queues and always lock em. int max = Math.Min(100, messages.Count); for (int i = 0; i < max; ++i) { var msg = messages.Dequeue(); Console.WriteLine(msg.Line); monitor.OutputLine(msg.Type, msg.Line); } } } // Game has ended monitor.GameEnded(proc.ExitCode); }); Console.WriteLine("Starting monitor threads..."); stdErrThread.Name = "Minecraft-StdErr"; stdErrThread.IsBackground = true; stdErrThread.Start(); stdOutThread.Name = "Minecraft-StdOut"; stdOutThread.IsBackground = true; stdOutThread.Start(); monitorThread.Name = "Minecraft-Monitor"; monitorThread.IsBackground = true; monitorThread.Start(); Console.WriteLine("Game has started, returning control to UI..."); return proc; }