1 #region Using Statements
8 using System.Diagnostics;
9 using System.Runtime.InteropServices;
10 using System.Xml.Linq;
24 public static class Program
31 [DllImport(
"linux_steam_env", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
32 private static extern void setLinuxEnv();
39 static void Main(
string[] args)
41 string executableDir =
"";
44 AppDomain currentDomain = AppDomain.CurrentDomain;
45 currentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(CrashHandler);
53 executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location);
54 Directory.SetCurrentDirectory(executableDir);
55 DebugConsoleCore.Init(
56 newMessage: (s, c) => DebugConsole.NewMessage(s, c),
57 log: DebugConsole.Log);
58 StoreIntegration.Init(ref args);
60 Game =
new GameMain(args);
65 CrossThread.ProcessTasks();
68 private static GameMain Game;
70 private static void CrashHandler(
object sender, UnhandledExceptionEventArgs args)
72 Exception unhandledException = args.ExceptionObject as Exception;
76 CrashDump(Game,
"crashreport.log", unhandledException);
79 catch (Exception exceptionHandlerError)
81 Debug.WriteLine(exceptionHandlerError.Message);
82 string slimCrashReport =
"Exception handler failed: " + exceptionHandlerError.Message +
"\n" + exceptionHandlerError.StackTrace;
83 if (unhandledException !=
null)
85 slimCrashReport +=
"\n\nInitial exception: " + unhandledException.Message +
"\n" + unhandledException.StackTrace;
87 File.WriteAllText(
"crashreportslim.log", slimCrashReport);
93 public static void CrashMessageBox(
string message,
string filePath)
95 Microsoft.Xna.Framework.MessageBox.ShowWrapped(Microsoft.Xna.Framework.MessageBox.Flags.Error,
"Oops! Barotrauma just crashed.", message);
98 if (!
string.IsNullOrWhiteSpace(filePath)) { ToolBox.OpenFileWithShell(filePath); }
101 static void CrashDump(GameMain game,
string filePath, Exception exception)
103 int existingFiles = 0;
104 string originalFilePath = filePath;
105 while (File.Exists(filePath))
108 filePath = Path.GetFileNameWithoutExtension(originalFilePath) +
" (" + (existingFiles + 1) +
")" + Path.GetExtension(originalFilePath);
111 DebugConsole.DequeueMessages();
113 Md5Hash exeHash =
null;
116 string exePath = System.Reflection.Assembly.GetEntryAssembly().Location;
117 exeHash = Md5Hash.CalculateForFile(exePath, Md5Hash.StringHashOptions.BytePerfect);
124 StringBuilder sb =
new StringBuilder();
126 sb.AppendLine(
"Barotrauma Client crash report (generated on " + DateTime.Now +
")");
128 sb.AppendLine(
"Barotrauma seems to have crashed. Sorry for the inconvenience! ");
131 string dxgiErrorHelpText =
133 GetDXGIErrorHelpText(game, exception);
137 if (!
string.IsNullOrEmpty(dxgiErrorHelpText))
139 sb.AppendLine(dxgiErrorHelpText);
145 if (exception.StackTrace.Contains(
"Barotrauma.GameMain.Load"))
149 XDocument doc = XMLExtensions.TryLoadXml(GameSettings.PlayerConfigPath);
150 if (doc?.Root !=
null)
152 XElement newElement =
new XElement(doc.Root.Name);
153 newElement.Add(doc.Root.Attributes());
154 Identifier[] contentPackageTags = {
"contentpackage".ToIdentifier(),
"contentpackages".ToIdentifier() };
155 newElement.Add(doc.Root.Elements().Where(e => !contentPackageTags.Contains(e.NameAsIdentifier())));
156 newElement.Add(
new XElement(
"core",
157 new XAttribute(
"path", ContentPackageManager.VanillaFileList)));
158 XDocument newDoc =
new XDocument(newElement);
159 newDoc.Save(GameSettings.PlayerConfigPath);
160 sb.AppendLine(
"To prevent further startup errors, installed mods will be disabled the next time you launch the game.");
170 if (exeHash?.StringRepresentation !=
null)
172 sb.AppendLine(exeHash.StringRepresentation);
175 sb.AppendLine(
"Game version " + GameMain.Version +
176 " (" + AssemblyInfo.BuildString +
", branch " + AssemblyInfo.GitBranch +
", revision " + AssemblyInfo.GitRevision +
")");
177 sb.AppendLine($
"Graphics mode: {GameSettings.CurrentConfig.Graphics.Width}x{GameSettings.CurrentConfig.Graphics.Height} ({GameSettings.CurrentConfig.Graphics.DisplayMode})");
178 sb.AppendLine(
"VSync " + (GameSettings.CurrentConfig.Graphics.VSync ?
"ON" :
"OFF"));
179 sb.AppendLine(
"Language: " + GameSettings.CurrentConfig.Language);
180 if (ContentPackageManager.EnabledPackages.All !=
null)
182 sb.AppendLine(
"Selected content packages: " +
183 (!ContentPackageManager.EnabledPackages.All.Any() ?
185 string.Join(
", ", ContentPackageManager.EnabledPackages.All.Select(c => $
"{c.Name} ({c.Hash?.ShortRepresentation ?? "unknown
"})"))));
187 sb.AppendLine(
"Level seed: " + ((Level.Loaded ==
null) ?
"no level loaded" : Level.Loaded.Seed));
188 sb.AppendLine(
"Loaded submarine: " + ((
Submarine.MainSub ==
null) ?
"None" :
Submarine.MainSub.Info.Name +
" (" +
Submarine.MainSub.Info.MD5Hash +
")"));
189 sb.AppendLine(
"Selected screen: " + (Screen.Selected ==
null ?
"None" : Screen.Selected.ToString()));
190 if (SteamManager.IsInitialized)
192 sb.AppendLine(
"SteamManager initialized");
194 else if (EosInterface.IdQueries.IsLoggedIntoEosConnect)
196 sb.AppendLine(
"Logged in to EOS connect");
199 if (GameMain.Client !=
null)
201 sb.AppendLine(
"Client (" + (GameMain.Client.GameStarted ?
"Round had started)" :
"Round hadn't been started)"));
205 sb.AppendLine(
"System info:");
206 sb.AppendLine(
" Operating system: " + System.Environment.OSVersion + (System.Environment.Is64BitOperatingSystem ?
" 64 bit" :
" x86"));
210 sb.AppendLine(
" Game not initialized");
214 if (game.GraphicsDevice ==
null)
216 sb.AppendLine(
" Graphics device not set");
220 if (game.GraphicsDevice.Adapter ==
null)
222 sb.AppendLine(
" Graphics adapter not set");
226 sb.AppendLine(
" GPU name: " + game.GraphicsDevice.Adapter.Description);
227 sb.AppendLine(
" Display mode: " + game.GraphicsDevice.Adapter.CurrentDisplayMode);
230 sb.AppendLine(
" GPU status: " + game.GraphicsDevice.GraphicsDeviceStatus);
235 sb.AppendLine($
"Exception: {exception.Message} ({exception.GetType()})");
237 if (exception is SharpDXException sharpDxException && ((uint)sharpDxException.HResult) == 0x887A0005)
239 var dxDevice = (SharpDX.Direct3D11.Device)game.GraphicsDevice.Handle;
240 var descriptor = ResultDescriptor.Find(dxDevice.DeviceRemovedReason)?.ApiCode ??
"UNKNOWN";
241 sb.AppendLine($
"Device removed reason: {descriptor} ({dxDevice.DeviceRemovedReason})");
244 if (exception.TargetSite !=
null)
246 sb.AppendLine(
"Target site: " + exception.TargetSite.ToString());
249 if (exception.StackTrace !=
null)
251 sb.AppendLine(
"Stack trace: ");
252 sb.AppendLine(exception.StackTrace.CleanupStackTrace());
256 if (exception.InnerException !=
null)
258 sb.AppendLine(
"InnerException: " + exception.InnerException.Message);
259 if (exception.InnerException.TargetSite !=
null)
261 sb.AppendLine(
"Target site: " + exception.InnerException.TargetSite.ToString());
263 if (exception.InnerException.StackTrace !=
null)
265 sb.AppendLine(
"Stack trace: ");
266 sb.AppendLine(exception.InnerException.StackTrace.CleanupStackTrace());
270 if (GameAnalyticsManager.SendUserStatistics)
273 string crashHeader = exception.Message;
274 if (exception.TargetSite !=
null)
276 crashHeader +=
" " + exception.TargetSite.ToString();
280 GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Critical, crashHeader);
281 GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Critical, crashHeader +
"\n\n" + sb.ToString());
282 GameAnalyticsManager.ShutDown();
285 sb.AppendLine(
"Last debug messages:");
286 for (
int i = DebugConsole.Messages.Count - 1; i >= 0; i--)
288 sb.AppendLine(
"[" + DebugConsole.Messages[i].Time +
"] " + DebugConsole.Messages[i].Text);
291 string crashReport = sb.ToString();
293 File.WriteAllText(filePath, crashReport);
295 if (GameSettings.CurrentConfig.SaveDebugConsoleLogs
296 || GameSettings.CurrentConfig.VerboseLogging) { DebugConsole.SaveLogs(); }
298 string msg =
string.Empty;
299 if (GameAnalyticsManager.SendUserStatistics)
301 msg =
"A crash report (\"" + filePath +
"\") was saved in the root folder of the game and sent to the developers.";
305 msg =
"A crash report (\"" + filePath +
"\") was saved in the root folder of the game. The error was not sent to the developers because user statistics have been disabled, but" +
306 " 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/";
308 if (
string.IsNullOrEmpty(dxgiErrorHelpText))
310 msg +=
"\n\n" + dxgiErrorHelpText;
312 CrashMessageBox(msg, filePath);
316 private static string GetDXGIErrorHelpText(GameMain game, Exception exception)
318 string text =
string.Empty;
319 if (exception is SharpDXException sharpDxException && ((uint)sharpDxException.HResult) == 0x887A0005)
321 var dxDevice = (SharpDX.Direct3D11.Device)game.GraphicsDevice.Handle;
322 var descriptor = ResultDescriptor.Find(dxDevice.DeviceRemovedReason)?.ApiCode ??
"UNKNOWN";
325 $
"The crash was caused by the DirectX error {descriptor} ({dxDevice.DeviceRemovedReason}). " +
326 "This is a common DirectX error that can be related to various different issues, such as outdated drivers, RAM problems or an overclocked or otherwise overstressed GPU. " +
327 "There are several potential ways to fix the issue: ensuring your graphics drivers and DirectX installation are up-to-date, disabling overclocking and adjusting various GPU-specific settings. " +
328 $
"You may also be able to find potential solutions to the problem by using the error code {descriptor} ({dxDevice.DeviceRemovedReason}) and your GPU manufacturer as search terms.";
334 private static IntPtr nvApi64Dll = IntPtr.Zero;
335 private static void EnableNvOptimus()
340 if (NativeLibrary.TryLoad(
"nvapi64.dll", out nvApi64Dll))
342 DebugConsole.Log(
"Loaded nvapi64.dll successfully");
347 private static void FreeNvOptimus()
349 #warning TODO: determine if we can do this safely