// static public static MinecraftConfiguration FromFile(string filename) { var config = new MinecraftConfiguration(filename); var lines = File.ReadAllLines(filename); for (int i = 0; i < lines.Length; ++i) { lines[i] = lines[i].Trim(); if (!lines[i].StartsWith("#") && lines[i].Length > 0) { var pos = lines[i].IndexOf('='); if (pos != -1) { var key = lines[i].Substring(0, pos).Trim(); var value = lines[i].Substring(pos + 1).Trim(); if (key.Length > 0) { config._properties.Add(key, value); } } } } //config._properties return(config); }
private void WorkerThread() { if (!File.Exists(_package.Filename)) { _logger.Write( LogType.Error, $"Failed to start instance. Physical package file '{_package.Name}' ('{_package.Filename}') not found."); Exception = new FileNotFoundException("The package file does not exist.", _package.Filename); SetStatus(InstanceStatus.Error); return; } SetStatus(InstanceStatus.Starting); var instanceDirectory = Path.Combine(Environment.CurrentDirectory, "instances", _id); Directory.CreateDirectory(instanceDirectory); // extract instance into a new folder and so forth try { using (var stream = File.Open(_package.Filename, FileMode.Open, FileAccess.Read, FileShare.Read)) using (var archive = new ZipArchive(stream)) { archive.ExtractToDirectory(instanceDirectory); } } catch (Exception ex) { Directory.Delete(instanceDirectory); _logger.Write( LogType.Error, $"Failed to start instance. Exception extracting package '{_package.Name}' ('{_package.Filename}') => ({ex.GetType().Name}) {ex.Message}"); Exception = ex; SetStatus(InstanceStatus.Error); return; } // always set the eula to true regardless of the package File.WriteAllText(Path.Combine(instanceDirectory, "eula.txt"), "eula=true\r\n"); // Configure the instance based on given settings var minecraftConfig = MinecraftConfiguration.FromFile(Path.Combine(instanceDirectory, "server.properties")); minecraftConfig.SetValue("snooper-enabled", "false"); // don't send snoop data to snoop.minecraft.net foreach (var extraConfiguration in Configuration.ExtraConfigurationValues) { minecraftConfig.SetValue(extraConfiguration.Key, extraConfiguration.Value); } minecraftConfig.SetValue("motd", Configuration.Motd); minecraftConfig.SetValue("enable-command-block", Configuration.EnableCommandBlocks); minecraftConfig.SetValue("max-players", Configuration.MaxPlayers); minecraftConfig.SetValue("announce-player-achievements", Configuration.AnnouncePlayerAchievements); // network minecraftConfig.SetValue("server-ip", _bindingInterface.IP != "0.0.0.0" ? _bindingInterface.IP : ""); minecraftConfig.SetValue("server-port", _bindingInterface.Port); minecraftConfig.Save(); try { // start process var psi = new ProcessStartInfo( Configuration.JavaExecutable, $"-Xmx{Configuration.JavaMaximumMemoryMegabytes}M -Xms{Configuration.JavaInitialMemoryMegabytes}M -jar \"{Configuration.MinecraftJarFilename}\" nogui"); psi.UseShellExecute = false; psi.RedirectStandardInput = true; psi.RedirectStandardOutput = true; psi.CreateNoWindow = true; psi.WorkingDirectory = instanceDirectory; Process process; try { process = Process.Start(psi); if (process == null) { throw new Exception("Process.Start returned null"); } } catch (Exception ex) { _logger.Write(LogType.Error, $"Exception in starting process: ({ex.GetType().Name}) {ex.Message}"); _logger.Write(LogType.Error, $"- Filename: {psi.FileName}"); _logger.Write(LogType.Error, $"- Arguments: {psi.Arguments}"); _logger.Write(LogType.Error, $"- Directory: {psi.WorkingDirectory}"); Exception = ex; SetStatus(InstanceStatus.Error); return; } _logger.Write(LogType.Notice, $"Started instance {_id}"); var timeMatch = new Regex(@"^\s*\[\d+:\d+(:\d+)?\]\s*"); var prefixMatch = new Regex(@"^\s*\[.*?\]:\s*"); // [Server thread/INFO]: etc.. using (process) { DateTime?stopTime = null; process.OutputDataReceived += (sender, e) => { if (e.Data != null) { string msg; var match = timeMatch.Match(e.Data); if (match.Success) { msg = e.Data.Substring(match.Index + match.Length); } else { msg = e.Data; } if ((match = prefixMatch.Match(msg)).Success) { msg = msg.Substring(match.Index + match.Length); } InstanceLogManager.AddLog(_id, msg); _logger.Write(LogType.Normal, "[MC] " + msg); foreach (var listener in _instanceManager._instanceEventListeners) { listener.OnInstanceLog(_id, DateTime.UtcNow, msg); } if (msg.StartsWith("Done (")) { SetStatus(InstanceStatus.Running); } } }; process.BeginOutputReadLine(); // set process? bool sentShutdown = false; var waitEvents = new WaitHandle[] { _executeCommandEvent, _shutdownEvent, _terminateEvent }; while (!process.HasExited) { if (!sentShutdown && stopTime.HasValue && DateTime.UtcNow >= stopTime) { sentShutdown = true; _logger.Write(LogType.Warning, "Sending stop command ..."); process.StandardInput.WriteLine("stop"); SetStatus(InstanceStatus.Stopping); } int n = WaitHandle.WaitAny(waitEvents, 100); // WaitHandle.WaitTimeout on timeout ... if (n == 0) //_executeCommandEvent.WaitOne(100)) { LinkedList <string> commands; lock (_commandLock) { commands = _commandQueue; _commandQueue = new LinkedList <string>(); } foreach (var command in commands) { process.StandardInput.WriteLine(command); } } else if (n == 1) // gracefully shutdown { if (!stopTime.HasValue) { stopTime = DateTime.UtcNow.AddSeconds(1); } } else if (n == 2) // terminate process { // terminate _logger.Write(LogType.Warning, $"Instance {_id} forcefully killed."); process.Kill(); } } process.CancelOutputRead(); } _logger.Write(LogType.Notice, $"Instance {_id} stopped"); SetStatus(InstanceStatus.Stopped); } catch (Exception ex) { _logger.Write(LogType.Error, $"Instance loop exception detected: ({ex.GetType().Name}) {ex.Message}"); Exception = ex; SetStatus(InstanceStatus.Error); } InstanceLogManager.Remove(_id); // delete instance while (true) { try { Directory.Delete(instanceDirectory, true); break; } catch { Thread.Sleep(500); } } }