You've already forked lidgren-network-gen3
mirror of
https://github.com/lidgren/lidgren-network-gen3.git
synced 2026-05-06 02:11:06 +09:00
346 lines
11 KiB
C#
346 lines
11 KiB
C#
/* Copyright (c) 2010 Michael Lidgren
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
|
and associated documentation files (the "Software"), to deal in the Software without
|
|
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
|
|
the Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all copies or
|
|
substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
*/
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Net;
|
|
using System.Threading;
|
|
|
|
namespace Lidgren.Network
|
|
{
|
|
//
|
|
// This partial file holds public netpeer methods accessible to the application
|
|
//
|
|
[DebuggerDisplay("Status={m_status}")]
|
|
public partial class NetPeer
|
|
{
|
|
internal const int kMinPacketHeaderSize = 2;
|
|
internal const int kMaxPacketHeaderSize = 5;
|
|
|
|
private NetPeerStatus m_status;
|
|
private readonly object m_initializeLock = new object();
|
|
internal long m_uniqueIdentifier;
|
|
|
|
internal NetPeerConfiguration m_configuration;
|
|
internal readonly NetPeerStatistics m_statistics;
|
|
private Thread m_networkThread;
|
|
private string m_shutdownReason;
|
|
|
|
internal readonly List<NetConnection> m_connections;
|
|
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
|
|
|
|
/// <summary>
|
|
/// Gets the status of the NetPeer
|
|
/// </summary>
|
|
public NetPeerStatus Status { get { return m_status; } }
|
|
|
|
/// <summary>
|
|
/// Gets a copy of the list of connections
|
|
/// </summary>
|
|
public NetConnection[] Connections
|
|
{
|
|
get
|
|
{
|
|
lock (m_connections)
|
|
return m_connections.ToArray();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of active connections
|
|
/// </summary>
|
|
public int ConnectionsCount
|
|
{
|
|
get { return m_connections.Count; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Statistics on this NetPeer since it was initialized
|
|
/// </summary>
|
|
public NetPeerStatistics Statistics
|
|
{
|
|
get { return m_statistics; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the configuration of the netpeer
|
|
/// </summary>
|
|
public NetPeerConfiguration Configuration { get { return m_configuration; } }
|
|
|
|
/// <summary>
|
|
/// Gets the port number this NetPeer is listening and sending on
|
|
/// </summary>
|
|
public int Port { get { return m_listenPort; } }
|
|
|
|
/// <summary>
|
|
/// Gets a semi-unique identifier based on Mac address and ip/port. Note! Not available until Start has been called!
|
|
/// </summary>
|
|
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
|
|
|
|
public NetPeer(NetPeerConfiguration configuration)
|
|
{
|
|
m_status = NetPeerStatus.NotRunning;
|
|
m_configuration = configuration;
|
|
m_connections = new List<NetConnection>(m_configuration.MaximumConnections);
|
|
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>(m_configuration.MaximumConnections);
|
|
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
|
|
m_statistics = new NetPeerStatistics(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Binds to socket
|
|
/// </summary>
|
|
public void Start()
|
|
{
|
|
if (m_status != NetPeerStatus.NotRunning)
|
|
{
|
|
// already running! Just ignore...
|
|
LogWarning("Start() called on already running NetPeer - ignoring.");
|
|
return;
|
|
}
|
|
|
|
m_status = NetPeerStatus.Starting;
|
|
|
|
m_releasedIncomingMessages.Clear();
|
|
m_unsentUnconnectedMessage.Clear();
|
|
|
|
m_configuration.VerifyAndLock();
|
|
|
|
// start network thread
|
|
m_networkThread = new Thread(new ThreadStart(Run));
|
|
m_networkThread.Name = "Lidgren network thread";
|
|
m_networkThread.IsBackground = true;
|
|
m_networkThread.Start();
|
|
|
|
// allow some time for network thread to start up in case they call Connect() immediately
|
|
Thread.Sleep(3);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Read a pending message from any connection, if any
|
|
/// </summary>
|
|
public NetIncomingMessage ReadMessage()
|
|
{
|
|
if (m_status == NetPeerStatus.NotRunning)
|
|
return null;
|
|
|
|
NetIncomingMessage retval = m_releasedIncomingMessages.TryDequeue();
|
|
if (retval != null)
|
|
{
|
|
if (retval.MessageType == NetIncomingMessageType.StatusChanged)
|
|
{
|
|
NetConnectionStatus status = (NetConnectionStatus)retval.PeekByte();
|
|
retval.SenderConnection.Status = status;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
public NetIncomingMessage WaitMessage(int maxMillis)
|
|
{
|
|
if (m_messageReceivedEvent != null)
|
|
m_messageReceivedEvent.WaitOne(maxMillis);
|
|
return m_releasedIncomingMessages.TryDequeue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a connection to a remote endpoint
|
|
/// </summary>
|
|
public NetConnection Connect(string host, int port)
|
|
{
|
|
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a connection to a remote endpoint
|
|
/// </summary>
|
|
public NetConnection Connect(IPEndPoint remoteEndpoint)
|
|
{
|
|
return Connect(remoteEndpoint, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a connection to a remote endpoint
|
|
/// </summary>
|
|
public NetConnection Connect(string host, int port, NetOutgoingMessage approvalMessage)
|
|
{
|
|
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), approvalMessage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a connection to a remote endpoint
|
|
/// </summary>
|
|
public virtual NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage approvalMessage)
|
|
{
|
|
if (m_status == NetPeerStatus.NotRunning)
|
|
throw new NetException("Must call Start() first");
|
|
|
|
if (m_connectionLookup.ContainsKey(remoteEndpoint))
|
|
throw new NetException("Already connected to that endpoint!");
|
|
|
|
NetConnection conn = new NetConnection(this, remoteEndpoint);
|
|
conn.m_approvalMessage = approvalMessage;
|
|
|
|
// handle on network thread
|
|
conn.m_connectRequested = true;
|
|
conn.m_connectionInitiator = true;
|
|
|
|
lock (m_connections)
|
|
{
|
|
m_connections.Add(conn);
|
|
m_connectionLookup[remoteEndpoint] = conn;
|
|
}
|
|
|
|
return conn;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a message to an existing connection
|
|
/// </summary>
|
|
public bool SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod deliveryMethod)
|
|
{
|
|
return SendMessage(msg, recipient, deliveryMethod, 0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a message to an existing connection
|
|
/// </summary>
|
|
/// <param name="msg">The NetOutgoingMessage to send</param>
|
|
/// <param name="recipient">The recipient connection</param>
|
|
/// <param name="deliveryMethod">How to deliver the message</param>
|
|
/// <param name="channel">Delivery channel (0-31)</param>
|
|
/// <returns>True if the message was queued for delivery, else false</returns>
|
|
public bool SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod deliveryMethod, int channel)
|
|
{
|
|
if (msg.IsSent)
|
|
throw new NetException("Message has already been sent!");
|
|
if (channel < 0 || channel > 63)
|
|
throw new NetException("Channel must be between 0 and 63");
|
|
if (channel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
|
|
throw new NetException("Channel must be 0 for Unreliable and ReliableUnordered");
|
|
|
|
if (m_status != NetPeerStatus.Running)
|
|
return false;
|
|
|
|
return recipient.SendMessage(msg, deliveryMethod, channel);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a message to a number of existing connections; returns true if all recipients were sent the message
|
|
/// </summary>
|
|
/// <param name="channel">Delivery channel (0-31)</param>
|
|
public bool SendMessage(NetOutgoingMessage msg, IEnumerable<NetConnection> recipients, NetDeliveryMethod deliveryMethod, int sequenceChannel)
|
|
{
|
|
if (msg.IsSent)
|
|
throw new NetException("Message has already been sent!");
|
|
if (sequenceChannel < 0 || sequenceChannel > NetConstants.NetChannelsPerDeliveryMethod)
|
|
throw new NetException("Channel must be between 0 and " + (NetConstants.NetChannelsPerDeliveryMethod - 1));
|
|
if (sequenceChannel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
|
|
throw new NetException("Channel must be 0 for Unreliable and ReliableUnordered");
|
|
|
|
if (m_status != NetPeerStatus.Running)
|
|
return false;
|
|
|
|
msg.m_wasSent = true;
|
|
|
|
NetMessageType tp = (NetMessageType)((int)deliveryMethod + sequenceChannel);
|
|
|
|
bool all = true;
|
|
foreach (NetConnection conn in recipients)
|
|
{
|
|
if (!conn.EnqueueSendMessage(msg, tp))
|
|
all = false;
|
|
}
|
|
|
|
return all;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a message to an unconnected host
|
|
/// </summary>
|
|
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
|
|
{
|
|
if (msg.IsSent)
|
|
throw new NetException("Message has already been sent!");
|
|
|
|
IPAddress adr = NetUtility.Resolve(host);
|
|
if (adr == null)
|
|
throw new NetException("Failed to resolve " + host);
|
|
|
|
EnqueueUnconnectedMessage(msg, new IPEndPoint(adr, port));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a message to an unconnected host
|
|
/// </summary>
|
|
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
|
{
|
|
if (msg.IsSent)
|
|
throw new NetException("Message has already been sent!");
|
|
EnqueueUnconnectedMessage(msg, recipient);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a message to a number of unconnected hosts
|
|
/// </summary>
|
|
public void SendUnconnectedMessage(NetOutgoingMessage msg, IEnumerable<IPEndPoint> recipients)
|
|
{
|
|
if (msg.IsSent)
|
|
throw new NetException("Message has already been sent!");
|
|
foreach (IPEndPoint rec in recipients)
|
|
EnqueueUnconnectedMessage(msg, rec);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send a discovery response message
|
|
/// </summary>
|
|
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
|
|
{
|
|
if (msg == null)
|
|
msg = CreateMessage(0);
|
|
if (msg.IsSent)
|
|
throw new NetException("Message has already been sent!");
|
|
|
|
msg.m_libType = NetMessageLibraryType.DiscoveryResponse;
|
|
SendUnconnectedLibrary(msg, recipient);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disconnects all active connections and closes the socket
|
|
/// </summary>
|
|
public void Shutdown(string bye)
|
|
{
|
|
// called on user thread
|
|
|
|
if (m_socket == null)
|
|
return; // already shut down
|
|
|
|
LogDebug("Shutdown requested");
|
|
m_shutdownReason = bye;
|
|
m_status = NetPeerStatus.ShutdownRequested;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return "[NetPeer bound to " + m_socket.LocalEndPoint + " " + ConnectionsCount + " connections]";
|
|
}
|
|
}
|
|
}
|