瀏覽代碼

Fix some APIs

CartBlanche 6 天之前
父節點
當前提交
6b9b8d64a8

+ 10 - 0
MonoGame.Xna.Framework.Net/Net/INetworkTransport.cs

@@ -34,5 +34,15 @@ namespace Microsoft.Xna.Framework.Net
         /// <param name="endpoint">The endpoint to send the data to.</param>
         /// <returns>A task that represents the asynchronous operation.</returns>
         Task SendAsync(byte[] data, IPEndPoint endpoint);
+
+        /// <summary>
+        /// Binds the transport to a local endpoint for receiving data.
+        /// </summary>
+        void Bind();
+
+        /// <summary>
+        /// Indicates whether the transport is currently bound to a local endpoint.
+        /// </summary>
+        bool IsBound { get; }
     }
 }

+ 28 - 7
MonoGame.Xna.Framework.Net/Net/NetworkGamer.cs

@@ -142,13 +142,34 @@ namespace Microsoft.Xna.Framework.Net
             return length;
         }
 
-        /// <summary>
-        /// Receives data from this gamer using PacketReader.
-        /// </summary>
-        /// <param name="data">Array to receive the data into.</param>
-        /// <param name="sender">The gamer who sent the data.</param>
-        /// <returns>The number of bytes received.</returns>
-        public int ReceiveData(out PacketReader reader, out NetworkGamer sender)
+		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;
+		}
+
+		/// <summary>
+		/// Receives data from this gamer using PacketReader.
+		/// </summary>
+		/// <param name="data">Array to receive the data into.</param>
+		/// <param name="sender">The gamer who sent the data.</param>
+		/// <returns>The number of bytes received.</returns>
+		public int ReceiveData(PacketReader reader, out NetworkGamer sender)
         {
             // Ensure the session is valid
             if (session == null)

+ 61 - 18
MonoGame.Xna.Framework.Net/Net/NetworkSession.cs

@@ -36,7 +36,32 @@ namespace Microsoft.Xna.Framework.Net
         // Events
         public event EventHandler<GameStartedEventArgs> GameStarted;
         public event EventHandler<GameEndedEventArgs> GameEnded;
-        public event EventHandler<GamerJoinedEventArgs> GamerJoined;
+
+        private event EventHandler<GamerJoinedEventArgs> gamerJoined;
+        private bool isGamerJoinedSubscribed = false;
+        public event EventHandler<GamerJoinedEventArgs> GamerJoined
+        {
+            add
+            {
+                lock (lockObject)
+                {
+                    gamerJoined += value;
+                    isGamerJoinedSubscribed = true;
+
+                    // Notify pending gamers if this is the first subscription
+                    NotifyPendingGamers();
+                }
+            }
+            remove
+            {
+                lock (lockObject)
+                {
+                    gamerJoined -= value;
+                    isGamerJoinedSubscribed = gamerJoined != null;
+                }
+            }
+        }
+
         public event EventHandler<GamerLeftEventArgs> GamerLeft;
         public event EventHandler<NetworkSessionEndedEventArgs> SessionEnded;
 
@@ -381,15 +406,15 @@ namespace Microsoft.Xna.Framework.Net
             return JoinAsync(availableSession).GetAwaiter().GetResult();
         }
 
-		public static NetworkSession JoinInvited(IEnumerable<SignedInGamer> localGamers, object state = null)
-		{
-			return JoinInvitedAsync(localGamers, state).GetAwaiter().GetResult();
-		}
+        public static NetworkSession JoinInvited(IEnumerable<SignedInGamer> localGamers, object state = null)
+        {
+            return JoinInvitedAsync(localGamers, state).GetAwaiter().GetResult();
+        }
 
-		/// <summary>
-		/// Updates the network session.
-		/// </summary>
-		public void Update()
+        /// <summary>
+        /// Updates the network session.
+        /// </summary>
+        public void Update()
         {
             if (disposed)
                 return;
@@ -491,6 +516,12 @@ namespace Microsoft.Xna.Framework.Net
             }
         }
 
+        public void AddLocalGamer(SignedInGamer gamer)
+        {
+            throw new NotImplementedException();
+        }
+
+
         private void AddGamer(NetworkGamer gamer)
         {
             lock (lockObject)
@@ -513,18 +544,19 @@ namespace Microsoft.Xna.Framework.Net
         // Modern async receive loop for SystemLink
         private async Task ReceiveLoopAsync(CancellationToken cancellationToken)
         {
-            using var udpClient = new UdpClient();
-            udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0)); // Use a dynamic port for tests
             while (!cancellationToken.IsCancellationRequested)
             {
                 try
                 {
-                    var result = await udpClient.ReceiveAsync();
-                    var data = result.Buffer;
-                    if (data.Length > 1)
+                    // Ensure the network transport is bound before receiving data
+                    if (!networkTransport.IsBound)
                     {
-                        var senderEndpoint = result.RemoteEndPoint;
+                        networkTransport.Bind();
+                    }
 
+                    var (data, senderEndpoint) = await networkTransport.ReceiveAsync();
+                    if (data.Length > 1)
+                    {
                         NetworkGamer senderGamer = null;
                         lock (lockObject)
                         {
@@ -540,7 +572,7 @@ namespace Microsoft.Xna.Framework.Net
                         var reader = new PacketReader(data); // Use only the byte array
                         var message = NetworkMessageRegistry.CreateMessage(typeId);
                         message?.Deserialize(reader);
-                        OnMessageReceived(new MessageReceivedEventArgs(message, result.RemoteEndPoint));
+                        OnMessageReceived(new MessageReceivedEventArgs(message, senderEndpoint));
                     }
                 }
                 catch (ObjectDisposedException) { break; }
@@ -571,7 +603,7 @@ namespace Microsoft.Xna.Framework.Net
 
         private void OnGamerJoined(NetworkGamer gamer)
         {
-            GamerJoined?.Invoke(this, new GamerJoinedEventArgs(gamer));
+            gamerJoined?.Invoke(this, new GamerJoinedEventArgs(gamer));
         }
 
         private void OnGamerLeft(NetworkGamer gamer)
@@ -693,7 +725,7 @@ namespace Microsoft.Xna.Framework.Net
                 while (gamer.IsDataAvailable)
                 {
                     var reader = new PacketReader();
-                    gamer.ReceiveData(out reader, out var sender);
+                    gamer.ReceiveData(reader, out var sender);
 
                     var messageType = reader.ReadString();
                     if (messageType == "SessionPropertiesUpdate")
@@ -704,5 +736,16 @@ namespace Microsoft.Xna.Framework.Net
                 }
             }
         }
+        private void NotifyPendingGamers()
+        {
+            if (isGamerJoinedSubscribed && sessionState == NetworkSessionState.Lobby)
+            {
+                // Notify all pending gamers that they can join
+                foreach (var gamer in gamers/*.Where(g => !g.IsLocal && !g.IsReady)*/)
+                {
+                    gamerJoined?.Invoke(this, new GamerJoinedEventArgs(gamer));
+                }
+			}
+		}
     }
 }

+ 18 - 0
MonoGame.Xna.Framework.Net/Net/UdpTransport.cs

@@ -10,6 +10,7 @@ namespace Microsoft.Xna.Framework.Net
     public class UdpTransport : INetworkTransport
     {
         private readonly UdpClient udpClient;
+        private bool isBound;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="UdpTransport"/> class.
@@ -69,5 +70,22 @@ namespace Microsoft.Xna.Framework.Net
             udpClient?.Close();
             udpClient?.Dispose();
         }
+
+        /// <summary>
+        /// Binds the transport to a local endpoint.
+        /// </summary>
+        public void Bind()
+        {
+            if (!isBound)
+            {
+                udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 0));
+                isBound = true;
+            }
+        }
+
+        /// <summary>
+        /// Gets a value indicating whether the transport is bound to a local endpoint.
+        /// </summary>
+        public bool IsBound => isBound;
     }
 }