3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
7 using System.Globalization;
10 using System.Security.Cryptography;
42 internal enum LootedMoneyDestination
51 PacketLimitWarning = 3500,
52 PacketLimitDefault = 4000,
53 PacketLimitMax = 10000;
67 public static readonly
string PermissionPresetFile =
"Data" + Path.DirectorySeparatorChar +
"permissionpresets.xml";
71 get {
return "ServerSettings"; }
82 public readonly
string Name;
90 this.AddressOrAccountId = addressOrAccountId;
91 this.Permissions = permissions;
96 partial class NetPropertyData
99 private readonly
string typeString;
100 private readonly
object parentObject;
102 public Identifier
Name =>
property.Name.ToIdentifier();
106 get {
return property.GetValue(parentObject); }
107 set {
property.SetValue(parentObject, value); }
110 public NetPropertyData(
object parentObject, SerializableProperty property,
string typeString)
112 this.
property = property;
113 this.typeString = typeString;
114 this.parentObject = parentObject;
117 public bool PropEquals(
object a,
object b)
122 if (a is not
float fa) {
return false; }
123 if (b is not
float fb) {
return false; }
124 return MathUtils.NearlyEqual(fa, fb);
126 if (a is not
int ia) {
return false; }
127 if (b is not
int ib) {
return false; }
130 if (a is not
bool ba) {
return false; }
131 if (b is not
bool bb) {
return false; }
134 if (a is not Enum ea) {
return false; }
135 if (b is not Enum eb) {
return false; }
136 return ea.Equals(eb);
138 return ReferenceEquals(a,b)
139 ||
string.Equals(a?.ToString(), b?.ToString(), StringComparison.OrdinalIgnoreCase);
143 public void Read(IReadMessage msg)
145 int oldPos = msg.BitPosition;
146 UInt32 size = msg.ReadVariableUInt32();
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;
155 if (size != 4)
break;
156 property.SetValue(parentObject, msg.ReadSingle());
159 if (size != 4)
break;
160 property.SetValue(parentObject, msg.ReadInt32());
163 if (size != 8)
break;
164 x = msg.ReadSingle();
165 y = msg.ReadSingle();
166 property.SetValue(parentObject,
new Vector2(x, y));
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));
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));
184 if (size != 4)
break;
189 property.SetValue(parentObject,
new Color(r, g, b, a));
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));
200 msg.BitPosition = oldPos;
201 string incVal = msg.ReadString();
202 property.TrySetValue(parentObject, incVal);
207 msg.BitPosition += (int)(8 * size);
210 public void Write(IWriteMessage msg,
object overrideValue =
null)
212 overrideValue ??= Value;
216 msg.WriteVariableUInt32(4);
217 msg.WriteSingle((
float)overrideValue);
220 msg.WriteVariableUInt32(4);
221 msg.WriteInt32((
int)overrideValue);
224 msg.WriteVariableUInt32(8);
225 msg.WriteSingle(((Vector2)overrideValue).X);
226 msg.WriteSingle(((Vector2)overrideValue).Y);
229 msg.WriteVariableUInt32(12);
230 msg.WriteSingle(((Vector3)overrideValue).X);
231 msg.WriteSingle(((Vector3)overrideValue).Y);
232 msg.WriteSingle(((Vector3)overrideValue).Z);
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);
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);
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);
256 string strVal = overrideValue.ToString();
258 msg.WriteString(strVal);
270 private readonly Dictionary<UInt32, NetPropertyData> netProperties;
272 partial
void InitProjSpecific();
274 public ServerSettings(NetworkMember networkMember,
string serverName,
int port,
int queryPort,
int maxPlayers,
bool isPublic,
bool enableUPnP, IPAddress listenIp)
280 ExtraCargo =
new Dictionary<ItemPrefab, int>();
295 netProperties =
new Dictionary<UInt32, NetPropertyData>();
297 using (MD5 md5 = MD5.Create())
300 foreach (var property
in saveProperties)
303 if (typeName !=
null || property.PropertyType.IsEnum)
305 NetPropertyData netPropertyData =
new NetPropertyData(
this, property, typeName);
306 UInt32 key = ToolBoxCore.IdentifierToUint32Hash(netPropertyData.Name, md5);
307 if (key == 0) { key++; }
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);
314 foreach (var property
in karmaProperties)
316 object value =
property.GetValue(networkMember.KarmaManager);
317 if (value ==
null) {
continue; }
320 if (typeName !=
null || property.PropertyType.IsEnum)
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);
331 private string serverName =
string.Empty;
336 get {
return serverName; }
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;
348 private string serverMessageText;
353 get {
return serverMessageText; }
357 if (val.Length > NetConfig.ServerMessageMaxLength) { val = val.Substring(0, NetConfig.ServerMessageMaxLength); }
358 if (serverMessageText == val) {
return; }
360 GameMain.Server?.SendChatMessage(TextManager.AddPunctuation(
':', TextManager.Get(
"servermotd"), val).Value,
ChatMessageType.Server);
364 serverMessageButton.Flash(GUIStyle.Green);
365 serverMessageButton.Pulsate(Vector2.One, Vector2.One * 1.2f, 1.0f);
368 serverMessageText = val;
387 public Dictionary<ItemPrefab, int>
ExtraCargo {
get;
private set; }
391 private float selectedLevelDifficulty;
392 private string password;
396 private bool autoRestart;
398 private int maxPlayers;
400 public List<SavedClientPermission>
ClientPermissions {
get;
private set; } =
new List<SavedClientPermission>();
415 get {
return tickRate; }
416 set { tickRate = MathHelper.Clamp(value, 1, 60); }
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.")]
535 private bool allowSpectating;
539 get {
return allowSpectating; }
542 if (allowSpectating == value) {
return; }
543 allowSpectating = value;
569 private bool voiceChatEnabled;
573 get {
return voiceChatEnabled; }
576 if (voiceChatEnabled == value) {
return; }
577 voiceChatEnabled = value;
586 get {
return playstyleSelection; }
589 playstyleSelection = value;
624 get {
return autoRestart; }
635 get {
return !
string.IsNullOrEmpty(password); }
639 password = value ? (password ??
"_") :
null;
660 get {
return respawnMode; }
663 if (respawnMode == value) {
return; }
702 get {
return selectedLevelDifficulty; }
703 set { selectedLevelDifficulty = MathHelper.Clamp(value, 0.0f, 100.0f); }
810 private float traitorProbability;
814 get {
return traitorProbability; }
817 if (MathUtils.NearlyEqual(traitorProbability, value)) {
return; }
818 traitorProbability = MathHelper.Clamp(value, 0.0f, 1.0f);
824 private int traitorDangerLevel;
828 get {
return traitorDangerLevel; }
832 if (traitorDangerLevel == clampedValue) {
return; }
833 traitorDangerLevel = clampedValue;
841 private int traitorsMinPlayerCount;
845 get {
return traitorsMinPlayerCount; }
846 set { traitorsMinPlayerCount = MathHelper.Clamp(value, 1, NetConfig.MaxPlayers); }
863 get {
return subSelectionMode; }
866 subSelectionMode = value;
876 get {
return modeSelectionMode; }
879 modeSelectionMode = value;
958 private bool karmaEnabled;
962 get {
return karmaEnabled; }
965 karmaEnabled = value;
967 if (karmaSettingsList !=
null)
969 SetElementInteractability(karmaSettingsList.
Content, !karmaEnabled ||
KarmaPreset !=
"custom");
971 karmaElements.ForEach(e => e.Visible = karmaEnabled);
976 private string karmaPreset =
"default";
980 get {
return karmaPreset; }
983 if (karmaPreset == value) {
return; }
1009 get {
return maxPlayers; }
1010 set { maxPlayers = MathHelper.Clamp(value, 0, NetConfig.MaxPlayers); }
1034 public LootedMoneyDestination LootedMoneyDestination {
get;
set; }
1044 private bool allowSubVoting;
1048 get {
return allowSubVoting; }
1051 if (value == allowSubVoting) {
return; }
1052 allowSubVoting = value;
1057 subVotesLabel.
Visible = value;
1060 =
new Point(value ? (
int)(subVotesLabel.TextSize.X + subVisButton.Rect.Width) : 0, 0);
1068 private bool allowModeVoting;
1072 get {
return allowModeVoting; }
1075 if (value == allowModeVoting) {
return; }
1076 allowModeVoting = value;
1085 if (guiComponent is
GUIFrame frame)
1087 frame.CanBeFocused = !allowModeVoting || ((
GameModePreset)frame.UserData).Votable;
1099 this.password =
string.IsNullOrEmpty(password) ? null : password;
1104 byte[] saltedPw =
new byte[password.Length*2];
1105 for (
int i = 0; i < password.Length; i++)
1107 saltedPw[(i * 2)] = password[i];
1108 saltedPw[(i * 2) + 1] = (
byte)((salt >> (8 * (i % 4))) & 0xff);
1110 saltedPw = Lidgren.Network.NetUtility.ComputeSHAHash(saltedPw);
1117 byte[] saltedPw =
SaltPassword(Encoding.UTF8.GetBytes(password), salt);
1118 return saltedPw.SequenceEqual(input);
1128 } =
new List<Range<int>>();
1130 private void InitMonstersEnabled()
1139 private static IReadOnlyList<Identifier> ExtractAndSortKeys(IReadOnlyDictionary<Identifier, bool> monsterEnabled)
1140 => monsterEnabled.Keys
1141 .OrderBy(k => CharacterPrefab.Prefabs[k].UintIdentifier)
1142 .ToImmutableArray();
1146 bool changed =
false;
1147 InitMonstersEnabled();
1150 if (monsterNames.Count != receivedMonsterCount)
1153 DebugConsole.AddWarning($
"Expected monster count {monsterNames.Count}, got {receivedMonsterCount}");
1157 foreach (Identifier s
in monsterNames)
1171 InitMonstersEnabled();
1173 var monsterNames = ExtractAndSortKeys(monsterEnabled);
1175 foreach (Identifier s
in monsterNames)
1177 msg.WriteBoolean(monsterEnabled[s]);
1184 bool changed =
false;
1187 Dictionary<ItemPrefab, int> extraCargo =
new Dictionary<ItemPrefab, int>();
1188 for (
int i = 0; i < count; i++)
1197 if (changed || !
ExtraCargo.ContainsKey(itemPrefab) ||
ExtraCargo[itemPrefab] != amount) { changed =
true; }
1198 extraCargo.Add(itemPrefab, amount);
1214 foreach (KeyValuePair<ItemPrefab, int> kvp
in ExtraCargo)
1216 msg.WriteIdentifier(kvp.Key.Identifier);
1217 msg.WriteByte((
byte)kvp.Value);
1227 for (
int i = 0; i < count; i++)
1230 if (index >= subList.Count) {
continue; }
1231 string submarineName = subList[index].Name;
1236 SelectNonHiddenSubmarine();
1247 msg.WriteUInt16((UInt16)subList.FindIndex(s => s.Name.Equals(submarineName, StringComparison.OrdinalIgnoreCase)));
1253 void set(
string key,
object obj) => setter(key.ToIdentifier(), obj);
1261 set(
"contentpackages", ContentPackageManager.EnabledPackages.All.Where(p => p.HasMultiplayerSyncedContent));
1275 set(
"eoscrossplay", EosInterface.Core.IsInitialized);
1277 set(
"eoscrossplay", EosInterface.IdQueries.IsLoggedIntoEosConnect || Eos.EosSessionManager.CurrentOwnedSession.IsSome());
1283 if (Steamworks.SteamClient.IsLoggedOn)
1285 string pingLocation = Steamworks.SteamNetworkingUtils.LocalPingLocation?.ToString();
1286 if (!pingLocation.IsNullOrEmpty())
1288 set(
"steampinglocation", pingLocation);
static readonly PrefabCollection< CharacterPrefab > Prefabs
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
RectTransform RectTransform
IEnumerable< GUIComponent > Children
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
static NetLobbyScreen NetLobbyScreen
static readonly Version Version
static NetworkMember NetworkMember
static MapEntityPrefab Find(string name, string identifier=null, bool showErrorMessages=true)
Find a matching map entity prefab
void SetTraitorDangerLevel(int dangerLevel)
SubmarineInfo SelectedSub
GUIButton SubVisibilityButton
IReadOnlyList< SubmarineInfo > GetSubList()
GUIButton ServerMessageButton
bool HasPermission(ClientPermissions permission)
static void LoadAll(string file)
SavedClientPermission(string name, Either< Address, AccountId > addressOrAccountId, ClientPermissions permissions, IEnumerable< DebugConsole.Command > permittedCommands)
readonly ImmutableHashSet< DebugConsole.Command > PermittedCommands
readonly ClientPermissions Permissions
readonly Either< Address, AccountId > AddressOrAccountId
bool StartWhenClientsReady
bool RequireAuthentication
float ReplaceCostPercentage
Percentage modifier for the cost of hiring a new character to replace a permanently killed one.
static byte[] SaltPassword(byte[] password, int salt)
IPAddress ListenIPAddress
bool IsPasswordCorrect(byte[] input, int salt)
bool AllowImmediateItemDelivery
void UpdateServerListInfo(Action< Identifier, object > setter)
float SkillLossPercentageOnImmediateRespawn
How much more the skills drop towards the job's default skill levels when dying, in addition to Skill...
void SetPassword(string password)
void ReadHiddenSubs(IReadMessage msg)
const int MaxExtraCargoItemsOfType
float DespawnDisconnectedPermadeathTime
The number of seconds a disconnected player's Character remains in the world until despawned,...
void WriteHiddenSubs(IWriteMessage msg)
bool ReadExtraCargo(IReadMessage msg)
const int MaxExtraCargoItemTypes
float SelectedLevelDifficulty
LanguageIdentifier Language
bool DestructibleOutposts
int MaxPasswordRetriesBeforeBan
float NewCampaignDefaultSalary
static readonly string PermissionPresetFile
const string SettingsFile
bool IronmanMode
This is an optional setting for permadeath mode. When it's enabled, a player client whose character d...
CampaignSettings CampaignSettings
Dictionary< Identifier, SerializableProperty > SerializableProperties
float StartWhenClientsReadyRatio
float SkillLossPercentageOnDeath
How much skills drop towards the job's default skill levels when dying
float DisallowKickVoteTime
SelectionMode ModeSelectionMode
bool ServerDetailsChanged
Have some of the properties listed in the server list changed
int MaximumMoneyTransferRequest
void WriteMonsterEnabled(IWriteMessage msg, Dictionary< Identifier, bool > monsterEnabled=null)
bool AllowBotTakeoverOnPermadeath
Are players allowed to take over bots when permadeath is enabled?
int TraitorsMinPlayerCount
const int DefaultTickRate
bool AllowLinkingWifiToChat
float AutoRestartInterval
HashSet< string > HiddenSubs
bool DisableBotConversations
float MinimumMidRoundSyncTimeout
float EndVoteRequiredRatio
List< MissionType > AllowedRandomMissionTypes
Dictionary< Identifier, bool > MonsterEnabled
bool AllowDragAndDropGive
bool ReadMonsterEnabled(IReadMessage inc)
float MinPercentageOfPlayersForTraitorAccusation
SelectionMode SubSelectionMode
Identifier GameModeIdentifier
Dictionary< ItemPrefab, int > ExtraCargo
void WriteExtraCargo(IWriteMessage msg)
EnemyHealthBarMode ShowEnemyHealthBars
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...
bool BanAfterWrongPassword
ServerSettings(NetworkMember networkMember, string serverName, int port, int queryPort, int maxPlayers, bool isPublic, bool enableUPnP, IPAddress listenIp)
float KickVoteRequiredRatio
static string GetSupportedTypeName(Type type)
static Dictionary< Identifier, SerializableProperty > GetProperties(object obj)
void UpdateVoteTexts(IEnumerable< Client > clients, VoteType voteType)
UInt32 ReadVariableUInt32()
Identifier ReadIdentifier()
void WriteVariableUInt32(UInt32 val)
void WriteUInt32(UInt32 val)
override string ToString()