#region Copyright & License Information /* * Copyright 2007-2022 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System; using System.Collections.Generic; using System.Linq; using OpenRA.Network; using OpenRA.Primitives; namespace OpenRA { public class GameInformation { public string Mod; public string Version; public string MapUid; public string MapTitle; public int FinalGameTick; /// Game start timestamp (when the recoding started). public DateTime StartTimeUtc; /// Game end timestamp (when the recoding stopped). public DateTime EndTimeUtc; /// Gets the game's duration, from the time the game started until the replay recording stopped. public TimeSpan Duration => EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; public IList Players { get; } public HashSet DisabledSpawnPoints = new HashSet(); public MapPreview MapPreview => Game.ModData.MapCache[MapUid]; public IEnumerable HumanPlayers { get { return Players.Where(p => p.IsHuman); } } public bool IsSinglePlayer => HumanPlayers.Count() == 1; readonly Dictionary playersByRuntime; public GameInformation() { Players = new List(); playersByRuntime = new Dictionary(); } public static GameInformation Deserialize(string data) { try { var info = new GameInformation(); var nodes = MiniYaml.FromString(data); foreach (var node in nodes) { var keyParts = node.Key.Split('@'); switch (keyParts[0]) { case "Root": FieldLoader.Load(info, node.Value); break; case "Player": info.Players.Add(FieldLoader.Load(node.Value)); break; } } return info; } catch (YamlException) { Log.Write("debug", $"GameInformation deserialized invalid MiniYaml:\n{data}"); throw; } } public string Serialize() { var nodes = new List { new MiniYamlNode("Root", FieldSaver.Save(this)) }; for (var i = 0; i < Players.Count; i++) nodes.Add(new MiniYamlNode($"Player@{i}", FieldSaver.Save(Players[i]))); return nodes.WriteToString(); } /// Adds the player information at start-up. public void AddPlayer(OpenRA.Player runtimePlayer, Session lobbyInfo) { if (runtimePlayer == null) throw new ArgumentNullException(nameof(runtimePlayer)); if (lobbyInfo == null) throw new ArgumentNullException(nameof(lobbyInfo)); // We don't care about spectators and map players if (runtimePlayer.NonCombatant || !runtimePlayer.Playable) return; // Find the lobby client that created the runtime player var client = lobbyInfo.ClientWithIndex(runtimePlayer.ClientIndex); if (client == null) return; var player = new Player { ClientIndex = runtimePlayer.ClientIndex, Name = runtimePlayer.PlayerName, IsHuman = !runtimePlayer.IsBot, IsBot = runtimePlayer.IsBot, FactionName = runtimePlayer.Faction.Name, FactionId = runtimePlayer.Faction.InternalName, DisplayFactionName = runtimePlayer.DisplayFaction.Name, DisplayFactionId = runtimePlayer.DisplayFaction.InternalName, Color = runtimePlayer.Color, Team = client.Team, Handicap = client.Handicap, SpawnPoint = runtimePlayer.SpawnPoint, IsRandomFaction = runtimePlayer.Faction.InternalName != client.Faction, IsRandomSpawnPoint = runtimePlayer.DisplaySpawnPoint == 0, Fingerprint = client.Fingerprint }; playersByRuntime.Add(runtimePlayer, player); Players.Add(player); } /// Gets the player information for the specified runtime player instance. public Player GetPlayer(OpenRA.Player runtimePlayer) { playersByRuntime.TryGetValue(runtimePlayer, out var player); return player; } public class Player { #region Start-up information public int ClientIndex; /// The player name, not guaranteed to be unique. public string Name; public bool IsHuman; public bool IsBot; /// The faction's display name. public string FactionName; /// The faction ID, a.k.a. the faction's internal name. public string FactionId; public Color Color; /// The faction (including Random, etc.) that was selected in the lobby. public string DisplayFactionName; public string DisplayFactionId; /// The team ID on start-up, or 0 if the player is not part of a team. public int Team; public int SpawnPoint; public int Handicap; /// True if the faction was chosen at random; otherwise, false. public bool IsRandomFaction; /// True if the spawn point was chosen at random; otherwise, false. public bool IsRandomSpawnPoint; /// Player authentication fingerprint for the OpenRA forum. public string Fingerprint; #endregion #region /// The game outcome for this player. public WinState Outcome; /// The time when this player won or lost the game. public DateTime OutcomeTimestampUtc; /// The frame at which this player disconnected. public int DisconnectFrame; #endregion } } }