Client LuaCsForBarotrauma
GameSettings.cs
1 #nullable enable
3 using Microsoft.Xna.Framework;
4 using System;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
7 using System.Globalization;
8 using System.Linq;
9 using System.Xml.Linq;
10 using Barotrauma.IO;
11 #if CLIENT
14 using Microsoft.Xna.Framework.Input;
15 #endif
16 
17 namespace Barotrauma
18 {
19  public enum WindowMode
20  {
22  }
23 
24  public enum LosMode
25  {
26  None = 0,
27  Transparent = 1,
28  Opaque = 2
29  }
30 
31  public enum VoiceMode
32  {
33  Disabled,
34  PushToTalk,
35  Activity
36  }
37 
38  public enum EnemyHealthBarMode
39  {
40  ShowAll,
42  HideAll
43  }
44 
46  {
47  Everything,
50  }
51 
52  public static class GameSettings
53  {
54  public struct Config
55  {
56  public const float DefaultAimAssist = 0.05f;
57 
58  public static Config GetDefault()
59  {
60  Config config = new Config
61  {
62 #if SERVER
63  //server defaults to English, clients get a prompt to select a language
64  Language = TextManager.DefaultLanguage,
65 #else
67 #endif
69  MaxAutoSaves = 8,
71  SubEditorBackground = new Color(13, 37, 69, 255),
72  EnableSplashScreen = true,
73  PauseOnFocusLost = true,
74  RemoteMainMenuContentUrl = "https://www.barotraumagame.com/gamedata/",
77  ChatSpeechBubbles = true,
79  EnableMouseLook = true,
80  ChatOpen = true,
81  CrewMenuOpen = true,
83  TutorialSkipWarning = true,
84  CorpseDespawnDelay = 600,
86 #if OSX
87  UseDualModeSockets = false,
88 #else
89  UseDualModeSockets = true,
90 #endif
91  DisableInGameHints = false,
95 #if CLIENT
96  CrossplayChoice = Eos.EosSteamPrimaryLogin.CrossplayChoice.Unknown,
97  DisableGlobalSpamList = false,
100 #endif
101 
102  };
103 #if DEBUG
104  config.QuickStartSub = "Humpback".ToIdentifier();
105  config.AutomaticQuickStartEnabled = false;
106  config.AutomaticCampaignLoadEnabled = false;
107  config.TextManagerDebugModeEnabled = false;
108  config.ModBreakerMode = false;
109 #endif
110  return config;
111  }
112 
113  public static Config FromElement(XElement element, in Config? fallback = null)
114  {
115  Config retVal = fallback ?? GetDefault();
116 
117  retVal.DeserializeElement(element);
118 #if SERVER
119  //server defaults to English, clients get a prompt to select a language
120  if (retVal.Language == LanguageIdentifier.None)
121  {
122  retVal.Language = TextManager.DefaultLanguage;
123  }
124 #endif
125  //RemoteMainMenuContentUrl gets set to default it left empty - lets allow leaving it empty to make it possible to disable the remote content
126  if (element.GetAttribute("RemoteMainMenuContentUrl")?.Value == string.Empty)
127  {
128  retVal.RemoteMainMenuContentUrl = string.Empty;
129  }
130  retVal.Graphics = GraphicsSettings.FromElements(element.GetChildElements("graphicsmode", "graphicssettings"), retVal.Graphics);
131  retVal.Audio = AudioSettings.FromElements(element.GetChildElements("audio"), retVal.Audio);
132 #if CLIENT
133  retVal.KeyMap = new KeyMapping(element.GetChildElements("keymapping"), retVal.KeyMap);
134  retVal.InventoryKeyMap = new InventoryKeyMapping(element.GetChildElements("inventorykeymapping"), retVal.InventoryKeyMap);
135  retVal.SavedCampaignSettings = element.GetChildElement("campaignsettings");
136  LoadSubEditorImages(element);
137 #endif
138 
139  return retVal;
140  }
141 
143  public bool VerboseLogging;
144  public bool SaveDebugConsoleLogs;
145  public string SavePath;
147  public int MaxAutoSaves;
149  public Color SubEditorBackground;
150  public bool EnableSplashScreen;
151  public bool PauseOnFocusLost;
152  public float AimAssistAmount;
153  public bool EnableMouseLook;
155  public bool ChatSpeechBubbles;
157  public bool ChatOpen;
158  public bool CrewMenuOpen;
160  public bool TutorialSkipWarning;
161  public int CorpseDespawnDelay;
163  public bool UseDualModeSockets;
164  public bool DisableInGameHints;
166  public Identifier QuickStartSub;
168 #if CLIENT
169  public Eos.EosSteamPrimaryLogin.CrossplayChoice CrossplayChoice;
170  public XElement SavedCampaignSettings;
172 #endif
173 #if DEBUG
174  public bool AutomaticQuickStartEnabled;
175  public bool AutomaticCampaignLoadEnabled;
176  public bool TestScreenEnabled;
177  public bool TextManagerDebugModeEnabled;
178  public bool ModBreakerMode;
179 #endif
180 
181  public struct GraphicsSettings
182  {
183  public static readonly Point MinSupportedResolution = new Point(1024, 540);
184 
185  public static GraphicsSettings GetDefault()
186  {
187  GraphicsSettings gfxSettings = new GraphicsSettings
188  {
189  RadialDistortion = true,
190  InventoryScale = 1.0f,
191  LightMapScale = 1.0f,
192  VisibleLightLimit = 50,
193  TextScale = 1.0f,
194  HUDScale = 1.0f,
195  Specularity = true,
196  ChromaticAberration = true,
197  ParticleLimit = 1500,
198  LosMode = LosMode.Transparent
199  };
200  gfxSettings.RadialDistortion = true;
201  gfxSettings.CompressTextures = true;
202  gfxSettings.FrameLimit = 300;
203  gfxSettings.VSync = true;
204 #if DEBUG
205  gfxSettings.DisplayMode = WindowMode.Windowed;
206 #else
207  gfxSettings.DisplayMode = WindowMode.BorderlessWindowed;
208 #endif
209  return gfxSettings;
210  }
211 
212  public static GraphicsSettings FromElements(IEnumerable<XElement> elements, in GraphicsSettings? fallback = null)
213  {
214  GraphicsSettings retVal = fallback ?? GetDefault();
215  elements.ForEach(element => retVal.DeserializeElement(element));
216  return retVal;
217  }
218 
219  public int Width;
220  public int Height;
221  public bool VSync;
222  public bool CompressTextures;
223  public int FrameLimit;
225  public int ParticleLimit;
226  public bool Specularity;
227  public bool ChromaticAberration;
228  public LosMode LosMode;
229  public float HUDScale;
230  public float InventoryScale;
231  public float LightMapScale;
232  public int VisibleLightLimit;
233  public float TextScale;
234  public bool RadialDistortion;
235  }
236 
237  [StructSerialization.Skip]
239 
240  public struct AudioSettings
241  {
242  public static class DeviceNameHandler
243  {
244  public static string Read(string s)
245  => System.Xml.XmlConvert.DecodeName(s)!;
246 
247  public static string Write(string s)
248  => System.Xml.XmlConvert.EncodeName(s)!;
249  }
250 
251  public static AudioSettings GetDefault()
252  {
253  AudioSettings audioSettings = new AudioSettings
254  {
255  MusicVolume = 0.3f,
256  SoundVolume = 0.5f,
257  UiVolume = 0.3f,
258  VoiceChatVolume = 0.5f,
260  MicrophoneVolume = 5,
261  MuteOnFocusLost = false,
264  VoipAttenuationEnabled = true,
265  VoiceSetting = VoiceMode.PushToTalk,
267  };
268  return audioSettings;
269  }
270 
271  public static AudioSettings FromElements(IEnumerable<XElement> elements, in AudioSettings? fallback = null)
272  {
273  AudioSettings retVal = fallback ?? GetDefault();
274  elements.ForEach(element => retVal.DeserializeElement(element));
275  return retVal;
276  }
277 
278  public float MusicVolume;
279  public float SoundVolume;
280  public float UiVolume;
281  public float VoiceChatVolume;
283  public float MicrophoneVolume;
284  public bool MuteOnFocusLost;
289 
290  [StructSerialization.Handler(typeof(DeviceNameHandler))]
291  public string AudioOutputDevice;
292  [StructSerialization.Handler(typeof(DeviceNameHandler))]
293  public string VoiceCaptureDevice;
294 
295  public float NoiseGateThreshold;
297  }
298 
299  [StructSerialization.Skip]
301 
302 #if CLIENT
303  public struct KeyMapping
304  {
305  private readonly static ImmutableDictionary<InputType, KeyOrMouse> DefaultsQwerty =
306  new Dictionary<InputType, KeyOrMouse>()
307  {
308  { InputType.Run, Keys.LeftShift },
309  { InputType.Attack, Keys.R },
310  { InputType.Crouch, Keys.LeftControl },
311  { InputType.Grab, Keys.G },
312  { InputType.Health, Keys.H },
313  { InputType.Ragdoll, Keys.Space },
314  { InputType.Aim, MouseButton.SecondaryMouse },
315  { InputType.DropItem, Keys.None },
316 
317  { InputType.InfoTab, Keys.Tab },
318  { InputType.Chat, Keys.None },
319  { InputType.RadioChat, Keys.None },
320  { InputType.ActiveChat, Keys.T },
321  { InputType.CrewOrders, Keys.C },
322  { InputType.ChatBox, Keys.B },
323 
324  { InputType.Voice, Keys.V },
325  { InputType.RadioVoice, Keys.None },
326  { InputType.LocalVoice, Keys.None },
327  { InputType.ToggleChatMode, Keys.R },
328  { InputType.Command, MouseButton.MiddleMouse },
329  { InputType.ContextualCommand, Keys.LeftShift },
330  { InputType.PreviousFireMode, MouseButton.MouseWheelDown },
331  { InputType.NextFireMode, MouseButton.MouseWheelUp },
332 
333  { InputType.TakeHalfFromInventorySlot, Keys.LeftShift },
334  { InputType.TakeOneFromInventorySlot, Keys.LeftControl },
335 
336  { InputType.Up, Keys.W },
337  { InputType.Down, Keys.S },
338  { InputType.Left, Keys.A },
339  { InputType.Right, Keys.D },
340  { InputType.ToggleInventory, Keys.Q },
341 
342  { InputType.SelectNextCharacter, Keys.Z },
343  { InputType.SelectPreviousCharacter, Keys.X },
344 
345  { InputType.Use, Keys.E },
346  { InputType.Select, MouseButton.PrimaryMouse },
347  { InputType.Deselect, MouseButton.SecondaryMouse },
348  { InputType.Shoot, MouseButton.PrimaryMouse },
349  { InputType.ShowInteractionLabels, Keys.LeftAlt }
350  }.ToImmutableDictionary();
351 
352  public static KeyMapping GetDefault() => new KeyMapping
353  {
354  Bindings = DefaultsQwerty
355  .Select(kvp =>
356  (kvp.Key, kvp.Value.MouseButton == MouseButton.None
357  ? (KeyOrMouse)Keyboard.QwertyToCurrentLayout(kvp.Value.Key)
358  : (KeyOrMouse)kvp.Value.MouseButton))
359  .ToImmutableDictionary()
360  };
361 
362  public KeyMapping(IEnumerable<XElement> elements, in KeyMapping? fallback)
363  {
364  var defaultBindings = GetDefault().Bindings;
365  Dictionary<InputType, KeyOrMouse> bindings = fallback?.Bindings?.ToMutable() ?? defaultBindings.ToMutable();
366  foreach (InputType inputType in (InputType[])Enum.GetValues(typeof(InputType)))
367  {
368  if (!bindings.ContainsKey(inputType))
369  {
370  bindings.Add(inputType, defaultBindings[inputType]);
371  }
372  }
373 
374  Dictionary<InputType, KeyOrMouse> savedBindings = new Dictionary<InputType, KeyOrMouse>();
375  bool playerConfigContainsNewChatBinds = false;
376  bool playerConfigContainsRestoredVoipBinds = false;
377  foreach (XElement element in elements)
378  {
379  foreach (XAttribute attribute in element.Attributes())
380  {
381  if (Enum.TryParse(attribute.Name.LocalName, out InputType result))
382  {
383  playerConfigContainsNewChatBinds |= result == InputType.ActiveChat;
384  playerConfigContainsRestoredVoipBinds |= result == InputType.RadioVoice;
385  var keyOrMouse = element.GetAttributeKeyOrMouse(attribute.Name.LocalName, bindings[result]);
386  savedBindings.Add(result, keyOrMouse);
387  bindings[result] = keyOrMouse;
388  }
389  }
390  }
391 
392  // Check for duplicate binds when introducing new binds
393  foreach (var defaultBinding in defaultBindings)
394  {
395  if (!IsSetToNone(defaultBinding.Value) && !savedBindings.ContainsKey(defaultBinding.Key))
396  {
397  foreach (var savedBinding in savedBindings)
398  {
399  if (savedBinding.Key is InputType.Run or InputType.TakeHalfFromInventorySlot &&
400  defaultBinding.Key == InputType.ContextualCommand)
401  {
402  //run and contextual commands have always defaulted to Shift, but the latter used to be hard-coded.
403  //don't show a warning about those being bound to the same key
404  continue;
405  }
406  if (savedBinding.Value == defaultBinding.Value)
407  {
408  OnGameMainHasLoaded += () =>
409  {
410  (string, string)[] replacements =
411  {
412  ("[defaultbind]", $"\"{TextManager.Get($"inputtype.{defaultBinding.Key}")}\""),
413  ("[savedbind]", $"\"{TextManager.Get($"inputtype.{savedBinding.Key}")}\""),
414  ("[key]", $"\"{defaultBinding.Value.Name}\"")
415  };
416  new GUIMessageBox(TextManager.Get("warning"), TextManager.GetWithVariables("duplicatebindwarning", replacements));
417  };
418  break;
419  }
420  }
421  }
422 
423  static bool IsSetToNone(KeyOrMouse keyOrMouse) => keyOrMouse == Keys.None && keyOrMouse == MouseButton.None;
424  }
425 
426  // Clear the old chat binds for configs saved before the introduction of the new chat binds
427  if (!playerConfigContainsNewChatBinds)
428  {
429  bindings[InputType.Chat] = Keys.None;
430  bindings[InputType.RadioChat] = Keys.None;
431  }
432 
433  // Clear old VOIP binds to make sure we have no overlapping binds
434  if (!playerConfigContainsRestoredVoipBinds)
435  {
436  bindings[InputType.LocalVoice] = Keys.None;
437  bindings[InputType.RadioVoice] = Keys.None;
438  }
439 
440  Bindings = bindings.ToImmutableDictionary();
441  }
442 
444  {
445  KeyMapping newMapping = this;
446  newMapping.Bindings = newMapping.Bindings
447  .Select(kvp =>
448  kvp.Key == type
449  ? (type, bind)
450  : (kvp.Key, kvp.Value))
451  .ToImmutableDictionary();
452  return newMapping;
453  }
454 
455  public ImmutableDictionary<InputType, KeyOrMouse> Bindings;
456 
457  public LocalizedString KeyBindText(InputType inputType) => Bindings[inputType].Name;
458  }
459 
460  [StructSerialization.Skip]
462 
463  public struct InventoryKeyMapping
464  {
465  public ImmutableArray<KeyOrMouse> Bindings;
466 
468  {
469  Bindings = new KeyOrMouse[]
470  {
471  Keys.D1,
472  Keys.D2,
473  Keys.D3,
474  Keys.D4,
475  Keys.D5,
476  Keys.D6,
477  Keys.D7,
478  Keys.D8,
479  Keys.D9,
480  Keys.D0,
481  }.ToImmutableArray()
482  };
483 
484  public InventoryKeyMapping WithBinding(int index, KeyOrMouse keyOrMouse)
485  {
486  var thisBindings = Bindings;
487  return new InventoryKeyMapping()
488  {
489  Bindings = Enumerable.Range(0, thisBindings.Length)
490  .Select(i => i == index ? keyOrMouse : thisBindings[i])
491  .ToImmutableArray()
492  };
493  }
494 
495  public InventoryKeyMapping(IEnumerable<XElement> elements, InventoryKeyMapping? fallback)
496  {
497  var bindings = (fallback?.Bindings ?? GetDefault().Bindings).ToArray();
498  foreach (XElement element in elements)
499  {
500  for (int i = 0; i < bindings.Length; i++)
501  {
502  bindings[i] = element.GetAttributeKeyOrMouse($"slot{i}", bindings[i]);
503  }
504  }
505  Bindings = bindings.ToImmutableArray();
506  }
507  }
508 
509  [StructSerialization.Skip]
511 #endif
512  }
513 
514  public const string PlayerConfigPath = "config_player.xml";
515 
516  private static Config currentConfig;
517  public static ref readonly Config CurrentConfig => ref currentConfig;
518 
519 #if CLIENT
520  public static Action? OnGameMainHasLoaded;
521 #endif
522 
523  public static void Init()
524  {
525  XDocument? currentConfigDoc = null;
526 
527  if (File.Exists(PlayerConfigPath))
528  {
529  currentConfigDoc = XMLExtensions.TryLoadXml(PlayerConfigPath);
530  }
531 
532  if (currentConfigDoc != null)
533  {
534  currentConfig = Config.FromElement(currentConfigDoc.Root ?? throw new NullReferenceException("Config XML element is invalid: document is null."));
535 #if CLIENT
536  ServerListFilters.Init(currentConfigDoc.Root.GetChildElement("serverfilters"));
538  currentConfigDoc.Root.GetChildElement("player"),
539  currentConfigDoc.Root.GetChildElement("gameplay")?.GetChildElement("jobpreferences"));
540  IgnoredHints.Init(currentConfigDoc.Root.GetChildElement("ignoredhints"));
541  DebugConsoleMapping.Init(currentConfigDoc.Root.GetChildElement("debugconsolemapping"));
542  CompletedTutorials.Init(currentConfigDoc.Root.GetChildElement("tutorials"));
543 #endif
544  }
545  else
546  {
547  currentConfig = Config.GetDefault();
548  SaveCurrentConfig();
549  }
550  }
551 
552  public static void SetCurrentConfig(in Config newConfig)
553  {
554  bool resolutionChanged =
555  currentConfig.Graphics.Width != newConfig.Graphics.Width ||
556  currentConfig.Graphics.Height != newConfig.Graphics.Height;
557  bool languageChanged = currentConfig.Language != newConfig.Language;
558  bool audioOutputChanged = currentConfig.Audio.AudioOutputDevice != newConfig.Audio.AudioOutputDevice;
559  bool voiceCaptureChanged = currentConfig.Audio.VoiceCaptureDevice != newConfig.Audio.VoiceCaptureDevice;
560  bool textScaleChanged = Math.Abs(currentConfig.Graphics.TextScale - newConfig.Graphics.TextScale) > MathF.Pow(2.0f, -7);
561 
562  bool hudScaleChanged = !MathUtils.NearlyEqual(currentConfig.Graphics.HUDScale, newConfig.Graphics.HUDScale);
563 
564  bool setGraphicsMode =
565  resolutionChanged ||
566  currentConfig.Graphics.VSync != newConfig.Graphics.VSync ||
567  currentConfig.Graphics.DisplayMode != newConfig.Graphics.DisplayMode;
568 
569 #if CLIENT
570  bool keybindsChanged = false;
571  foreach (var kvp in newConfig.KeyMap.Bindings)
572  {
573  if (!currentConfig.KeyMap.Bindings.TryGetValue(kvp.Key, out var existingBinding) ||
574  existingBinding != kvp.Value)
575  {
576  keybindsChanged = true;
577  break;
578  }
579  }
580 #endif
581 
582  currentConfig = newConfig;
583 
584 #if CLIENT
585  if (setGraphicsMode)
586  {
587  GameMain.Instance.ApplyGraphicsSettings(recalculateFontsAndStyles: true);
588  }
589  else if (textScaleChanged)
590  {
591  GUIStyle.RecalculateFonts();
592  }
593 
594  if (audioOutputChanged)
595  {
596  GameMain.SoundManager?.InitializeAlcDevice(currentConfig.Audio.AudioOutputDevice);
597  }
598 
599  if (voiceCaptureChanged)
600  {
602  }
603 
604  if (hudScaleChanged)
605  {
606  HUDLayoutSettings.CreateAreas();
607  GameMain.GameSession?.HUDScaleChanged();
608  }
609 
610  if (keybindsChanged)
611  {
612  foreach (var item in Item.ItemList)
613  {
614  foreach (var ic in item.Components)
615  {
616  //parse messages because they may contain keybind texts
617  ic.ParseMsg();
618  }
619  }
620  }
621 
622  GameMain.SoundManager?.ApplySettings();
623 #endif
624  if (languageChanged) { TextManager.ClearCache(); }
625  }
626 
627  public static void SaveCurrentConfig()
628  {
629  XDocument configDoc = new XDocument();
630  XElement root = new XElement("config"); configDoc.Add(root);
631  currentConfig.SerializeElement(root);
632 
633  XElement graphicsElement = new XElement("graphicssettings"); root.Add(graphicsElement);
634  currentConfig.Graphics.SerializeElement(graphicsElement);
635 
636  XElement audioElement = new XElement("audio"); root.Add(audioElement);
637  currentConfig.Audio.SerializeElement(audioElement);
638 
639  XElement contentPackagesElement = new XElement("contentpackages"); root.Add(contentPackagesElement);
640  XComment corePackageComment = new XComment(ContentPackageManager.EnabledPackages.Core?.Name ?? "Vanilla"); contentPackagesElement.Add(corePackageComment);
641  XElement corePackageElement = new XElement(ContentPackageManager.CorePackageElementName); contentPackagesElement.Add(corePackageElement);
642  corePackageElement.SetAttributeValue("path", ContentPackageManager.EnabledPackages.Core?.Path ?? ContentPackageManager.VanillaFileList);
643 
644  XElement regularPackagesElement = new XElement(ContentPackageManager.RegularPackagesElementName); contentPackagesElement.Add(regularPackagesElement);
645  foreach (var regularPackage in ContentPackageManager.EnabledPackages.Regular)
646  {
647  XComment packageComment = new XComment(regularPackage.Name); regularPackagesElement.Add(packageComment);
648  XElement packageElement = new XElement(ContentPackageManager.RegularPackagesSubElementName); regularPackagesElement.Add(packageElement);
649  packageElement.SetAttributeValue("path", regularPackage.Path);
650  }
651 
652 #if CLIENT
653  XElement serverFiltersElement = new XElement("serverfilters"); root.Add(serverFiltersElement);
654  ServerListFilters.Instance.SaveTo(serverFiltersElement);
655 
656  XElement characterElement = new XElement("player"); root.Add(characterElement);
657  MultiplayerPreferences.Instance.SaveTo(characterElement);
658 
659  XElement ignoredHintsElement = new XElement("ignoredhints"); root.Add(ignoredHintsElement);
660  IgnoredHints.Instance.SaveTo(ignoredHintsElement);
661 
662  XElement debugConsoleMappingElement = new XElement("debugconsolemapping"); root.Add(debugConsoleMappingElement);
663  DebugConsoleMapping.Instance.SaveTo(debugConsoleMappingElement);
664 
665  XElement tutorialsElement = new XElement("tutorials"); root.Add(tutorialsElement);
666  CompletedTutorials.Instance.SaveTo(tutorialsElement);
667 
668  XElement keyMappingElement = new XElement("keymapping",
669  currentConfig.KeyMap.Bindings.Select(kvp
670  => new XAttribute(kvp.Key.ToString(), kvp.Value.ToString())));
671  root.Add(keyMappingElement);
672 
673  XElement inventoryKeyMappingElement = new XElement("inventorykeymapping",
674  Enumerable.Range(0, currentConfig.InventoryKeyMap.Bindings.Length)
675  .Zip(currentConfig.InventoryKeyMap.Bindings)
676  .Cast<(int Index, KeyOrMouse Bind)>()
677  .Select(kvp
678  => new XAttribute($"slot{kvp.Index.ToString(CultureInfo.InvariantCulture)}", kvp.Bind.ToString())));
679  root.Add(inventoryKeyMappingElement);
680 
681  SubEditorScreen.ImageManager.Save(root);
682 
683  root.Add(CampaignSettings.CurrentSettings.Save());
684 #endif
685 
686  configDoc.SaveSafe(PlayerConfigPath);
687 
688  System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
689  {
690  Indent = true,
691  OmitXmlDeclaration = true,
692  NewLineOnAttributes = true
693  };
694 
695  try
696  {
697  using (var writer = XmlWriter.Create(PlayerConfigPath, settings))
698  {
699  configDoc.WriteTo(writer);
700  writer.Flush();
701  }
702  }
703  catch (Exception e)
704  {
705  DebugConsole.ThrowError("Saving game settings failed.", e);
706  GameAnalyticsManager.AddErrorEventOnce("GameSettings.Save:SaveFailed", GameAnalyticsManager.ErrorSeverity.Error,
707  "Saving game settings failed.\n" + e.Message + "\n" + e.StackTrace.CleanupStackTrace());
708  }
709  }
710 
711 #if CLIENT
712  private static void LoadSubEditorImages(XElement configElement)
713  {
714  XElement? element = configElement?.Element("editorimages");
715  if (element == null)
716  {
717  SubEditorScreen.ImageManager.Clear(alsoPending: true);
718  return;
719  }
720  SubEditorScreen.ImageManager.Load(element);
721  }
722 #endif
723  }
724 }
static void Init(XElement? element)
static XmlWriter Create(string path, System.Xml.XmlWriterSettings settings)
Definition: SafeIO.cs:163
static void Init(XElement? element)
Definition: IgnoredHints.cs:22
static void Init(params XElement?[] elements)
static void ChangeCaptureDevice(string deviceName)
Definition: VoipCapture.cs:161
static void Init(XElement? elem)
InteractionLabelDisplayMode
Definition: GameSettings.cs:46
static AudioSettings FromElements(IEnumerable< XElement > elements, in AudioSettings? fallback=null)
static GraphicsSettings FromElements(IEnumerable< XElement > elements, in GraphicsSettings? fallback=null)
InventoryKeyMapping WithBinding(int index, KeyOrMouse keyOrMouse)
InventoryKeyMapping(IEnumerable< XElement > elements, InventoryKeyMapping? fallback)
KeyMapping(IEnumerable< XElement > elements, in KeyMapping? fallback)
KeyMapping WithBinding(InputType type, KeyOrMouse bind)
LocalizedString KeyBindText(InputType inputType)
ImmutableDictionary< InputType, KeyOrMouse > Bindings
InteractionLabelDisplayMode InteractionLabelDisplayMode
Eos.EosSteamPrimaryLogin.CrossplayChoice CrossplayChoice
EnemyHealthBarMode ShowEnemyHealthBars
static Config FromElement(XElement element, in Config? fallback=null)
InventoryKeyMapping InventoryKeyMap
LanguageIdentifier Language
static readonly LanguageIdentifier None
Definition: TextPack.cs:12