4 using Microsoft.Xna.Framework;
6 using System.Collections.Generic;
7 using System.Collections.Immutable;
9 using System.Reflection;
10 using System.Xml.Linq;
12 using System.Globalization;
20 public static Option<DataSource>
Parse(XElement element)
21 => ReflectionUtils.ParseDerived<
DataSource, XElement>(element);
22 public abstract void Write(XElement element);
83 public Version
GameVersion {
get;
set; } =
new Version(0, 0, 0, 0);
85 public Option<int>
Ping = Option<int>.None();
112 ContentPackages = ContentPackageManager.EnabledPackages.All.Select(p =>
new ServerListContentPackageInfo(p)).ToImmutableArray(),
132 var serverInfoSerializableProperties
134 var serverSettingsSerializableProperties
137 var intersection = serverInfoSerializableProperties.Keys
138 .Where(serverSettingsSerializableProperties.ContainsKey);
140 foreach (var key
in intersection)
142 var propToGet = serverSettingsSerializableProperties[key];
143 var propToSet = serverInfoSerializableProperties[key];
144 if (!propToGet.PropertyInfo.CanRead) {
continue; }
145 if (!propToSet.PropertyInfo.CanWrite) {
continue; }
148 propToGet.GetValue(serverSettings));
165 title.Text = ToolBox.LimitString(title.Text, title.Font, (
int)(title.Rect.Width * 0.85f));
168 TextManager.AddPunctuation(
':', TextManager.Get(
"ServerListVersion"),
178 Color playStyleBannerColor;
179 if (playStyleBannerSprite !=
null)
181 float playStyleBannerAspectRatio = (float)playStyleBannerSprite.
SourceRect.Width / (
float)playStyleBannerSprite.
SourceRect.Height;
183 playStyleBannerSprite,
null,
true);
191 DisabledColor = Color.Black,
192 OutlineColor = Color.Black,
193 PressedColor = Color.Black,
194 SelectedColor = Color.Black,
195 HoverColor = Color.Black
197 playStyleBannerColor = Color.Black;
202 { RelativeOffset = new Vector2(0.0f, 0.06f) },
203 TextManager.AddPunctuation(
':', TextManager.Get(
"serverplaystyle"),
204 TextManager.Get($
"servertag.{playStyle}")), textColor: Color.White,
205 font: GUIStyle.SmallFont, textAlignment: Alignment.Center,
206 color: playStyleBannerColor, style:
"GUISlopedHeader");
207 playStyleName.RectTransform.NonScaledSize = (playStyleName.Font.MeasureString(playStyleName.Text) +
new Vector2(20, 5) * GUI.Scale).ToPoint();
208 playStyleName.RectTransform.IsFixedSize =
true;
212 textAlignment: Alignment.TopLeft)
224 isHorizontal:
true, childAnchor:
Anchor.BottomRight);
229 Color = Color.Black * 0.7f,
230 IgnoreLayoutGroups =
true
233 bool isFavorite = serverListScreen.IsFavorite(
this);
235 => TextManager.Get(isFavorite ?
"RemoveFromFavorites" :
"AddToFavorites");
238 "",
null,
"GUIServerListFavoriteTickBox")
242 ToolTip = favoriteTickBoxToolTip(isFavorite),
243 OnSelected = tickbox =>
246 if (tickbox.Selected)
254 tickbox.ToolTip = favoriteTickBoxToolTip(tickbox.Selected);
261 ToolTip = TextManager.Get(
"reportserver"),
267 ToolTip = TextManager.Get(
"filterserver"),
268 OnClicked = (_, _) =>
277 var playStyleContainer =
new GUILayoutGroup(
new RectTransform(
new Vector2(1.0f, 0.15f), content.RectTransform), isHorizontal:
true)
280 RelativeSpacing = 0.01f,
285 foreach (var tag
in playStyleTags)
287 var playStyleIcon = GUIStyle.GetComponentStyle($
"PlayStyleIcon.{tag}")
289 if (playStyleIcon is
null) {
continue; }
292 playStyleIcon, scaleToFit:
true)
294 ToolTip = TextManager.Get($
"servertagdescription.{tag}"),
299 playStyleContainer.Recalculate();
303 const float elementHeight = 0.075f;
308 var serverMsg =
new GUIListBox(
new RectTransform(
new Vector2(1.0f, 0.3f), content.RectTransform)) { ScrollBarVisible =
true };
314 msgText.RectTransform.SizeChanged += () => { serverMsg.UpdateScrollBarSize(); };
316 var languageLabel =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get(
"Language"));
318 ServerLanguageOptions.Options.FirstOrNull(o => o.Identifier ==
Language)?.Label ?? TextManager.Get(
"Unknown"),
319 textAlignment: Alignment.Right);
321 var gameMode =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get(
"GameMode"));
324 textAlignment: Alignment.Right);
328 var submarineText =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get(
"Submarine"));
331 textAlignment: Alignment.Right);
337 var subSelection =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get(
"ServerListSubSelection"));
340 var modeSelection =
new GUITextBlock(
new RectTransform(
new Vector2(1.0f, elementHeight), content.RectTransform), TextManager.Get(
"ServerListModeSelection"));
344 subSelection.TextSize.X + subSelection.GetChild<
GUITextBlock>().
TextSize.X > subSelection.Rect.Width ||
345 modeSelection.TextSize.X + modeSelection.GetChild<
GUITextBlock>().
TextSize.X > modeSelection.Rect.Width)
347 gameMode.Font = subSelection.Font = modeSelection.Font = GUIStyle.SmallFont;
352 var allowSpectating =
new GUITickBox(
new RectTransform(
new Vector2(1, elementHeight), content.RectTransform), TextManager.Get(
"ServerListAllowSpectating"))
358 var allowRespawn =
new GUITickBox(
new RectTransform(
new Vector2(1, elementHeight), content.RectTransform), TextManager.Get(
"ServerSettingsAllowRespawning"))
365 TextManager.Get(
"ServerListContentPackages"), textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont);
369 ScrollBarVisible =
true,
370 OnSelected = (component, o) =>
false
374 new GUITextBlock(
new RectTransform(Vector2.One, contentPackageList.Content.RectTransform), TextManager.Get(
"Unknown"), textAlignment: Alignment.Center)
384 new RectTransform(
new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform)
385 { MinSize = new Point(0, 15) },
390 if (!
string.IsNullOrEmpty(package.Hash))
392 if (ContentPackageManager.AllPackages.Any(contentPackage => contentPackage.Hash.StringRepresentation == package.Hash))
395 packageText.Selected =
true;
398 else if (package.Id.TryUnwrap(out var ugcId) && ugcId is
SteamWorkshopId)
400 packageText.ToolTip = TextManager.GetWithVariable(
"ServerListIncompatibleContentPackageWorkshopAvailable",
"[contentpackage]", package.Name);
405 packageText.ToolTip = TextManager.GetWithVariables(
"ServerListIncompatibleContentPackage",
406 (
"[contentpackage]", package.Name), (
"[hash]", package.Hash));
413 new RectTransform(
new Vector2(1.0f, 0.15f), contentPackageList.Content.RectTransform) { MinSize = new Point(0, 15) },
431 yield
return $
"Karma.{KarmaEnabled}".ToIdentifier();
432 yield
return (
TraitorProbability > 0.0f ? $
"Traitors.True" : $
"Traitors.False").ToIdentifier();
433 yield
return $
"VoIP.{VoipEnabled}".ToIdentifier();
434 yield
return $
"FriendlyFire.{FriendlyFireEnabled}".ToIdentifier();
435 yield
return $
"Modded.{IsModded}".ToIdentifier();
441 if (Version.TryParse(valueGetter(
"version"), out var version))
445 if (
int.TryParse(valueGetter(
"playercount"), out
int playerCount)) {
PlayerCount = playerCount; }
447 if (
int.TryParse(valueGetter(
"maxplayers"), out
int maxPlayers)) {
MaxPlayers = maxPlayers; }
448 else if (
int.TryParse(valueGetter(
"maxplayernum"), out maxPlayers)) {
MaxPlayers = maxPlayers; }
463 GameMode = valueGetter(
"gamemode")?.ToIdentifier() ?? Identifier.Empty;
464 if (
float.TryParse(valueGetter(
"traitors"), NumberStyles.Any, CultureInfo.InvariantCulture, out
float traitorProbability)) {
TraitorProbability = traitorProbability; }
465 if (Enum.TryParse(valueGetter(
"playstyle"), out
PlayStyle playStyle)) {
PlayStyle = playStyle; }
467 SelectedSub = valueGetter(
"submarine") ??
string.Empty;
471 if (
int.TryParse(valueGetter(
"packagecount"), out
int packageCount)) {
ContentPackageCount = packageCount; }
473 bool getBool(
string key)
475 string? data = valueGetter(key);
476 return bool.TryParse(data, out var result) && result;
480 private static ServerListContentPackageInfo[] ExtractContentPackageInfo(
string serverName, Func<string, string?> valueGetter)
483 int individualPackageIndex = 0;
484 string? individualPackage = valueGetter($
"contentpackage{individualPackageIndex}");
485 if (!individualPackage.IsNullOrEmpty())
487 List<ServerListContentPackageInfo> contentPackages =
new List<ServerListContentPackageInfo>();
490 if (!ServerListContentPackageInfo.ParseSingleEntry(individualPackage).TryUnwrap(out var info))
492 return Array.Empty<ServerListContentPackageInfo>();
494 contentPackages.Add(info);
496 individualPackageIndex++;
497 individualPackage = valueGetter($
"contentpackage{individualPackageIndex}");
498 }
while (!individualPackage.IsNullOrEmpty());
499 return contentPackages.ToArray();
502 string? joinedNames = valueGetter(
"contentpackage");
503 string? joinedHashes = valueGetter(
"contentpackagehash");
504 string? joinedUgcIds = valueGetter(
"contentpackageid");
506 var contentPackageNames = joinedNames.IsNullOrEmpty() ? Array.Empty<
string>() : joinedNames.SplitEscaped(
',');
507 var contentPackageHashes = joinedHashes.IsNullOrEmpty() ? Array.Empty<
string>() : joinedHashes.SplitEscaped(
',');
508 var contentPackageIds = joinedUgcIds.IsNullOrEmpty() ?
new string[1] {
string.Empty } : joinedUgcIds.SplitEscaped(
',');
510 if (contentPackageNames.Count != contentPackageHashes.Count || contentPackageHashes.Count != contentPackageIds.Count)
513 $
"The number of names, hashes and UGC IDs on server \"{serverName}\"" +
514 $
" doesn't match: {contentPackageNames.Count} names ({string.Join(",
", contentPackageNames)}), {contentPackageHashes.Count} hashes, {contentPackageIds.Count} ids)");
515 return Array.Empty<ServerListContentPackageInfo>();
518 return contentPackageNames
519 .Zip(contentPackageHashes, (name, hash) => (name, hash))
520 .Zip(contentPackageIds, (t1,
id) =>
521 new ServerListContentPackageInfo(
524 ContentPackageId.Parse(
id)))
530 var endpoints =
new List<Endpoint>();
533 = element.GetAttributeString(
"Endpoint",
null)
534 ?? element.GetAttributeString(
"OwnerID",
null)
535 ?? $
"{element.GetAttributeString("IP
", "")}:{element.GetAttributeInt("Port
", 0)}";
539 endpoints.Add(endpoint);
543 var multipleEndpointStrs
544 = element.GetAttributeStringArray(
"Endpoints", Array.Empty<
string>());
551 if (endpoints.Count == 0) {
return Option.None; }
553 var gameVersionStr = element.GetAttributeString(
"GameVersion",
"");
554 if (!Version.TryParse(gameVersionStr, out var gameVersion)) { gameVersion =
GameMain.
Version; }
555 var info =
new ServerInfo(endpoints.ToImmutableArray())
563 return Option.Some(info);
568 XElement element =
new XElement(GetType().Name);
570 element.SetAttributeValue(
"Endpoints",
string.Join(
",",
Endpoints.Select(e => e.StringRepresentation)));
571 element.SetAttributeValue(
"GameVersion",
GameVersion.ToString());
577 dataSource.Write(element);
589 => other.Endpoints.Any(e =>
Endpoints.Contains(e));
bool NameMatches(Identifier name)
Color GetAttributeColor(string key, in Color def)
GUIComponent GetChild(int index)
virtual void ClearChildren()
RectTransform RectTransform
IEnumerable< GUIComponent > Children
static NetLobbyScreen NetLobbyScreen
static ContentPackage VanillaContent
static readonly Version Version
static ServerListScreen ServerListScreen
readonly Identifier Identifier
static Option< Endpoint > Parse(string str)
override IReadOnlyList< Client > ConnectedClients
abstract void Write(XElement element)
static Option< DataSource > Parse(XElement element)
ImmutableArray< Endpoint > Endpoints
IEnumerable< Identifier > GetPlayStyleTags()
void UpdateInfo(Func< string, string?> valueGetter)
ImmutableArray< ServerListContentPackageInfo > ContentPackages
ServerInfo(ImmutableArray< Endpoint > endpoints)
Option< DataSource > MetadataSource
SelectionMode ModeSelectionMode
static Option< ServerInfo > FromXElement(XElement element)
override bool Equals(object? obj)
LanguageIdentifier Language
void CreatePreviewWindow(GUIFrame frame)
bool Equals(ServerInfo other)
override int GetHashCode()
static ServerInfo FromServerEndpoints(ImmutableArray< Endpoint > endpoints, ServerSettings serverSettings)
Dictionary< Identifier, SerializableProperty > SerializableProperties
SelectionMode SubSelectionMode
ServerInfo(params Endpoint[] endpoint)
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)
static Dictionary< Identifier, SerializableProperty > GetProperties(object obj)
static void SerializeProperties(ISerializableEntity obj, XElement element, bool saveIfDefault=false, bool ignoreEditable=false)
void AddToFavoriteServers(ServerInfo info)
static void CreateFilterServerPrompt(ServerInfo info)
void RemoveFromFavoriteServers(ServerInfo info)
static void CreateReportPrompt(ServerInfo info)
ContentXElement SourceElement
Reference to the xml element from where the sprite was created. Can be null if the sprite was not def...
static readonly LanguageIdentifier None
GameModePreset SelectedMode