1 #region Using Statements
5 using System.Diagnostics;
9 using System.Threading;
13 using System.Runtime.InteropServices;
23 public static class Program
29 [DllImport(
"linux_steam_env", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
30 private static extern void setLinuxEnv();
33 public static bool TryStartChildServerRelay(
string[] commandLineArgs)
35 for (
int i = 0; i < commandLineArgs.Length; i++)
37 switch (commandLineArgs[i].Trim())
40 ChildServerRelay.Start(commandLineArgs[i + 2], commandLineArgs[i + 1]);
51 static void Main(
string[] args)
53 AppDomain currentDomain = AppDomain.CurrentDomain;
54 currentDomain.ProcessExit += OnProcessExit;
56 currentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CrashHandler);
58 TryStartChildServerRelay(args);
62 AppDomain.CurrentDomain.ProcessExit += (s, e) =>
64 GameMain.ShouldRun =
false;
67 Console.WriteLine(
"Barotrauma Dedicated Server " + GameMain.Version +
68 " (" + AssemblyInfo.BuildString +
", branch " + AssemblyInfo.GitBranch +
", revision " + AssemblyInfo.GitRevision +
")");
71 Console.WriteLine(
"Output redirection detected; colored text and command input will be disabled.");
75 Console.WriteLine(
"Redirected input is detected but is not supported by this application. Input will be ignored.");
78 string executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
79 if (!File.Exists(Path.Combine(executableDir,
"workshop.txt")))
81 Directory.SetCurrentDirectory(executableDir);
84 DebugConsoleCore.Init(
85 newMessage: (s, c) => DebugConsole.NewMessage(s, c),
86 log: DebugConsole.Log);
87 Game =
new GameMain(args);
93 private static bool hasShutDown =
false;
94 private static void ShutDown()
96 if (hasShutDown) {
return; }
99 if (GameAnalyticsManager.SendUserStatistics) { GameAnalyticsManager.ShutDown(); }
100 SteamManager.ShutDown();
103 EosInterface.Core.CleanupAndQuit();
104 while (EosInterface.Core.IsInitialized)
106 EosInterface.Core.Update();
112 private static void OnProcessExit(
object sender, EventArgs e)
117 static GameMain Game;
119 private static void NotifyCrash(
string reportFilePath, Exception e)
121 string errorMsg = $
"{reportFilePath}||\n{e.Message} ({e.GetType().Name}) {e.StackTrace}";
122 if (e.InnerException !=
null)
124 var innerMost = e.GetInnermost();
125 errorMsg += $
"\nInner exception: {innerMost.Message} ({innerMost.GetType().Name}) {e.StackTrace}";
127 if (errorMsg.Length > ushort.MaxValue) { errorMsg = errorMsg[..ushort.MaxValue]; }
128 ChildServerRelay.NotifyCrash(errorMsg);
129 GameMain.Server?.NotifyCrash();
132 private static void CrashHandler(
object sender, UnhandledExceptionEventArgs args)
134 static void swallowExceptions(Action action)
146 Exception unhandledException = args.ExceptionObject as Exception;
147 string reportFilePath =
"";
150 reportFilePath =
"servercrashreport.log";
151 CrashDump(ref reportFilePath, unhandledException);
153 catch (Exception exceptionHandlerError)
155 Debug.WriteLine(exceptionHandlerError.Message);
156 string slimCrashReport =
"Exception handler failed: " + exceptionHandlerError.Message +
"\n" + exceptionHandlerError.StackTrace;
157 if (unhandledException !=
null)
159 slimCrashReport +=
"\n\nInitial exception: " + unhandledException.Message +
"\n" + unhandledException.StackTrace;
161 File.WriteAllText(
"servercrashreportslim.log", slimCrashReport);
164 swallowExceptions(() => NotifyCrash(reportFilePath, unhandledException));
165 swallowExceptions(() => Game?.Exit());
168 static void CrashDump(ref
string filePath, Exception exception)
172 GameMain.Server?.ServerSettings?.SaveSettings();
173 GameMain.Server?.ServerSettings?.BanList.Save();
174 if (GameMain.Server?.ServerSettings?.KarmaPreset ==
"custom")
176 GameMain.Server?.KarmaManager?.SaveCustomPreset();
177 GameMain.Server?.KarmaManager?.Save();
185 int existingFiles = 0;
186 string originalFilePath = filePath;
187 while (File.Exists(filePath))
190 filePath = Path.GetFileNameWithoutExtension(originalFilePath) +
" (" + (existingFiles + 1) +
")" + Path.GetExtension(originalFilePath);
193 StringBuilder sb =
new StringBuilder();
194 sb.AppendLine(
"Barotrauma Dedicated Server crash report (generated on " + DateTime.Now +
")");
196 sb.AppendLine(
"Barotrauma seems to have crashed. Sorry for the inconvenience! ");
198 sb.AppendLine(
"Game version " + GameMain.Version +
" (" + AssemblyInfo.BuildString +
", branch " + AssemblyInfo.GitBranch +
", revision " + AssemblyInfo.GitRevision +
")");
199 sb.AppendLine(
"Language: " + GameSettings.CurrentConfig.Language);
200 if (ContentPackageManager.EnabledPackages.All !=
null)
202 sb.AppendLine(
"Selected content packages: " +
203 (!ContentPackageManager.EnabledPackages.All.Any() ?
205 string.Join(
", ", ContentPackageManager.EnabledPackages.All.Select(c => $
"{c.Name} ({c.Hash?.ShortRepresentation ?? "unknown
"})"))));
207 sb.AppendLine(
"Level seed: " + ((Level.Loaded ==
null) ?
"no level loaded" : Level.Loaded.Seed));
208 sb.AppendLine(
"Loaded submarine: " + ((
Submarine.MainSub ==
null) ?
"None" :
Submarine.MainSub.Info.Name +
" (" +
Submarine.MainSub.Info.MD5Hash +
")"));
209 sb.AppendLine(
"Selected screen: " + (Screen.Selected ==
null ?
"None" : Screen.Selected.ToString()));
211 if (GameMain.Server !=
null)
213 sb.AppendLine(
"Server (" + (GameMain.Server.GameStarted ?
"Round had started)" :
"Round hadn't been started)"));
217 sb.AppendLine(
"System info:");
218 sb.AppendLine(
" Operating system: " + System.Environment.OSVersion + (System.Environment.Is64BitOperatingSystem ?
" 64 bit" :
" x86"));
220 sb.AppendLine(
"Exception: " + exception.Message +
" (" + exception.GetType().ToString() +
")");
221 sb.AppendLine(
"Target site: " +exception.TargetSite.ToString());
222 if (exception.StackTrace !=
null)
224 sb.AppendLine(
"Stack trace: ");
225 sb.AppendLine(exception.StackTrace.CleanupStackTrace());
229 if (exception.InnerException !=
null)
231 sb.AppendLine(
"InnerException: " + exception.InnerException.Message);
232 if (exception.InnerException.TargetSite !=
null)
234 sb.AppendLine(
"Target site: " + exception.InnerException.TargetSite.ToString());
236 if (exception.InnerException.StackTrace !=
null)
238 sb.AppendLine(
"Stack trace: ");
239 sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace());
243 if (GameAnalyticsManager.SendUserStatistics)
246 GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Critical, sb.ToString());
247 GameAnalyticsManager.ShutDown();
250 sb.AppendLine(
"Last debug messages:");
251 DebugConsole.Clear();
252 for (
int i = DebugConsole.Messages.Count - 1; i > 0 && i > DebugConsole.Messages.Count - 15; i--)
254 sb.AppendLine(
" " + DebugConsole.Messages[i].Time +
" - " + DebugConsole.Messages[i].Text);
257 string crashReport = sb.ToString();
259 if (!
Console.IsOutputRedirected)
261 Console.ForegroundColor = ConsoleColor.Red;
265 File.WriteAllText(filePath, sb.ToString());
267 if (GameSettings.CurrentConfig.SaveDebugConsoleLogs
268 || GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.SaveLogs(); }
270 if (GameAnalyticsManager.SendUserStatistics)
272 Console.Write(
"A crash report (\"servercrashreport.log\") was saved in the root folder of the game and sent to the developers.");
276 Console.Write(
"A crash report(\"servercrashreport.log\") was saved in the root folder of the game. The error was not sent to the developers because user statistics have been disabled, but" +
277 " if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/");
279 SteamManager.ShutDown();