using Microsoft.Xna.Framework.GamerServices; using System; using System.Collections.Generic; using System.Diagnostics; using System.Net; namespace Microsoft.Xna.Framework.Net { /// /// Represents a player in a network session. /// public class NetworkGamer { private static NetworkGamer localGamer; private readonly NetworkSession session; private readonly string id; private readonly bool isLocal; private readonly bool isHost; private string gamertag; private bool isReady; private object tag; // Network performance properties private TimeSpan roundtripTime = TimeSpan.Zero; /// /// Gets the unique identifier for this gamer. /// public string Id => id; /// /// Gets whether this gamer is the local player. /// public bool IsLocal => isLocal; /// /// Gets whether this gamer is the host of the session. /// public bool IsHost => isHost; /// /// Gets or sets the gamertag for this gamer. /// public string Gamertag { get => gamertag; set => gamertag = value ?? throw new ArgumentNullException(nameof(value)); } /// /// Gets or sets whether this gamer is ready to start the game. /// public bool IsReady { get => isReady; set { if (isReady != value) { isReady = value; // Notify session of readiness change if this is the local gamer if (isLocal) { session?.NotifyReadinessChanged(this); } } } } /// /// Gets or sets custom data associated with this gamer. /// public object Tag { get => tag; set => tag = value; } /// /// Gets the machine for this network gamer. /// public NetworkMachine Machine => null; // Mock implementation /// /// Gets whether data is available to be received from this gamer. /// public bool IsDataAvailable => incomingPackets.Count > 0; /// /// Gets the SignedInGamer associated with this NetworkGamer. /// public SignedInGamer SignedInGamer => SignedInGamer.Current; /// /// Gets whether this gamer is muted by the local user. /// public bool IsMutedByLocalUser => false; // Mock implementation /// /// Gets whether this gamer is currently talking. /// public bool IsTalking => false; // Mock implementation /// /// Gets whether this gamer has voice capabilities. /// public bool HasVoice => false; // Mock implementation // Add to NetworkGamer class private readonly Queue<(byte[] Data, NetworkGamer Sender)> incomingPackets = new Queue<(byte[], NetworkGamer)>(); internal NetworkGamer(NetworkSession session, string id, bool isLocal, bool isHost, string gamertag) { this.session = session; this.id = id; this.isLocal = isLocal; this.isHost = isHost; this.gamertag = gamertag; } /// /// Receives data from this gamer. /// /// Array to receive the data into. /// The gamer who sent the data. /// The number of bytes received. public int ReceiveData(byte[] data, out NetworkGamer sender) { if (data == null) throw new ArgumentNullException(nameof(data)); sender = null; // Check if data is available if (!IsDataAvailable) return 0; var packet = incomingPackets.Dequeue(); sender = packet.Sender; int length = Math.Min(data.Length, packet.Data.Length); Array.Copy(packet.Data, data, length); return length; } public int ReceiveData(byte[] data, int offset, out NetworkGamer sender) { if (data == null) throw new ArgumentNullException(nameof(data)); if (offset < 0 || offset >= data.Length) throw new ArgumentOutOfRangeException(nameof(offset), "Offset must be within the bounds of the data array."); sender = null; // Check if data is available if (!IsDataAvailable) return 0; var packet = incomingPackets.Dequeue(); sender = packet.Sender; int length = Math.Min(data.Length - offset, packet.Data.Length); Array.Copy(packet.Data, 0, data, offset, length); return length; } /// /// Receives data from this gamer using PacketReader. /// /// Array to receive the data into. /// The gamer who sent the data. /// The number of bytes received. public int ReceiveData(PacketReader reader, out NetworkGamer sender) { // Ensure the session is valid if (session == null) throw new InvalidOperationException("Network session is not initialized."); reader = null; sender = null; // Check if data is available if (!IsDataAvailable) return 0; var packet = incomingPackets.Dequeue(); sender = packet.Sender; reader = new PacketReader(packet.Data); return packet.Data.Length; } /// /// Sends data to other gamers in the session. /// /// The data to send. /// Send options. public void SendData(byte[] data, SendDataOptions options) { if (data == null) throw new ArgumentNullException(nameof(data)); if (!Enum.IsDefined(typeof(SendDataOptions), options)) throw new ArgumentOutOfRangeException(nameof(options), "Invalid send data option."); SendDataInternal(data, options, session.AllGamers); } /// /// Sends data to specific gamers in the session. /// /// The data to send. /// Send options. /// The gamers to send to. public void SendData(byte[] data, SendDataOptions options, IEnumerable recipients) { if (data == null) throw new ArgumentNullException(nameof(data)); if (!Enum.IsDefined(typeof(SendDataOptions), options)) throw new ArgumentOutOfRangeException(nameof(options), "Invalid send data option."); SendDataInternal(data, options, recipients); } /// /// Sends data using PacketWriter. /// /// The data to send. /// Send options. public void SendData(PacketWriter data, SendDataOptions options) { if (data == null) throw new ArgumentNullException(nameof(data), "PacketWriter cannot be null."); if (!Enum.IsDefined(typeof(SendDataOptions), options)) throw new ArgumentOutOfRangeException(nameof(options), "Invalid send data option."); byte[] serializedData = data.GetData(); SendDataInternal(serializedData, options, session.AllGamers); } /// /// Sends data using PacketWriter to specific recipients. /// /// The data to send. /// Send options. /// The gamers to send to. public void SendData(PacketWriter data, SendDataOptions options, IEnumerable recipients) { if (data == null) throw new ArgumentNullException(nameof(data), "PacketWriter cannot be null."); if (recipients == null) throw new ArgumentNullException(nameof(recipients)); byte[] serializedData = data.GetData(); SendDataInternal(serializedData, options, recipients); } /// /// Sends data using PacketWriter to a specific recipient. /// /// The data to send. /// Send options. /// The gamer to send to. public void SendData(PacketWriter data, SendDataOptions options, NetworkGamer recipient) { SendData(data, options, new[] { recipient }); } /// /// Sends byte array data to a specific recipient. /// /// The data to send. /// Send options. /// The gamer to send to. public void SendData(byte[] data, SendDataOptions options, NetworkGamer recipient) { SendData(data, options, new[] { recipient }); } /// /// Gets the current round-trip time for network communication with this gamer. /// public TimeSpan RoundtripTime => roundtripTime; /// /// Updates the round-trip time (internal use). /// internal void UpdateRoundtripTime(TimeSpan newRoundtripTime) { roundtripTime = newRoundtripTime; } public override string ToString() { return $"{Gamertag} ({(IsLocal ? "Local" : "Remote")}, {(IsHost ? "Host" : "Player")})"; } public override bool Equals(object obj) { return obj is NetworkGamer other && Id == other.Id; } public override int GetHashCode() { return Id.GetHashCode(); } /// /// Gets the local gamer. /// public static NetworkGamer LocalGamer { get => localGamer; internal set => localGamer = value; } /// /// Implicit conversion from NetworkGamer to SignedInGamer. /// public static implicit operator SignedInGamer(NetworkGamer gamer) { return gamer?.SignedInGamer; } private void SendDataInternal(byte[] data, SendDataOptions options, IEnumerable recipients) { switch (session.SessionType) { case NetworkSessionType.SystemLink: foreach (var recipient in recipients) { session.SendDataToGamer(recipient, data, options); } break; case NetworkSessionType.Local: // TODO: session.ProcessIncomingMessages(); // Simulate message delivery break; case NetworkSessionType.PlayerMatch: // Placeholder for future implementation throw new NotImplementedException("PlayerMatch session type is not yet supported."); case NetworkSessionType.Ranked: // Placeholder for future implementation throw new NotImplementedException("Ranked session type is not yet supported."); default: throw new ArgumentOutOfRangeException(nameof(session.SessionType), $"Unsupported session type: {session.SessionType}"); } } internal void EnqueueIncomingPacket(byte[] data, NetworkGamer sender) { lock (incomingPackets) { incomingPackets.Enqueue((data, sender)); } } } }