Client LuaCsForBarotrauma
BarotraumaShared/SharedSource/Networking/ServerSettings.cs
2 using Barotrauma.IO;
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.Net;
10 using System.Security.Cryptography;
11 using System.Text;
12 
13 namespace Barotrauma.Networking
14 {
15  public enum SelectionMode
16  {
17  Manual = 0, Random = 1, Vote = 2
18  }
19 
20  public enum BotSpawnMode
21  {
22  Normal, Fill
23  }
24 
25  public enum PlayStyle
26  {
27  Serious = 0,
28  Casual = 1,
29  Roleplay = 2,
30  Rampage = 3,
32  }
33 
34  public enum RespawnMode
35  {
36  MidRound,
38  Permadeath,
39  }
40 
41 
42  internal enum LootedMoneyDestination
43  {
44  Bank,
45  Wallet
46  }
47 
49  {
50  public const int PacketLimitMin = 1200,
51  PacketLimitWarning = 3500,
52  PacketLimitDefault = 4000,
53  PacketLimitMax = 10000;
54 
55  public const string SettingsFile = "serversettings.xml";
56 
57  [Flags]
58  public enum NetFlags : byte
59  {
60  None = 0x0,
61  Properties = 0x4,
62  Misc = 0x8,
63  LevelSeed = 0x10,
64  HiddenSubs = 0x20
65  }
66 
67  public static readonly string PermissionPresetFile = "Data" + Path.DirectorySeparatorChar + "permissionpresets.xml";
68 
69  public string Name
70  {
71  get { return "ServerSettings"; }
72  }
73 
77  public bool ServerDetailsChanged;
78 
79  public class SavedClientPermission
80  {
81  public readonly Either<Address, AccountId> AddressOrAccountId;
82  public readonly string Name;
83  public readonly ImmutableHashSet<DebugConsole.Command> PermittedCommands;
84 
85  public readonly ClientPermissions Permissions;
86 
87  public SavedClientPermission(string name, Either<Address, AccountId> addressOrAccountId, ClientPermissions permissions, IEnumerable<DebugConsole.Command> permittedCommands)
88  {
89  this.Name = name;
90  this.AddressOrAccountId = addressOrAccountId;
91  this.Permissions = permissions;
92  this.PermittedCommands = permittedCommands.ToImmutableHashSet();
93  }
94  }
95 
96  partial class NetPropertyData
97  {
98  private readonly SerializableProperty property;
99  private readonly string typeString;
100  private readonly object parentObject;
101 
102  public Identifier Name => property.Name.ToIdentifier();
103 
104  public object Value
105  {
106  get { return property.GetValue(parentObject); }
107  set { property.SetValue(parentObject, value); }
108  }
109 
110  public NetPropertyData(object parentObject, SerializableProperty property, string typeString)
111  {
112  this.property = property;
113  this.typeString = typeString;
114  this.parentObject = parentObject;
115  }
116 
117  public bool PropEquals(object a, object b)
118  {
119  switch (typeString)
120  {
121  case "float":
122  if (a is not float fa) { return false; }
123  if (b is not float fb) { return false; }
124  return MathUtils.NearlyEqual(fa, fb);
125  case "int":
126  if (a is not int ia) { return false; }
127  if (b is not int ib) { return false; }
128  return ia == ib;
129  case "bool":
130  if (a is not bool ba) { return false; }
131  if (b is not bool bb) { return false; }
132  return ba == bb;
133  case "Enum":
134  if (a is not Enum ea) { return false; }
135  if (b is not Enum eb) { return false; }
136  return ea.Equals(eb);
137  default:
138  return ReferenceEquals(a,b)
139  || string.Equals(a?.ToString(), b?.ToString(), StringComparison.OrdinalIgnoreCase);
140  }
141  }
142 
143  public void Read(IReadMessage msg)
144  {
145  int oldPos = msg.BitPosition;
146  UInt32 size = msg.ReadVariableUInt32();
147 
148  float x; float y; float z; float w;
149  byte r; byte g; byte b; byte a;
150  int ix; int iy; int width; int height;
151 
152  switch (typeString)
153  {
154  case "float":
155  if (size != 4) break;
156  property.SetValue(parentObject, msg.ReadSingle());
157  return;
158  case "int":
159  if (size != 4) break;
160  property.SetValue(parentObject, msg.ReadInt32());
161  return;
162  case "vector2":
163  if (size != 8) break;
164  x = msg.ReadSingle();
165  y = msg.ReadSingle();
166  property.SetValue(parentObject, new Vector2(x, y));
167  return;
168  case "vector3":
169  if (size != 12) break;
170  x = msg.ReadSingle();
171  y = msg.ReadSingle();
172  z = msg.ReadSingle();
173  property.SetValue(parentObject, new Vector3(x, y, z));
174  return;
175  case "vector4":
176  if (size != 16) break;
177  x = msg.ReadSingle();
178  y = msg.ReadSingle();
179  z = msg.ReadSingle();
180  w = msg.ReadSingle();
181  property.SetValue(parentObject, new Vector4(x, y, z, w));
182  return;
183  case "color":
184  if (size != 4) break;
185  r = msg.ReadByte();
186  g = msg.ReadByte();
187  b = msg.ReadByte();
188  a = msg.ReadByte();
189  property.SetValue(parentObject, new Color(r, g, b, a));
190  return;
191  case "rectangle":
192  if (size != 16) break;
193  ix = msg.ReadInt32();
194  iy = msg.ReadInt32();
195  width = msg.ReadInt32();
196  height = msg.ReadInt32();
197  property.SetValue(parentObject, new Rectangle(ix, iy, width, height));
198  return;
199  default:
200  msg.BitPosition = oldPos; //reset position to properly read the string
201  string incVal = msg.ReadString();
202  property.TrySetValue(parentObject, incVal);
203  return;
204  }
205 
206  //size didn't match: skip this
207  msg.BitPosition += (int)(8 * size);
208  }
209 
210  public void Write(IWriteMessage msg, object overrideValue = null)
211  {
212  overrideValue ??= Value;
213  switch (typeString)
214  {
215  case "float":
216  msg.WriteVariableUInt32(4);
217  msg.WriteSingle((float)overrideValue);
218  break;
219  case "int":
220  msg.WriteVariableUInt32(4);
221  msg.WriteInt32((int)overrideValue);
222  break;
223  case "vector2":
224  msg.WriteVariableUInt32(8);
225  msg.WriteSingle(((Vector2)overrideValue).X);
226  msg.WriteSingle(((Vector2)overrideValue).Y);
227  break;
228  case "vector3":
229  msg.WriteVariableUInt32(12);
230  msg.WriteSingle(((Vector3)overrideValue).X);
231  msg.WriteSingle(((Vector3)overrideValue).Y);
232  msg.WriteSingle(((Vector3)overrideValue).Z);
233  break;
234  case "vector4":
235  msg.WriteVariableUInt32(16);
236  msg.WriteSingle(((Vector4)overrideValue).X);
237  msg.WriteSingle(((Vector4)overrideValue).Y);
238  msg.WriteSingle(((Vector4)overrideValue).Z);
239  msg.WriteSingle(((Vector4)overrideValue).W);
240  break;
241  case "color":
242  msg.WriteVariableUInt32(4);
243  msg.WriteByte(((Color)overrideValue).R);
244  msg.WriteByte(((Color)overrideValue).G);
245  msg.WriteByte(((Color)overrideValue).B);
246  msg.WriteByte(((Color)overrideValue).A);
247  break;
248  case "rectangle":
249  msg.WriteVariableUInt32(16);
250  msg.WriteInt32(((Rectangle)overrideValue).X);
251  msg.WriteInt32(((Rectangle)overrideValue).Y);
252  msg.WriteInt32(((Rectangle)overrideValue).Width);
253  msg.WriteInt32(((Rectangle)overrideValue).Height);
254  break;
255  default:
256  string strVal = overrideValue.ToString();
257 
258  msg.WriteString(strVal);
259  break;
260  }
261  }
262  };
263 
264  public Dictionary<Identifier, SerializableProperty> SerializableProperties
265  {
266  get;
267  private set;
268  }
269 
270  private readonly Dictionary<UInt32, NetPropertyData> netProperties;
271 
272  partial void InitProjSpecific();
273 
274  public ServerSettings(NetworkMember networkMember, string serverName, int port, int queryPort, int maxPlayers, bool isPublic, bool enableUPnP, IPAddress listenIp)
275  {
276  ServerLog = new ServerLog(serverName);
277 
278  BanList = new BanList();
279 
280  ExtraCargo = new Dictionary<ItemPrefab, int>();
281 
282  HiddenSubs = new HashSet<string>();
283 
285  InitProjSpecific();
286 
287  ServerName = serverName;
288  ListenIPAddress = listenIp;
289  Port = port;
290  QueryPort = queryPort;
291  EnableUPnP = enableUPnP;
292  MaxPlayers = maxPlayers;
293  IsPublic = isPublic;
294 
295  netProperties = new Dictionary<UInt32, NetPropertyData>();
296 
297  using (MD5 md5 = MD5.Create())
298  {
299  var saveProperties = SerializableProperty.GetProperties<Serialize>(this);
300  foreach (var property in saveProperties)
301  {
302  string typeName = SerializableProperty.GetSupportedTypeName(property.PropertyType);
303  if (typeName != null || property.PropertyType.IsEnum)
304  {
305  NetPropertyData netPropertyData = new NetPropertyData(this, property, typeName);
306  UInt32 key = ToolBoxCore.IdentifierToUint32Hash(netPropertyData.Name, md5);
307  if (key == 0) { key++; } //0 is reserved to indicate the end of the netproperties section of a message
308  if (netProperties.ContainsKey(key)){ throw new Exception("Hashing collision in ServerSettings.netProperties: " + netProperties[key] + " has same key as " + property.Name + " (" + key.ToString() + ")"); }
309  netProperties.Add(key, netPropertyData);
310  }
311  }
312 
313  var karmaProperties = SerializableProperty.GetProperties<Serialize>(networkMember.KarmaManager);
314  foreach (var property in karmaProperties)
315  {
316  object value = property.GetValue(networkMember.KarmaManager);
317  if (value == null) { continue; }
318 
319  string typeName = SerializableProperty.GetSupportedTypeName(value.GetType());
320  if (typeName != null || property.PropertyType.IsEnum)
321  {
322  NetPropertyData netPropertyData = new NetPropertyData(networkMember.KarmaManager, property, typeName);
323  UInt32 key = ToolBoxCore.IdentifierToUint32Hash(netPropertyData.Name, md5);
324  if (netProperties.ContainsKey(key)) { throw new Exception("Hashing collision in ServerSettings.netProperties: " + netProperties[key] + " has same key as " + property.Name + " (" + key.ToString() + ")"); }
325  netProperties.Add(key, netPropertyData);
326  }
327  }
328  }
329  }
330 
331  private string serverName = string.Empty;
332 
333  [Serialize("", IsPropertySaveable.Yes)]
334  public string ServerName
335  {
336  get { return serverName; }
337  set
338  {
339  string newName = value;
340  if (newName.Length > NetConfig.ServerNameMaxLength) { newName = newName.Substring(0, NetConfig.ServerNameMaxLength); }
341  if (serverName == newName) { return; }
342  if (newName.IsNullOrWhiteSpace()) { return; }
343  serverName = newName;
344  ServerDetailsChanged = true;
345  }
346  }
347 
348  private string serverMessageText;
349 
350  [Serialize("", IsPropertySaveable.Yes)]
351  public string ServerMessageText
352  {
353  get { return serverMessageText; }
354  set
355  {
356  string val = value;
357  if (val.Length > NetConfig.ServerMessageMaxLength) { val = val.Substring(0, NetConfig.ServerMessageMaxLength); }
358  if (serverMessageText == val) { return; }
359 #if SERVER
360  GameMain.Server?.SendChatMessage(TextManager.AddPunctuation(':', TextManager.Get("servermotd"), val).Value, ChatMessageType.Server);
361 #elif CLIENT
362  if (GameMain.NetLobbyScreen.ServerMessageButton is { } serverMessageButton)
363  {
364  serverMessageButton.Flash(GUIStyle.Green);
365  serverMessageButton.Pulsate(Vector2.One, Vector2.One * 1.2f, 1.0f);
366  }
367 #endif
368  serverMessageText = val;
369  ServerDetailsChanged = true;
370  }
371  }
372 
373  public int Port;
374 
375  public int QueryPort;
376 
377  public IPAddress ListenIPAddress;
378 
379  public bool EnableUPnP;
380 
382 
383  public Dictionary<Identifier, bool> MonsterEnabled { get; private set; }
384 
385  public const int MaxExtraCargoItemsOfType = 10;
386  public const int MaxExtraCargoItemTypes = 20;
387  public Dictionary<ItemPrefab, int> ExtraCargo { get; private set; }
388 
389  public HashSet<string> HiddenSubs { get; set; }
390 
391  private float selectedLevelDifficulty;
392  private string password;
393 
394  public float AutoRestartTimer;
395 
396  private bool autoRestart;
397 
398  private int maxPlayers;
399 
400  public List<SavedClientPermission> ClientPermissions { get; private set; } = new List<SavedClientPermission>();
401 
402  [Serialize(true, IsPropertySaveable.Yes)]
403  public bool IsPublic
404  {
405  get;
406  set;
407  }
408 
409  public const int DefaultTickRate = 20;
410 
411  private int tickRate = DefaultTickRate;
413  public int TickRate
414  {
415  get { return tickRate; }
416  set { tickRate = MathHelper.Clamp(value, 1, 60); }
417  }
418 
419  [Serialize(true, IsPropertySaveable.Yes, description: "Do clients need to be authenticated (e.g. based on Steam ID or an EGS ownership token). Can be disabled if you for example want to play the game in a local network without a connection to external services.")]
421  {
422  get;
423  set;
424  }
425 
426  [Serialize(true, IsPropertySaveable.Yes)]
427  public bool RandomizeSeed
428  {
429  get;
430  set;
431  }
432 
433  [Serialize(true, IsPropertySaveable.Yes)]
434  public bool UseRespawnShuttle
435  {
436  get;
437  private set;
438  }
439 
440  [Serialize(300.0f, IsPropertySaveable.Yes)]
441  public float RespawnInterval
442  {
443  get;
444  private set;
445  }
446 
447  [Serialize(180.0f, IsPropertySaveable.Yes)]
448  public float MaxTransportTime
449  {
450  get;
451  private set;
452  }
453 
454  [Serialize(0.2f, IsPropertySaveable.Yes)]
455  public float MinRespawnRatio
456  {
457  get;
458  private set;
459  }
460 
461  [Serialize(20f, IsPropertySaveable.Yes)]
466  {
467  get;
468  private set;
469  }
470 
471  [Serialize(10f, IsPropertySaveable.Yes)]
478  {
479  get;
480  private set;
481  }
482 
483  [Serialize(100f, IsPropertySaveable.Yes)]
488  {
489  get;
490  private set;
491  }
492 
493  [Serialize(true, IsPropertySaveable.Yes)]
498  {
499  get;
500  private set;
501  }
502 
503  [Serialize(false, IsPropertySaveable.Yes)]
508  public bool IronmanMode
509  {
510  get;
511  private set;
512  }
513 
514  [Serialize(60.0f, IsPropertySaveable.Yes)]
515  public float AutoRestartInterval
516  {
517  get;
518  set;
519  }
520 
521  [Serialize(false, IsPropertySaveable.Yes)]
523  {
524  get;
525  set;
526  }
527 
528  [Serialize(0.8f, IsPropertySaveable.Yes)]
530  {
531  get;
532  private set;
533  }
534 
535  private bool allowSpectating;
536  [Serialize(true, IsPropertySaveable.Yes)]
537  public bool AllowSpectating
538  {
539  get { return allowSpectating; }
540  private set
541  {
542  if (allowSpectating == value) { return; }
543  allowSpectating = value;
544  ServerDetailsChanged = true;
545  }
546  }
547 
548  [Serialize(true, IsPropertySaveable.Yes)]
549  public bool SaveServerLogs
550  {
551  get;
552  private set;
553  }
554 
555  [Serialize(true, IsPropertySaveable.Yes)]
556  public bool AllowModDownloads
557  {
558  get;
559  private set;
560  } = true;
561 
562  [Serialize(true, IsPropertySaveable.Yes)]
563  public bool AllowFileTransfers
564  {
565  get;
566  private set;
567  }
568 
569  private bool voiceChatEnabled;
570  [Serialize(true, IsPropertySaveable.Yes)]
571  public bool VoiceChatEnabled
572  {
573  get { return voiceChatEnabled; }
574  set
575  {
576  if (voiceChatEnabled == value) { return; }
577  voiceChatEnabled = value;
578  ServerDetailsChanged = true;
579  }
580  }
581 
582  private PlayStyle playstyleSelection;
583  [Serialize(PlayStyle.Casual, IsPropertySaveable.Yes)]
585  {
586  get { return playstyleSelection; }
587  set
588  {
589  playstyleSelection = value;
590  ServerDetailsChanged = true;
591  }
592  }
593 
594  [Serialize(LosMode.Transparent, IsPropertySaveable.Yes)]
596  {
597  get;
598  set;
599  }
600 
603  {
604  get;
605  set;
606  }
607 
608  [Serialize(800, IsPropertySaveable.Yes)]
609  public int LinesPerLogFile
610  {
611  get
612  {
613  return ServerLog.LinesPerFile;
614  }
615  set
616  {
617  ServerLog.LinesPerFile = value;
618  }
619  }
620 
621  [Serialize(false, IsPropertySaveable.Yes)]
622  public bool AutoRestart
623  {
624  get { return autoRestart; }
625  set
626  {
627  autoRestart = value;
628 
629  AutoRestartTimer = autoRestart ? AutoRestartInterval : 0.0f;
630  }
631  }
632 
633  public bool HasPassword
634  {
635  get { return !string.IsNullOrEmpty(password); }
636 #if CLIENT
637  set
638  {
639  password = value ? (password ?? "_") : null;
640  }
641 #endif
642  }
643 
644  [Serialize(true, IsPropertySaveable.Yes)]
645  public bool AllowVoteKick
646  {
647  get; set;
648  }
649 
650  [Serialize(true, IsPropertySaveable.Yes)]
651  public bool AllowEndVoting
652  {
653  get; set;
654  }
655 
656  private RespawnMode respawnMode;
657  [Serialize(RespawnMode.MidRound, IsPropertySaveable.Yes)]
659  {
660  get { return respawnMode; }
661  set
662  {
663  if (respawnMode == value) { return; }
664  //can't change this when a round is running (but clients can, if the server says so, e.g. when a client joins and needs to know what it's set to despite a round being running)
665  if (GameMain.NetworkMember is { GameStarted: true, IsServer: true }) { return; }
666  respawnMode = value;
667  ServerDetailsChanged = true;
668  }
669  }
670 
671  [Serialize(0, IsPropertySaveable.Yes)]
672  public int BotCount
673  {
674  get;
675  set;
676  }
677 
678  [Serialize(16, IsPropertySaveable.Yes)]
679  public int MaxBotCount
680  {
681  get;
682  set;
683  }
684 
687  {
688  get;
689  set;
690  }
691 
692  [Serialize(false, IsPropertySaveable.Yes)]
694  {
695  get;
696  set;
697  }
698 
699  [Serialize(0.0f, IsPropertySaveable.Yes)]
701  {
702  get { return selectedLevelDifficulty; }
703  set { selectedLevelDifficulty = MathHelper.Clamp(value, 0.0f, 100.0f); }
704  }
705 
706  [Serialize(true, IsPropertySaveable.Yes)]
707  public bool AllowDisguises
708  {
709  get;
710  set;
711  }
712 
713  [Serialize(true, IsPropertySaveable.Yes)]
714  public bool AllowRewiring
715  {
716  get;
717  set;
718  }
719 
720  [Serialize(true, IsPropertySaveable.Yes)]
722  {
723  get;
724  set;
725  }
726 
727  [Serialize(false, IsPropertySaveable.Yes)]
729  {
730  get;
731  set;
732  }
733 
734  [Serialize(false, IsPropertySaveable.Yes)]
736  {
737  get;
738  set;
739  }
740 
741  [Serialize(true, IsPropertySaveable.Yes)]
742  public bool AllowFriendlyFire
743  {
744  get;
745  set;
746  }
747 
748  [Serialize(true, IsPropertySaveable.Yes)]
750  {
751  get;
752  set;
753  }
754 
755  [Serialize(false, IsPropertySaveable.Yes)]
757  {
758  get;
759  set;
760  }
761 
762  [Serialize(true, IsPropertySaveable.Yes)]
763  public bool KillableNPCs
764  {
765  get;
766  set;
767  }
768 
769  [Serialize(true, IsPropertySaveable.Yes)]
771  {
772  get;
773  set;
774  }
775 
776  [Serialize(3, IsPropertySaveable.Yes)]
778  {
779  get;
780  private set;
781  }
782 
783  [Serialize(true, IsPropertySaveable.Yes)]
785  {
786  get;
787  private set;
788  }
789 
790  [Serialize(PacketLimitDefault, IsPropertySaveable.Yes)]
791  public int MaxPacketAmount
792  {
793  get;
794  private set;
795  }
796 
797  [Serialize("", IsPropertySaveable.Yes)]
798  public string SelectedSubmarine
799  {
800  get;
801  set;
802  }
803  [Serialize("", IsPropertySaveable.Yes)]
804  public string SelectedShuttle
805  {
806  get;
807  set;
808  }
809 
810  private float traitorProbability;
811  [Serialize(0.0f, IsPropertySaveable.Yes)]
812  public float TraitorProbability
813  {
814  get { return traitorProbability; }
815  set
816  {
817  if (MathUtils.NearlyEqual(traitorProbability, value)) { return; }
818  traitorProbability = MathHelper.Clamp(value, 0.0f, 1.0f);
819  ServerDetailsChanged = true;
820  }
821  }
822 
823 
824  private int traitorDangerLevel;
827  {
828  get { return traitorDangerLevel; }
829  set
830  {
831  int clampedValue = MathHelper.Clamp(value, TraitorEventPrefab.MinDangerLevel, TraitorEventPrefab.MaxDangerLevel);
832  if (traitorDangerLevel == clampedValue) { return; }
833  traitorDangerLevel = clampedValue;
834  ServerDetailsChanged = true;
835 #if CLIENT
836  GameMain.NetLobbyScreen?.SetTraitorDangerLevel(traitorDangerLevel);
837 #endif
838  }
839  }
840 
841  private int traitorsMinPlayerCount;
842  [Serialize(defaultValue: 1, isSaveable: IsPropertySaveable.Yes)]
844  {
845  get { return traitorsMinPlayerCount; }
846  set { traitorsMinPlayerCount = MathHelper.Clamp(value, 1, NetConfig.MaxPlayers); }
847  }
848 
849  [Serialize(defaultValue: 50.0f, isSaveable: IsPropertySaveable.Yes)]
851  {
852  get;
853  set;
854  }
855 
856  [Serialize(defaultValue: "", IsPropertySaveable.Yes)]
857  public LanguageIdentifier Language { get; set; }
858 
859  private SelectionMode subSelectionMode;
862  {
863  get { return subSelectionMode; }
864  set
865  {
866  subSelectionMode = value;
867  AllowSubVoting = subSelectionMode == SelectionMode.Vote;
868  ServerDetailsChanged = true;
869  }
870  }
871 
872  private SelectionMode modeSelectionMode;
875  {
876  get { return modeSelectionMode; }
877  set
878  {
879  modeSelectionMode = value;
880  AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
881  ServerDetailsChanged = true;
882  }
883  }
884 
885  public BanList BanList { get; private set; }
886 
887  [Serialize(0.6f, IsPropertySaveable.Yes)]
888  public float EndVoteRequiredRatio
889  {
890  get;
891  private set;
892  }
893 
894  [Serialize(0.6f, IsPropertySaveable.Yes)]
895  public float VoteRequiredRatio
896  {
897  get;
898  private set;
899  }
900 
901  [Serialize(30f, IsPropertySaveable.Yes)]
902  public float VoteTimeout
903  {
904  get;
905  private set;
906  }
907 
908  [Serialize(0.6f, IsPropertySaveable.Yes)]
910  {
911  get;
912  private set;
913  }
914 
915  [Serialize(120.0f, IsPropertySaveable.Yes)]
916  public float DisallowKickVoteTime
917  {
918  get;
919  private set;
920  }
921 
925  [Serialize(300.0f, IsPropertySaveable.Yes)]
926  public float KillDisconnectedTime
927  {
928  get;
929  private set;
930  }
931 
937  [Serialize(10.0f, IsPropertySaveable.Yes)]
939  {
940  get;
941  private set;
942  }
943 
944  [Serialize(600.0f, IsPropertySaveable.Yes)]
945  public float KickAFKTime
946  {
947  get;
948  private set;
949  }
950 
951  [Serialize(10.0f, IsPropertySaveable.Yes)]
953  {
954  get;
955  private set;
956  }
957 
958  private bool karmaEnabled;
959  [Serialize(false, IsPropertySaveable.Yes)]
960  public bool KarmaEnabled
961  {
962  get { return karmaEnabled; }
963  set
964  {
965  karmaEnabled = value;
966 #if CLIENT
967  if (karmaSettingsList != null)
968  {
969  SetElementInteractability(karmaSettingsList.Content, !karmaEnabled || KarmaPreset != "custom");
970  }
971  karmaElements.ForEach(e => e.Visible = karmaEnabled);
972 #endif
973  }
974  }
975 
976  private string karmaPreset = "default";
977  [Serialize("default", IsPropertySaveable.Yes)]
978  public string KarmaPreset
979  {
980  get { return karmaPreset; }
981  set
982  {
983  if (karmaPreset == value) { return; }
984  if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
985  {
986  GameMain.NetworkMember?.KarmaManager?.SelectPreset(value);
987  }
988  karmaPreset = value;
989  }
990  }
991 
992  [Serialize("sandbox", IsPropertySaveable.Yes)]
993  public Identifier GameModeIdentifier
994  {
995  get;
996  set;
997  }
998 
999  [Serialize("All", IsPropertySaveable.Yes)]
1000  public string MissionType
1001  {
1002  get;
1003  set;
1004  }
1005 
1006  [Serialize(8, IsPropertySaveable.Yes)]
1007  public int MaxPlayers
1008  {
1009  get { return maxPlayers; }
1010  set { maxPlayers = MathHelper.Clamp(value, 0, NetConfig.MaxPlayers); }
1011  }
1012 
1013  public List<MissionType> AllowedRandomMissionTypes
1014  {
1015  get;
1016  set;
1017  }
1018 
1019  [Serialize(60f * 60.0f, IsPropertySaveable.Yes)]
1020  public float AutoBanTime
1021  {
1022  get;
1023  private set;
1024  }
1025 
1026  [Serialize(60.0f * 60.0f * 24.0f, IsPropertySaveable.Yes)]
1027  public float MaxAutoBanTime
1028  {
1029  get;
1030  private set;
1031  }
1032 
1033  [Serialize(LootedMoneyDestination.Bank, IsPropertySaveable.Yes)]
1034  public LootedMoneyDestination LootedMoneyDestination { get; set; }
1035 
1036  [Serialize(999999, IsPropertySaveable.Yes)]
1037  public int MaximumMoneyTransferRequest { get; set; }
1038 
1039  [Serialize(0f, IsPropertySaveable.Yes)]
1040  public float NewCampaignDefaultSalary { get; set; }
1041 
1043 
1044  private bool allowSubVoting;
1045  //Don't serialize: the value is set based on SubSelectionMode
1046  public bool AllowSubVoting
1047  {
1048  get { return allowSubVoting; }
1049  set
1050  {
1051  if (value == allowSubVoting) { return; }
1052  allowSubVoting = value;
1053 #if CLIENT
1055  (GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectSub));
1056  var subVotesLabel = GameMain.NetLobbyScreen.Frame.FindChild("subvotes", true) as GUITextBlock;
1057  subVotesLabel.Visible = value;
1058  var subVisButton = GameMain.NetLobbyScreen.SubVisibilityButton;
1059  subVisButton.RectTransform.AbsoluteOffset
1060  = new Point(value ? (int)(subVotesLabel.TextSize.X + subVisButton.Rect.Width) : 0, 0);
1061 
1062  GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Sub);
1064 #endif
1065  }
1066  }
1067 
1068  private bool allowModeVoting;
1069  //Don't serialize: the value is set based on ModeSelectionMode
1070  public bool AllowModeVoting
1071  {
1072  get { return allowModeVoting; }
1073  set
1074  {
1075  if (value == allowModeVoting) { return; }
1076  allowModeVoting = value;
1077 #if CLIENT
1079  value ||
1080  (GameMain.Client != null && GameMain.Client.HasPermission(Networking.ClientPermissions.SelectMode));
1081  GameMain.NetLobbyScreen.Frame.FindChild("modevotes", true).Visible = value;
1082  // Disable modes that cannot be voted on
1083  foreach (var guiComponent in GameMain.NetLobbyScreen.ModeList.Content.Children)
1084  {
1085  if (guiComponent is GUIFrame frame)
1086  {
1087  frame.CanBeFocused = !allowModeVoting || ((GameModePreset)frame.UserData).Votable;
1088  }
1089  }
1090  GameMain.Client?.Voting.UpdateVoteTexts(null, VoteType.Mode);
1092 #endif
1093  }
1094  }
1095 
1096 
1097  public void SetPassword(string password)
1098  {
1099  this.password = string.IsNullOrEmpty(password) ? null : password;
1100  }
1101 
1102  public static byte[] SaltPassword(byte[] password, int salt)
1103  {
1104  byte[] saltedPw = new byte[password.Length*2];
1105  for (int i = 0; i < password.Length; i++)
1106  {
1107  saltedPw[(i * 2)] = password[i];
1108  saltedPw[(i * 2) + 1] = (byte)((salt >> (8 * (i % 4))) & 0xff);
1109  }
1110  saltedPw = Lidgren.Network.NetUtility.ComputeSHAHash(saltedPw);
1111  return saltedPw;
1112  }
1113 
1114  public bool IsPasswordCorrect(byte[] input, int salt)
1115  {
1116  if (!HasPassword) { return true; }
1117  byte[] saltedPw = SaltPassword(Encoding.UTF8.GetBytes(password), salt);
1118  return saltedPw.SequenceEqual(input);
1119  }
1120 
1124  public List<Range<int>> AllowedClientNameChars
1125  {
1126  get;
1127  private set;
1128  } = new List<Range<int>>();
1129 
1130  private void InitMonstersEnabled()
1131  {
1132  //monster spawn settings
1133  if (MonsterEnabled is null || MonsterEnabled.Count != CharacterPrefab.Prefabs.Count())
1134  {
1135  MonsterEnabled = CharacterPrefab.Prefabs.Select(p => (p.Identifier, true)).ToDictionary();
1136  }
1137  }
1138 
1139  private static IReadOnlyList<Identifier> ExtractAndSortKeys(IReadOnlyDictionary<Identifier, bool> monsterEnabled)
1140  => monsterEnabled.Keys
1141  .OrderBy(k => CharacterPrefab.Prefabs[k].UintIdentifier)
1142  .ToImmutableArray();
1143 
1145  {
1146  bool changed = false;
1147  InitMonstersEnabled();
1148  var monsterNames = ExtractAndSortKeys(MonsterEnabled);
1149  uint receivedMonsterCount = inc.ReadVariableUInt32();
1150  if (monsterNames.Count != receivedMonsterCount)
1151  {
1152  inc.BitPosition += (int)receivedMonsterCount;
1153  DebugConsole.AddWarning($"Expected monster count {monsterNames.Count}, got {receivedMonsterCount}");
1154  }
1155  else
1156  {
1157  foreach (Identifier s in monsterNames)
1158  {
1159  MonsterEnabled.TryGetValue(s, out bool prevEnabled);
1160  MonsterEnabled[s] = inc.ReadBoolean();
1161  changed |= prevEnabled != MonsterEnabled[s];
1162  }
1163  }
1164  inc.ReadPadBits();
1165  return changed;
1166  }
1167 
1168  public void WriteMonsterEnabled(IWriteMessage msg, Dictionary<Identifier, bool> monsterEnabled = null)
1169  {
1170  //monster spawn settings
1171  InitMonstersEnabled();
1172  monsterEnabled ??= MonsterEnabled;
1173  var monsterNames = ExtractAndSortKeys(monsterEnabled);
1174  msg.WriteVariableUInt32((uint)monsterNames.Count);
1175  foreach (Identifier s in monsterNames)
1176  {
1177  msg.WriteBoolean(monsterEnabled[s]);
1178  }
1179  msg.WritePadBits();
1180  }
1181 
1182  public bool ReadExtraCargo(IReadMessage msg)
1183  {
1184  bool changed = false;
1185  UInt32 count = msg.ReadUInt32();
1186  if (ExtraCargo == null || count != ExtraCargo.Count) { changed = true; }
1187  Dictionary<ItemPrefab, int> extraCargo = new Dictionary<ItemPrefab, int>();
1188  for (int i = 0; i < count; i++)
1189  {
1190  Identifier prefabIdentifier = msg.ReadIdentifier();
1191  byte amount = msg.ReadByte();
1192 
1193  if (MapEntityPrefab.Find(null, prefabIdentifier, showErrorMessages: false) is ItemPrefab itemPrefab && amount > 0)
1194  {
1195  if (ExtraCargo.Keys.Count() >= MaxExtraCargoItemTypes) { continue; }
1196  if (ExtraCargo.ContainsKey(itemPrefab) && ExtraCargo[itemPrefab] >= MaxExtraCargoItemsOfType) { continue; }
1197  if (changed || !ExtraCargo.ContainsKey(itemPrefab) || ExtraCargo[itemPrefab] != amount) { changed = true; }
1198  extraCargo.Add(itemPrefab, amount);
1199  }
1200  }
1201  if (changed) { ExtraCargo = extraCargo; }
1202  return changed;
1203  }
1204 
1206  {
1207  if (ExtraCargo == null)
1208  {
1209  msg.WriteUInt32((UInt32)0);
1210  return;
1211  }
1212 
1213  msg.WriteUInt32((UInt32)ExtraCargo.Count);
1214  foreach (KeyValuePair<ItemPrefab, int> kvp in ExtraCargo)
1215  {
1216  msg.WriteIdentifier(kvp.Key.Identifier);
1217  msg.WriteByte((byte)kvp.Value);
1218  }
1219  }
1220 
1221  public void ReadHiddenSubs(IReadMessage msg)
1222  {
1223  var subList = GameMain.NetLobbyScreen.GetSubList();
1224 
1225  HiddenSubs.Clear();
1226  uint count = msg.ReadVariableUInt32();
1227  for (int i = 0; i < count; i++)
1228  {
1229  int index = msg.ReadUInt16();
1230  if (index >= subList.Count) { continue; }
1231  string submarineName = subList[index].Name;
1232  HiddenSubs.Add(submarineName);
1233  }
1234 
1235 #if SERVER
1236  SelectNonHiddenSubmarine();
1237 #endif
1238  }
1239 
1241  {
1242  var subList = GameMain.NetLobbyScreen.GetSubList();
1243 
1244  msg.WriteVariableUInt32((uint)HiddenSubs.Count);
1245  foreach (string submarineName in HiddenSubs)
1246  {
1247  msg.WriteUInt16((UInt16)subList.FindIndex(s => s.Name.Equals(submarineName, StringComparison.OrdinalIgnoreCase)));
1248  }
1249  }
1250 
1251  public void UpdateServerListInfo(Action<Identifier, object> setter)
1252  {
1253  void set(string key, object obj) => setter(key.ToIdentifier(), obj);
1254 
1255  set("ServerName", ServerName);
1256  set("MaxPlayers", MaxPlayers);
1257  set("HasPassword", HasPassword);
1258  set("message", ServerMessageText);
1259  set("version", GameMain.Version);
1260  set("playercount", GameMain.NetworkMember.ConnectedClients.Count);
1261  set("contentpackages", ContentPackageManager.EnabledPackages.All.Where(p => p.HasMultiplayerSyncedContent));
1262  set("modeselectionmode", ModeSelectionMode);
1263  set("subselectionmode", SubSelectionMode);
1264  set("voicechatenabled", VoiceChatEnabled);
1265  set("allowspectating", AllowSpectating);
1266  set("allowrespawn", RespawnMode is RespawnMode.MidRound or RespawnMode.BetweenRounds);
1267  set("traitors", TraitorProbability.ToString(CultureInfo.InvariantCulture));
1268  set("friendlyfireenabled", AllowFriendlyFire);
1269  set("karmaenabled", KarmaEnabled);
1270  set("gamestarted", GameMain.NetworkMember.GameStarted);
1271  set("gamemode", GameModeIdentifier);
1272  set("playstyle", PlayStyle);
1273  set("language", Language.ToString());
1274 #if SERVER
1275  set("eoscrossplay", EosInterface.Core.IsInitialized);
1276 #else
1277  set("eoscrossplay", EosInterface.IdQueries.IsLoggedIntoEosConnect || Eos.EosSessionManager.CurrentOwnedSession.IsSome());
1278 #endif
1279  if (GameMain.NetLobbyScreen?.SelectedSub != null)
1280  {
1281  set("submarine", GameMain.NetLobbyScreen.SelectedSub.Name);
1282  }
1283  if (Steamworks.SteamClient.IsLoggedOn)
1284  {
1285  string pingLocation = Steamworks.SteamNetworkingUtils.LocalPingLocation?.ToString();
1286  if (!pingLocation.IsNullOrEmpty())
1287  {
1288  set("steampinglocation", pingLocation);
1289  }
1290  }
1291  }
1292  }
1293 }
static readonly PrefabCollection< CharacterPrefab > Prefabs
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
Definition: GUIComponent.cs:95
RectTransform RectTransform
IEnumerable< GUIComponent > Children
Definition: GUIComponent.cs:29
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
Definition: GUIListBox.cs:33
static NetLobbyScreen NetLobbyScreen
Definition: GameMain.cs:55
static readonly Version Version
Definition: GameMain.cs:46
static NetworkMember NetworkMember
Definition: GameMain.cs:190
static GameClient Client
Definition: GameMain.cs:188
static MapEntityPrefab Find(string name, string identifier=null, bool showErrorMessages=true)
Find a matching map entity prefab
IReadOnlyList< SubmarineInfo > GetSubList()
bool HasPermission(ClientPermissions permission)
Definition: GameClient.cs:2620
SavedClientPermission(string name, Either< Address, AccountId > addressOrAccountId, ClientPermissions permissions, IEnumerable< DebugConsole.Command > permittedCommands)
float ReplaceCostPercentage
Percentage modifier for the cost of hiring a new character to replace a permanently killed one.
float SkillLossPercentageOnImmediateRespawn
How much more the skills drop towards the job's default skill levels when dying, in addition to Skill...
float DespawnDisconnectedPermadeathTime
The number of seconds a disconnected player's Character remains in the world until despawned,...
bool IronmanMode
This is an optional setting for permadeath mode. When it's enabled, a player client whose character d...
Dictionary< Identifier, SerializableProperty > SerializableProperties
float SkillLossPercentageOnDeath
How much skills drop towards the job's default skill levels when dying
bool ServerDetailsChanged
Have some of the properties listed in the server list changed
void WriteMonsterEnabled(IWriteMessage msg, Dictionary< Identifier, bool > monsterEnabled=null)
bool AllowBotTakeoverOnPermadeath
Are players allowed to take over bots when permadeath is enabled?
List< Range< int > > AllowedClientNameChars
A list of int pairs that represent the ranges of UTF-16 codes allowed in client names
float KillDisconnectedTime
The number of seconds a disconnected player's Character remains in the world until despawned (via "br...
ServerSettings(NetworkMember networkMember, string serverName, int port, int queryPort, int maxPlayers, bool isPublic, bool enableUPnP, IPAddress listenIp)
Point AbsoluteOffset
Absolute in pixels but relative to the anchor point. Calculated away from the anchor point,...
static string GetSupportedTypeName(Type type)
static Dictionary< Identifier, SerializableProperty > GetProperties(object obj)
void UpdateVoteTexts(IEnumerable< Client > clients, VoteType voteType)
override string ToString()