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