You've already forked lidgren-network-gen3
mirror of
https://github.com/lidgren/lidgren-network-gen3.git
synced 2026-05-19 00:26:30 +09:00
major update; gen 3.5
This commit is contained in:
@@ -22,11 +22,18 @@ using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed size vector of booleans
|
||||
/// </summary>
|
||||
public sealed class NetBitVector
|
||||
{
|
||||
private readonly int m_capacity;
|
||||
private readonly int[] m_data;
|
||||
private int m_numBitsSet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bits/booleans stored in this vector
|
||||
/// </summary>
|
||||
public int Capacity { get { return m_capacity; } }
|
||||
|
||||
public NetBitVector(int bitsCapacity)
|
||||
@@ -35,12 +42,21 @@ namespace Lidgren.Network
|
||||
m_data = new int[(bitsCapacity + 31) / 32];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all bits/booleans are set to zero/false
|
||||
/// </summary>
|
||||
public bool IsEmpty()
|
||||
{
|
||||
foreach (int v in m_data)
|
||||
if (v != 0)
|
||||
return false;
|
||||
return true;
|
||||
return (m_numBitsSet == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of bits/booleans set to one/true
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int Count()
|
||||
{
|
||||
return m_numBitsSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -62,6 +78,8 @@ namespace Lidgren.Network
|
||||
cur |= firstBit << lastIndex;
|
||||
|
||||
m_data[lenMinusOne] = cur;
|
||||
|
||||
throw new NetException("TODO: update m_numBitsSet");
|
||||
}
|
||||
|
||||
public int GetFirstSetIndex()
|
||||
@@ -82,20 +100,41 @@ namespace Lidgren.Network
|
||||
return (idx * 32) + a;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bit/bool at the specified index
|
||||
/// </summary>
|
||||
public bool Get(int bitIndex)
|
||||
{
|
||||
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
|
||||
|
||||
return (m_data[bitIndex / 32] & (1 << (bitIndex % 32))) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets or clears the bit/bool at the specified index
|
||||
/// </summary>
|
||||
public void Set(int bitIndex, bool value)
|
||||
{
|
||||
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
|
||||
|
||||
int idx = bitIndex / 32;
|
||||
if (value)
|
||||
{
|
||||
if ((m_data[idx] & (1 << (bitIndex % 32))) == 0)
|
||||
m_numBitsSet++;
|
||||
m_data[idx] |= (1 << (bitIndex % 32));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_data[idx] & (1 << (bitIndex % 32))) != 0)
|
||||
m_numBitsSet--;
|
||||
m_data[idx] &= (~(1 << (bitIndex % 32)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bit/bool at the specified index
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.IndexerName("Bit")]
|
||||
public bool this[int index]
|
||||
{
|
||||
@@ -103,9 +142,13 @@ namespace Lidgren.Network
|
||||
set { Set(index, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all bits/booleans to zero/false
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(m_data, 0, m_data.Length);
|
||||
m_numBitsSet = 0;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -21,6 +21,9 @@ using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Specialized version of NetPeer used for a "client" connection. It does not accept any incoming connections and maintains a ServerConnection property
|
||||
/// </summary>
|
||||
public class NetClient : NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
@@ -53,9 +56,9 @@ namespace Lidgren.Network
|
||||
config.AcceptIncomingConnections = false;
|
||||
}
|
||||
|
||||
public override NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage approvalMessage)
|
||||
public override NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
lock(m_connections)
|
||||
lock (m_connections)
|
||||
{
|
||||
if (m_connections.Count > 0)
|
||||
{
|
||||
@@ -63,7 +66,7 @@ namespace Lidgren.Network
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return base.Connect(remoteEndpoint, approvalMessage);
|
||||
return base.Connect(remoteEndpoint, hailMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -84,13 +87,13 @@ namespace Lidgren.Network
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public bool SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
//LogError("Cannot send message, no server connection!");
|
||||
return false;
|
||||
LogWarning("Cannot send message, no server connection!");
|
||||
return NetSendResult.Failed;
|
||||
}
|
||||
|
||||
return serverConnection.SendMessage(msg, method, 0);
|
||||
@@ -99,13 +102,13 @@ namespace Lidgren.Network
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public bool SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
//LogError("Cannot send message, no server connection!");
|
||||
return false;
|
||||
LogWarning("Cannot send message, no server connection!");
|
||||
return NetSendResult.Failed;
|
||||
}
|
||||
|
||||
return serverConnection.SendMessage(msg, method, sequenceChannel);
|
||||
|
||||
@@ -1,72 +1,42 @@
|
||||
/* 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
internal bool m_connectRequested;
|
||||
internal string m_disconnectByeMessage;
|
||||
internal bool m_disconnectRequested;
|
||||
internal bool m_connectionInitiator;
|
||||
internal double m_connectInitationTime; // regardless of initiator
|
||||
internal NetOutgoingMessage m_approvalMessage;
|
||||
internal string m_disconnectMessage;
|
||||
internal NetIncomingMessage m_remoteHailMessage;
|
||||
|
||||
internal void SetStatus(NetConnectionStatus status, string reason)
|
||||
{
|
||||
if (status == m_status)
|
||||
return;
|
||||
m_status = status;
|
||||
if (reason == null)
|
||||
reason = string.Empty;
|
||||
/// <summary>
|
||||
/// The message that the remote part specified via Connect() or Approve() - can be null.
|
||||
/// </summary>
|
||||
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
|
||||
|
||||
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.StatusChanged))
|
||||
internal void UnconnectedHeartbeat(float now)
|
||||
{
|
||||
NetIncomingMessage info = m_owner.CreateIncomingMessage(NetIncomingMessageType.StatusChanged, 4 + reason.Length + (reason.Length > 126 ? 2 : 1));
|
||||
info.m_senderConnection = this;
|
||||
info.m_senderEndpoint = m_remoteEndpoint;
|
||||
info.Write((byte)m_status);
|
||||
info.Write(reason);
|
||||
m_owner.ReleaseMessage(info);
|
||||
}
|
||||
else
|
||||
{
|
||||
// app dont want those messages, update visible status immediately
|
||||
m_visibleStatus = m_status;
|
||||
}
|
||||
}
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
private void SendConnect()
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
if (m_disconnectRequested)
|
||||
ExecuteDisconnect(m_disconnectMessage, true);
|
||||
|
||||
if (m_connectRequested)
|
||||
{
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.Connected:
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// reconnect
|
||||
m_disconnectByeMessage = "Reconnecting";
|
||||
ExecuteDisconnect(true);
|
||||
FinishDisconnect();
|
||||
ExecuteDisconnect("Reconnecting", true);
|
||||
break;
|
||||
case NetConnectionStatus.Connecting:
|
||||
case NetConnectionStatus.None:
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// send another connect attempt
|
||||
SendConnect();
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
throw new NetException("This connection is Disconnected; spent. A new one should have been created");
|
||||
@@ -74,190 +44,318 @@ namespace Lidgren.Network
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
// let disconnect finish first
|
||||
return;
|
||||
case NetConnectionStatus.None:
|
||||
default:
|
||||
SendConnect();
|
||||
break;
|
||||
}
|
||||
|
||||
m_connectRequested = false;
|
||||
|
||||
// start handshake
|
||||
|
||||
int len = 2 + m_peerConfiguration.AppIdentifier.Length + 8 + 4 + (m_approvalMessage == null ? 0 : m_approvalMessage.LengthBytes);
|
||||
NetOutgoingMessage om = m_owner.CreateMessage(len);
|
||||
om.m_libType = NetMessageLibraryType.Connect;
|
||||
om.Write(m_peerConfiguration.AppIdentifier);
|
||||
om.Write(m_owner.m_uniqueIdentifier);
|
||||
|
||||
if (m_approvalMessage == null)
|
||||
{
|
||||
om.WriteVariableUInt32(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
om.WriteVariableUInt32((uint)m_approvalMessage.LengthBits);
|
||||
om.Write(m_approvalMessage);
|
||||
}
|
||||
m_owner.LogVerbose("Sending Connect");
|
||||
|
||||
m_owner.SendLibraryImmediately(om, m_remoteEndpoint);
|
||||
|
||||
m_connectInitationTime = NetTime.Now;
|
||||
SetStatus(NetConnectionStatus.Connecting, "Connecting");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
internal void SendConnectResponse()
|
||||
// TODO: handle dangling connections
|
||||
}
|
||||
|
||||
internal void ExecuteDisconnect(string reason, bool sendByeMessage)
|
||||
{
|
||||
double now = NetTime.Now;
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
NetOutgoingMessage reply = m_owner.CreateMessage(4);
|
||||
reply.m_libType = NetMessageLibraryType.ConnectResponse;
|
||||
reply.Write((float)now);
|
||||
// m_peer.LogDebug("Executing disconnect");
|
||||
|
||||
m_owner.LogVerbose("Sending LibraryConnectResponse");
|
||||
m_owner.SendLibraryImmediately(reply, m_remoteEndpoint);
|
||||
// clear send queues
|
||||
for (int i = 0; i < m_sendChannels.Length; i++)
|
||||
{
|
||||
NetSenderChannelBase channel = m_sendChannels[i];
|
||||
if (channel != null)
|
||||
channel.Reset();
|
||||
}
|
||||
|
||||
if (sendByeMessage)
|
||||
SendDisconnect(reason, true);
|
||||
|
||||
SetStatus(NetConnectionStatus.Disconnected, reason);
|
||||
m_disconnectRequested = false;
|
||||
m_connectRequested = false;
|
||||
}
|
||||
|
||||
internal void SendConnect()
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 1 + 4);
|
||||
om.m_messageType = NetMessageType.Connect;
|
||||
om.Write(m_peerConfiguration.AppIdentifier);
|
||||
om.Write(m_peer.m_uniqueIdentifier);
|
||||
|
||||
WriteLocalHail(om);
|
||||
|
||||
m_peer.SendLibrary(om, m_remoteEndpoint);
|
||||
|
||||
SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
|
||||
m_connectRequested = false;
|
||||
}
|
||||
|
||||
internal void SendConnectResponse(bool onLibraryThread)
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 1 + 4);
|
||||
om.m_messageType = NetMessageType.ConnectResponse;
|
||||
om.Write(m_peerConfiguration.AppIdentifier);
|
||||
om.Write(m_peer.m_uniqueIdentifier);
|
||||
|
||||
WriteLocalHail(om);
|
||||
|
||||
if (onLibraryThread)
|
||||
m_peer.SendLibrary(om, m_remoteEndpoint);
|
||||
else
|
||||
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndpoint, om));
|
||||
|
||||
SetStatus(NetConnectionStatus.RespondedConnect, "Remotely requested connect");
|
||||
}
|
||||
|
||||
internal void SendDisconnect(string reason, bool onLibraryThread)
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(reason);
|
||||
om.m_messageType = NetMessageType.Disconnect;
|
||||
if (onLibraryThread)
|
||||
m_peer.SendLibrary(om, m_remoteEndpoint);
|
||||
else
|
||||
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndpoint, om));
|
||||
}
|
||||
|
||||
private void WriteLocalHail(NetOutgoingMessage om)
|
||||
{
|
||||
if (m_localHailMessage != null)
|
||||
{
|
||||
byte[] hi = m_localHailMessage.PeekDataBuffer();
|
||||
if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
|
||||
{
|
||||
if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10)
|
||||
throw new NetException("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes));
|
||||
om.Write(m_localHailMessage.PeekDataBuffer(), 0, m_localHailMessage.LengthBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SendConnectionEstablished()
|
||||
{
|
||||
double now = NetTime.Now;
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(0);
|
||||
om.m_messageType = NetMessageType.ConnectionEstablished;
|
||||
m_peer.SendLibrary(om, m_remoteEndpoint);
|
||||
|
||||
NetOutgoingMessage ce = m_owner.CreateMessage(4);
|
||||
ce.m_libType = NetMessageLibraryType.ConnectionEstablished;
|
||||
ce.Write((float)now);
|
||||
|
||||
m_owner.LogVerbose("Sending LibraryConnectionEstablished");
|
||||
m_owner.SendLibraryImmediately(ce, m_remoteEndpoint);
|
||||
m_sentPingTime = (float)NetTime.Now - (m_peerConfiguration.PingInterval / 2.0f); // delay ping for a little while
|
||||
if (m_status != NetConnectionStatus.Connected)
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
|
||||
}
|
||||
|
||||
internal void ExecuteDisconnect(bool sendByeMessage)
|
||||
/// <summary>
|
||||
/// Approves this connection; sending a connection response to the remote host
|
||||
/// </summary>
|
||||
public void Approve()
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
if (m_status == NetConnectionStatus.Disconnected || m_status == NetConnectionStatus.None)
|
||||
return;
|
||||
|
||||
if (sendByeMessage)
|
||||
{
|
||||
NetOutgoingMessage om = m_owner.CreateLibraryMessage(NetMessageLibraryType.Disconnect, m_disconnectByeMessage);
|
||||
SendLibrary(om);
|
||||
m_localHailMessage = null;
|
||||
SendConnectResponse(false);
|
||||
}
|
||||
|
||||
m_owner.LogVerbose("Executing Disconnect(" + m_disconnectByeMessage + ")");
|
||||
|
||||
return;
|
||||
/// <summary>
|
||||
/// Approves this connection; sending a connection response to the remote host
|
||||
/// </summary>
|
||||
/// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param>
|
||||
public void Approve(NetOutgoingMessage localHail)
|
||||
{
|
||||
m_localHailMessage = localHail;
|
||||
SendConnectResponse(false);
|
||||
}
|
||||
|
||||
internal void FinishDisconnect()
|
||||
/// <summary>
|
||||
/// Denies this connection; disconnecting it
|
||||
/// </summary>
|
||||
public void Deny()
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
if (m_status == NetConnectionStatus.Disconnected || m_status == NetConnectionStatus.None)
|
||||
return;
|
||||
|
||||
m_owner.LogVerbose("Finishing Disconnect(" + m_disconnectByeMessage + ")");
|
||||
|
||||
if (m_unsentMessages.Count > 0)
|
||||
m_owner.LogDebug(m_unsentMessages.Count + " unsent messages were not sent before disconnected");
|
||||
|
||||
// release some held memory
|
||||
m_unackedSends.Clear();
|
||||
m_acknowledgesToSend.Clear();
|
||||
foreach(var wma in m_withheldMessages)
|
||||
if (wma != null)
|
||||
wma.Clear();
|
||||
m_fragmentGroups.Clear();
|
||||
|
||||
SetStatus(NetConnectionStatus.Disconnected, m_disconnectByeMessage);
|
||||
m_disconnectByeMessage = null;
|
||||
m_connectionInitiator = false;
|
||||
Deny("");
|
||||
}
|
||||
|
||||
private void HandleIncomingHandshake(NetMessageLibraryType libType, int ptr, int payloadBitsLength)
|
||||
/// <summary>
|
||||
/// Denies this connection; disconnecting it
|
||||
/// </summary>
|
||||
/// <param name="reason">The stated reason for the disconnect, readable as a string in the StatusChanged message on the remote host</param>
|
||||
public void Deny(string reason)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
// send disconnect; remove from handshakes
|
||||
SendDisconnect(reason, false);
|
||||
|
||||
switch (libType)
|
||||
{
|
||||
case NetMessageLibraryType.Connect:
|
||||
|
||||
m_owner.LogDebug("Received Connect");
|
||||
|
||||
if (m_status == NetConnectionStatus.Connecting)
|
||||
{
|
||||
// our connectresponse must have been lost, send another one
|
||||
SendConnectResponse();
|
||||
return;
|
||||
// remove from handshakes
|
||||
m_peer.m_handshakes.Remove(m_remoteEndpoint); // TODO: make this more thread safe? we're on user thread
|
||||
}
|
||||
|
||||
internal void ReceivedHandshake(NetMessageType tp, int ptr, int payloadLength)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
byte[] hail;
|
||||
switch (tp)
|
||||
{
|
||||
case NetMessageType.Connect:
|
||||
if (m_status == NetConnectionStatus.None)
|
||||
{
|
||||
// Whee! Server full has already been checked
|
||||
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
|
||||
if (ok)
|
||||
{
|
||||
if (hail != null)
|
||||
{
|
||||
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
|
||||
m_remoteHailMessage.LengthBits = (hail.Length * 8);
|
||||
}
|
||||
if (m_connectionInitiator)
|
||||
m_owner.LogError("Received Connect; altho we're connect initiator! Status is " + m_status);
|
||||
else
|
||||
m_owner.LogError("NetConnection.HandleIncomingHandshake() passed LibraryConnect but status is " + m_status + "!? now is " + NetTime.Now + " LastHeardFrom is " + m_lastHeardFrom);
|
||||
{
|
||||
m_remoteHailMessage = null;
|
||||
}
|
||||
|
||||
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
|
||||
{
|
||||
// ok, let's not add connection just yet
|
||||
NetIncomingMessage appMsg = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, m_remoteHailMessage.LengthBytes);
|
||||
appMsg.m_senderConnection = this;
|
||||
appMsg.m_senderEndpoint = this.m_remoteEndpoint;
|
||||
appMsg.Write(m_remoteHailMessage.m_data, 0, m_remoteHailMessage.LengthBytes);
|
||||
m_peer.ReleaseMessage(appMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
SendConnectResponse(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_status == NetConnectionStatus.RespondedConnect)
|
||||
{
|
||||
// our ConnectResponse must have been lost
|
||||
SendConnectResponse(true);
|
||||
return;
|
||||
}
|
||||
m_peer.LogDebug("Unhandled handshake: " + tp + ", status is " + m_status + " length: " + payloadLength);
|
||||
break;
|
||||
case NetMessageLibraryType.ConnectResponse:
|
||||
if (!m_connectionInitiator)
|
||||
case NetMessageType.ConnectResponse:
|
||||
switch (m_status)
|
||||
{
|
||||
m_owner.LogError("NetConnection.HandleIncomingHandshake() passed LibraryConnectResponse, but we're not initiator!");
|
||||
// weird, just drop it
|
||||
return;
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// awesome
|
||||
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
|
||||
if (ok)
|
||||
{
|
||||
if (hail != null)
|
||||
{
|
||||
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
|
||||
m_remoteHailMessage.LengthBits = (hail.Length * 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_remoteHailMessage = null;
|
||||
}
|
||||
|
||||
m_owner.LogDebug("Received ConnectResponse");
|
||||
|
||||
if (m_status == NetConnectionStatus.Connecting)
|
||||
{
|
||||
m_owner.m_statistics.m_bytesAllocated += NetUtility.BytesToHoldBits(payloadBitsLength);
|
||||
|
||||
float remoteNetTime = BitConverter.ToSingle(m_owner.m_receiveBuffer, ptr);
|
||||
ptr += 4;
|
||||
|
||||
// excellent, handshake making progress; send connectionestablished
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected");
|
||||
|
||||
SendConnectionEstablished();
|
||||
|
||||
// setup initial ping estimation
|
||||
InitializeLatency((float)(NetTime.Now - m_connectInitationTime), remoteNetTime);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_status == NetConnectionStatus.Connected)
|
||||
{
|
||||
// received (another) connectresponse; our connectionestablished must have been lost, send another one
|
||||
m_peer.AcceptConnection(this);
|
||||
SendConnectionEstablished();
|
||||
return;
|
||||
}
|
||||
|
||||
m_owner.LogWarning("NetConnection.HandleIncomingHandshake() passed " + libType + ", but status is " + m_status);
|
||||
break;
|
||||
case NetMessageLibraryType.ConnectionEstablished:
|
||||
|
||||
m_owner.LogDebug("Received ConnectionEstablished");
|
||||
|
||||
if (!m_connectionInitiator && m_status == NetConnectionStatus.Connecting)
|
||||
{
|
||||
float remoteNetTime = BitConverter.ToSingle(m_owner.m_receiveBuffer, ptr);
|
||||
|
||||
// handshake done
|
||||
InitializeLatency((float)(NetTime.Now - m_connectInitationTime), remoteNetTime);
|
||||
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected");
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// hello, wtf?
|
||||
break;
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
case NetConnectionStatus.Disconnected:
|
||||
case NetConnectionStatus.None:
|
||||
// wtf? anyway, bye!
|
||||
break;
|
||||
case NetConnectionStatus.Connected:
|
||||
// my ConnectionEstablished must have been lost, send another one
|
||||
SendConnectionEstablished();
|
||||
return;
|
||||
}
|
||||
|
||||
m_owner.LogWarning("NetConnection.HandleIncomingHandshake() passed " + libType + ", but initiator is " + m_connectionInitiator + " and status is " + m_status);
|
||||
break;
|
||||
case NetMessageLibraryType.Disconnect:
|
||||
// extract bye message
|
||||
NetIncomingMessage im = m_owner.CreateIncomingMessage(NetIncomingMessageType.Data, m_owner.m_receiveBuffer, ptr, NetUtility.BytesToHoldBits(payloadBitsLength));
|
||||
im.m_bitLength = payloadBitsLength;
|
||||
m_disconnectByeMessage = im.ReadString();
|
||||
FinishDisconnect();
|
||||
case NetMessageType.ConnectionEstablished:
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.Connected:
|
||||
// ok...
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
case NetConnectionStatus.None:
|
||||
// too bad, almost made it
|
||||
break;
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// weird, should have been ConnectResponse...
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// awesome
|
||||
m_peer.AcceptConnection(this);
|
||||
m_sentPingTime = (float)NetTime.Now - (m_peerConfiguration.PingInterval / 2.0f); // delay ping for a little while
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NetMessageType.Disconnect:
|
||||
// ouch
|
||||
string reason = "Ouch";
|
||||
try
|
||||
{
|
||||
NetIncomingMessage inc = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
reason = inc.ReadString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
SetStatus(NetConnectionStatus.Disconnected, reason);
|
||||
break;
|
||||
default:
|
||||
// huh?
|
||||
m_owner.LogWarning("Unhandled library type in " + this + ": " + libType);
|
||||
m_peer.LogDebug("Unhandled handshake: " + tp + " length: " + payloadLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail)
|
||||
{
|
||||
hail = null;
|
||||
|
||||
// create temporary incoming message
|
||||
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
try
|
||||
{
|
||||
string remoteAppIdentifier = msg.ReadString();
|
||||
long remoteUniqueIdentifier = msg.ReadInt64();
|
||||
|
||||
int remainingBytes = payloadLength - (msg.PositionInBytes - ptr);
|
||||
if (remainingBytes > 0)
|
||||
hail = msg.ReadBytes(remainingBytes);
|
||||
|
||||
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
|
||||
{
|
||||
// wrong app identifier
|
||||
ExecuteDisconnect("Wrong application identifier!", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_remoteUniqueIdentifier = remoteUniqueIdentifier;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
// whatever; we failed
|
||||
ExecuteDisconnect("Handshake data validation failed", true);
|
||||
m_peer.LogWarning("ReadRemoteHandshakeData failed: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Disconnect(string byeMessage)
|
||||
{
|
||||
// user or library thread
|
||||
if (m_status == NetConnectionStatus.None || m_status == NetConnectionStatus.Disconnected)
|
||||
return;
|
||||
|
||||
m_peer.LogVerbose("Disconnect requested for " + this);
|
||||
m_disconnectMessage = byeMessage;
|
||||
|
||||
if (m_status != NetConnectionStatus.Disconnected && m_status != NetConnectionStatus.None)
|
||||
SetStatus(NetConnectionStatus.Disconnecting, byeMessage);
|
||||
|
||||
m_disconnectRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,161 +1,71 @@
|
||||
/* 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.Text;
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
//
|
||||
// Connection keepalive and latency calculation
|
||||
//
|
||||
internal float m_averageRoundtripTime = 0.05f;
|
||||
private byte m_lastSentPingNumber;
|
||||
private double m_lastPingSendTime;
|
||||
private double m_nextPing;
|
||||
internal double m_lastSendRespondedTo;
|
||||
|
||||
// remote + diff = local
|
||||
// diff = local - remote
|
||||
// local - diff = remote
|
||||
internal double m_remoteToLocalNetTime = double.MinValue;
|
||||
private float m_sentPingTime;
|
||||
private int m_sentPingNumber;
|
||||
private float m_averageRoundtripTime;
|
||||
private float m_timeoutDeadline = float.MaxValue;
|
||||
|
||||
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } }
|
||||
|
||||
internal void InitializeLatency(float rtt, float remoteNetTime)
|
||||
internal void SendPing()
|
||||
{
|
||||
m_averageRoundtripTime = Math.Max(0.005f, rtt - 0.005f); // TODO: find out why initial ping always overshoots
|
||||
m_nextPing = NetTime.Now + m_peerConfiguration.m_pingFrequency / 2.0f;
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
double currentRemoteNetTime = remoteNetTime - (rtt * 0.5);
|
||||
m_remoteToLocalNetTime = (float)(NetTime.Now - currentRemoteNetTime);
|
||||
m_sentPingNumber++;
|
||||
if (m_sentPingNumber >= 256)
|
||||
m_sentPingNumber = 0;
|
||||
m_sentPingTime = (float)NetTime.Now;
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(1);
|
||||
om.Write((byte)m_sentPingNumber);
|
||||
om.m_messageType = NetMessageType.Ping;
|
||||
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.Append("Initialized average roundtrip time to: ");
|
||||
bdr.Append(NetTime.ToReadable(m_averageRoundtripTime));
|
||||
bdr.Append(" remote time diff to ");
|
||||
bdr.Append(NetTime.ToReadable(m_remoteToLocalNetTime));
|
||||
m_owner.LogDebug(bdr.ToString());
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
m_peer.SendPacket(len, m_remoteEndpoint, 1, out connectionReset);
|
||||
}
|
||||
|
||||
internal void HandleIncomingPing(byte pingNumber)
|
||||
internal void SendPong(int pingNumber)
|
||||
{
|
||||
double now = NetTime.Now;
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
// send pong
|
||||
NetOutgoingMessage pong = m_owner.CreateMessage(1 + 8);
|
||||
pong.m_libType = NetMessageLibraryType.Pong;
|
||||
pong.Write((byte)pingNumber);
|
||||
pong.Write(now);
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(1);
|
||||
om.Write((byte)pingNumber);
|
||||
om.m_messageType = NetMessageType.Pong;
|
||||
|
||||
m_owner.LogDebug("Sending pong for remote ping #" + pingNumber);
|
||||
|
||||
m_owner.SendLibraryImmediately(pong, m_remoteEndpoint);
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
m_peer.SendPacket(len, m_remoteEndpoint, 1, out connectionReset);
|
||||
}
|
||||
|
||||
internal void HandleIncomingPong(double receiveNow, byte pingNumber, double remoteNetTime)
|
||||
internal void ReceivedPong(float now, int pongNumber)
|
||||
{
|
||||
// verify it´s the correct ping number
|
||||
if (pingNumber != m_lastSentPingNumber)
|
||||
if (pongNumber != m_sentPingNumber)
|
||||
{
|
||||
m_owner.LogError("Received wrong pong number; got " + pingNumber + " expected " + m_lastSentPingNumber);
|
||||
|
||||
// but still, a pong must have been triggered by a ping...
|
||||
double diff = receiveNow - m_lastSendRespondedTo;
|
||||
if (diff > 0)
|
||||
m_lastSendRespondedTo += (diff / 2); // postpone timing out a bit
|
||||
|
||||
m_peer.LogVerbose("Ping/Pong mismatch; dropped message?");
|
||||
return;
|
||||
}
|
||||
|
||||
double now = NetTime.Now; // need exact number
|
||||
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
|
||||
|
||||
m_lastHeardFrom = now;
|
||||
if (m_lastPingSendTime > m_lastSendRespondedTo)
|
||||
m_lastSendRespondedTo = m_lastPingSendTime;
|
||||
float rtt = now - m_sentPingTime;
|
||||
NetException.Assert(rtt >= 0);
|
||||
|
||||
double rtt = now - m_lastPingSendTime;
|
||||
|
||||
// update clock sync
|
||||
double currentRemoteNetTime = remoteNetTime - (rtt * 0.5);
|
||||
double foundDiffRemoteToLocal = receiveNow - currentRemoteNetTime;
|
||||
|
||||
if (m_remoteToLocalNetTime == double.MinValue)
|
||||
m_remoteToLocalNetTime = foundDiffRemoteToLocal;
|
||||
if (m_averageRoundtripTime < 0)
|
||||
{
|
||||
m_averageRoundtripTime = rtt; // initial estimate
|
||||
m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime));
|
||||
}
|
||||
else
|
||||
m_remoteToLocalNetTime = ((m_remoteToLocalNetTime + foundDiffRemoteToLocal) * 0.5);
|
||||
|
||||
{
|
||||
m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f);
|
||||
m_owner.LogVerbose("Found rtt: " + NetTime.ToReadable(rtt) + ", new average: " + NetTime.ToReadable(m_averageRoundtripTime) + " new time diff: " + NetTime.ToReadable(m_remoteToLocalNetTime));
|
||||
m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime));
|
||||
}
|
||||
|
||||
internal void KeepAliveHeartbeat(double now)
|
||||
{
|
||||
// do keepalive and latency pings
|
||||
if (m_status == NetConnectionStatus.Disconnected || m_status == NetConnectionStatus.None)
|
||||
return;
|
||||
|
||||
// force ack sending?
|
||||
if (now > m_nextForceAckTime)
|
||||
{
|
||||
// send keepalive thru regular channels to give acks something to piggyback on
|
||||
NetOutgoingMessage pig = m_owner.CreateMessage(1);
|
||||
pig.m_libType = NetMessageLibraryType.KeepAlive;
|
||||
SendLibrary(pig);
|
||||
m_nextForceAckTime = now + m_peerConfiguration.m_maxAckDelayTime;
|
||||
}
|
||||
|
||||
// timeout
|
||||
if (now > m_lastSendRespondedTo + m_peerConfiguration.m_connectionTimeout)
|
||||
{
|
||||
m_owner.LogDebug(this + " timed out; now is " + now + " last responded to is: " + m_lastSendRespondedTo);
|
||||
Disconnect("Timed out after " + (now - m_lastSendRespondedTo) + " seconds");
|
||||
return;
|
||||
}
|
||||
|
||||
// ping time?
|
||||
if (now >= m_nextPing)
|
||||
{
|
||||
//
|
||||
// send ping
|
||||
//
|
||||
now = NetTime.Now; // need exact number
|
||||
m_lastSentPingNumber++;
|
||||
m_lastPingSendTime = now;
|
||||
|
||||
m_owner.LogDebug("Sending ping #" + m_lastSentPingNumber);
|
||||
|
||||
// in case of not heard for a while
|
||||
if (m_lastSendRespondedTo > 0 && (now > m_lastSendRespondedTo + (m_owner.Configuration.m_pingFrequency * 1.5f)))
|
||||
m_nextPing = now + (m_owner.Configuration.m_pingFrequency * 0.5f); // double ping rate
|
||||
else
|
||||
m_nextPing = now + m_owner.Configuration.m_pingFrequency;
|
||||
|
||||
NetOutgoingMessage ping = m_owner.CreateMessage(1);
|
||||
ping.m_libType = NetMessageLibraryType.Ping;
|
||||
ping.Write((byte)m_lastSentPingNumber);
|
||||
|
||||
m_owner.SendLibraryImmediately(ping, m_remoteEndpoint);
|
||||
}
|
||||
m_peer.LogVerbose("Timeout deadline pushed to " + m_timeoutDeadline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,9 @@ using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Statistics for a NetConnection instance
|
||||
/// </summary>
|
||||
public sealed class NetConnectionStatistics
|
||||
{
|
||||
private readonly NetConnection m_connection;
|
||||
@@ -77,23 +80,12 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
public int ResentMessages { get { return m_resentMessages; } }
|
||||
|
||||
public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
|
||||
|
||||
public int MostSends
|
||||
{
|
||||
get
|
||||
{
|
||||
int most = 0;
|
||||
foreach (var a in m_connection.m_unackedSends)
|
||||
if (a.NumSends > most)
|
||||
most = a.NumSends;
|
||||
return most;
|
||||
}
|
||||
}
|
||||
// public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
@@ -102,6 +94,7 @@ namespace Lidgren.Network
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
@@ -116,21 +109,44 @@ namespace Lidgren.Network
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.AppendLine("Average roundtrip time: " + NetTime.ToReadable(m_connection.m_averageRoundtripTime));
|
||||
//bdr.AppendLine("Average roundtrip time: " + NetTime.ToReadable(m_connection.m_averageRoundtripTime));
|
||||
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
|
||||
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets");
|
||||
|
||||
if (m_resentMessages > 0)
|
||||
bdr.AppendLine("Resent messages: " + m_resentMessages);
|
||||
|
||||
int numUnsent = m_connection.m_unsentMessages.Count;
|
||||
if (numUnsent > 0)
|
||||
int numUnsent = 0;
|
||||
int numStored = 0;
|
||||
foreach (NetSenderChannelBase sendChan in m_connection.m_sendChannels)
|
||||
{
|
||||
if (sendChan == null)
|
||||
continue;
|
||||
numUnsent += sendChan.m_queuedSends.Count;
|
||||
|
||||
var relSendChan = sendChan as NetReliableSenderChannel;
|
||||
if (relSendChan != null)
|
||||
{
|
||||
for (int i = 0; i < relSendChan.m_storedMessages.Length; i++)
|
||||
if (relSendChan.m_storedMessages[i].Message != null)
|
||||
numStored++;
|
||||
}
|
||||
}
|
||||
|
||||
int numWithheld = 0;
|
||||
foreach (NetReceiverChannelBase recChan in m_connection.m_receiveChannels)
|
||||
{
|
||||
var relRecChan = recChan as NetReliableOrderedReceiver;
|
||||
if (relRecChan != null)
|
||||
{
|
||||
for (int i = 0; i < relRecChan.m_withheldMessages.Length; i++)
|
||||
if (relRecChan.m_withheldMessages[i] != null)
|
||||
numWithheld++;
|
||||
}
|
||||
}
|
||||
|
||||
bdr.AppendLine("Unsent messages: " + numUnsent);
|
||||
int numStored = m_connection.GetStoredMessagesCount();
|
||||
if (numStored > 0)
|
||||
bdr.AppendLine("Stored messages: " + numStored);
|
||||
int numWithheld = m_connection.GetWithheldMessagesCount();
|
||||
if (numWithheld > 0)
|
||||
bdr.AppendLine("Withheld messages: " + numWithheld);
|
||||
|
||||
return bdr.ToString();
|
||||
|
||||
@@ -21,13 +21,17 @@ using System;
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Status of a connection
|
||||
/// Status for a NetConnection instance
|
||||
/// </summary>
|
||||
public enum NetConnectionStatus
|
||||
{
|
||||
None,
|
||||
Connecting,
|
||||
Connected,
|
||||
|
||||
InitiatedConnect, // we sent Connect
|
||||
RespondedConnect, // we got Connect, sent ConnectResponse
|
||||
|
||||
Connected, // we received ConnectResponse (if initiator) or ConnectionEstablished (if passive)
|
||||
|
||||
Disconnecting,
|
||||
Disconnected
|
||||
}
|
||||
|
||||
@@ -20,26 +20,34 @@ using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// All the constants used when compiling the library
|
||||
/// </summary>
|
||||
public static class NetConstants
|
||||
{
|
||||
public const int NumTotalChannels = 99;
|
||||
|
||||
public const int NetChannelsPerDeliveryMethod = 32;
|
||||
|
||||
public const int NumSequenceNumbers = ushort.MaxValue + 1; // 0 is a valid sequence number
|
||||
public const int NumSequenceNumbers = 1024;
|
||||
|
||||
public const int HeaderByteSize = 5;
|
||||
|
||||
public const int UnreliableWindowSize = 128;
|
||||
public const int ReliableOrderedWindowSize = 64;
|
||||
public const int ReliableSequencedWindowSize = 64;
|
||||
|
||||
public const int MaxFragmentationGroups = ushort.MaxValue - 1;
|
||||
|
||||
/// <summary>
|
||||
/// Number of channels which needs a sequence number to work
|
||||
/// </summary>
|
||||
internal const int NumSequencedChannels = ((int)NetMessageType.UserReliableOrdered + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced;
|
||||
internal const int NumSequencedChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced1;
|
||||
|
||||
/// <summary>
|
||||
/// Number of reliable channels
|
||||
/// </summary>
|
||||
internal const int NumReliableChannels = ((int)NetMessageType.UserReliableOrdered + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserReliableUnordered;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes added when message is really a fragment
|
||||
/// </summary>
|
||||
internal const int FragmentHeaderSize = 6;
|
||||
internal const int NumReliableChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserReliableUnordered;
|
||||
|
||||
internal const string ConnResetMessage = "Connection was reset by remote host";
|
||||
}
|
||||
|
||||
23
Lidgren.Network/NetDeliveryMethod.cs
Normal file
23
Lidgren.Network/NetDeliveryMethod.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// How the library deals with resends and handling of late messages
|
||||
/// </summary>
|
||||
public enum NetDeliveryMethod : byte
|
||||
{
|
||||
//
|
||||
// Actually a publicly visible subset of NetMessageType
|
||||
//
|
||||
Unknown = 0,
|
||||
Unreliable = 1,
|
||||
UnreliableSequenced = 2,
|
||||
ReliableUnordered = 34,
|
||||
ReliableSequenced = 35,
|
||||
ReliableOrdered = 67,
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace Lidgren.Network
|
||||
/// String to hash for key
|
||||
/// </summary>
|
||||
public NetXtea(string key)
|
||||
: this(NetSha.Hash(Encoding.ASCII.GetBytes(key)), 32)
|
||||
: this(SHA1.Create().ComputeHash(Encoding.ASCII.GetBytes(key)), 32)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -138,195 +138,4 @@ namespace Lidgren.Network
|
||||
destination[destinationOffset++] = (byte)value;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NetSha
|
||||
{
|
||||
// TODO: switch to SHA256
|
||||
private static SHA1 m_sha;
|
||||
private static object s_lock = new object();
|
||||
|
||||
public static byte[] Hash(byte[] data)
|
||||
{
|
||||
lock (s_lock)
|
||||
{
|
||||
if (m_sha == null)
|
||||
m_sha = SHA1Managed.Create();
|
||||
return m_sha.ComputeHash(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NetSRP
|
||||
{
|
||||
public static readonly BigInteger N = new BigInteger(NetUtility.ToByteArray("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3"));
|
||||
public static readonly BigInteger g = new BigInteger((uint)2);
|
||||
public static readonly BigInteger k = ComputeMultiplier();
|
||||
|
||||
/// <summary>
|
||||
/// Compute multiplier (k)
|
||||
/// </summary>
|
||||
private static BigInteger ComputeMultiplier()
|
||||
{
|
||||
string one = NetUtility.ToHexString(N.GetBytes());
|
||||
string two = NetUtility.ToHexString(g.GetBytes());
|
||||
byte[] cc = NetUtility.ToByteArray(one + two.PadLeft(one.Length, '0'));
|
||||
BigInteger retval = BigInteger.Modulus(new BigInteger(NetSha.Hash(cc)), N);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a verifier that the server can use to authenticate users later on (v)
|
||||
/// </summary>
|
||||
public static void ComputePasswordVerifier(string username, string password, byte[] salt, out byte[] serverVerifier, out byte[] clientVerifier)
|
||||
{
|
||||
byte[] tmp = Encoding.ASCII.GetBytes(username + ":" + password);
|
||||
byte[] innerHash = NetSha.Hash(tmp);
|
||||
|
||||
byte[] total = new byte[innerHash.Length + salt.Length];
|
||||
Buffer.BlockCopy(salt, 0, total, 0, salt.Length);
|
||||
Buffer.BlockCopy(innerHash, 0, total, salt.Length, innerHash.Length);
|
||||
|
||||
clientVerifier = NetSha.Hash(total);
|
||||
|
||||
// Verifier (v) = g^x (mod N)
|
||||
BigInteger xx = new BigInteger(clientVerifier);
|
||||
serverVerifier = g.ModPow(xx, N).GetBytes();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get 256 random bits
|
||||
/// </summary>
|
||||
public static byte[] CreateRandomKey()
|
||||
{
|
||||
byte[] retval = new byte[32];
|
||||
NetRandom.Instance.NextBytes(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets 80 random bits
|
||||
/// </summary>
|
||||
public static byte[] CreateRandomSalt()
|
||||
{
|
||||
byte[] retval = new byte[10];
|
||||
NetRandom.Instance.NextBytes(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute client challenge (A)
|
||||
/// </summary>
|
||||
public static byte[] ComputeClientPublicKey(byte[] clientPrivateKey) // a
|
||||
{
|
||||
BigInteger a = new BigInteger(clientPrivateKey);
|
||||
|
||||
BigInteger retval = g.ModPow(a, N);
|
||||
|
||||
string gs = NetUtility.ToHexString(g.GetBytes());
|
||||
|
||||
|
||||
Console.WriteLine("a: " + NetUtility.ToHexString(a.GetBytes()));
|
||||
Console.WriteLine("A: " + NetUtility.ToHexString(retval.GetBytes()));
|
||||
|
||||
return retval.GetBytes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute server challenge (B)
|
||||
/// </summary>
|
||||
public static byte[] ComputeServerPublicKey(byte[] serverPrivateKey, byte[] verifier) // b
|
||||
{
|
||||
BigInteger salt = new BigInteger(serverPrivateKey);
|
||||
|
||||
var bb = g.ModPow(salt, N);
|
||||
var B = BigInteger.Modulus((bb + (new BigInteger(verifier) * k)), N);
|
||||
|
||||
return B.GetBytes();
|
||||
}
|
||||
|
||||
public static byte[] ComputeU(byte[] clientPublicKey, byte[] serverPublicKey) // u
|
||||
{
|
||||
byte[] A = clientPublicKey;
|
||||
byte[] B = serverPublicKey;
|
||||
|
||||
string one = NetUtility.ToHexString(A);
|
||||
string two = NetUtility.ToHexString(B);
|
||||
string compound = one.PadLeft(66, '0') + two.PadLeft(66, '0');
|
||||
|
||||
byte[] cc = NetUtility.ToByteArray(compound);
|
||||
|
||||
return NetSha.Hash(cc);
|
||||
}
|
||||
|
||||
public static byte[] ComputeServerSessionKey(byte[] clientPublicKey, byte[] verifier, byte[] u, byte[] serverPrivateKey) // Ss
|
||||
{
|
||||
// S = (Av^u) ^ b (mod N)
|
||||
// return vv.modPow(uu, N).multiply(A).mod(N).modPow(bb, N);
|
||||
|
||||
BigInteger verBi = new BigInteger(verifier);
|
||||
BigInteger uBi = new BigInteger(u);
|
||||
BigInteger ABi = new BigInteger(clientPublicKey); // A
|
||||
BigInteger bBi = new BigInteger(serverPrivateKey); // b
|
||||
|
||||
Console.WriteLine("Ss input v: " + NetUtility.ToHexString(verifier));
|
||||
Console.WriteLine("Ss input u: " + NetUtility.ToHexString(u));
|
||||
Console.WriteLine("Ss input A: " + NetUtility.ToHexString(clientPublicKey));
|
||||
Console.WriteLine("Ss input A: " + ABi.ToString(16));
|
||||
Console.WriteLine("Ss input b: " + NetUtility.ToHexString(serverPrivateKey));
|
||||
|
||||
BigInteger retval = verBi.ModPow(uBi, N).Multiply(ABi).Modulus(N).ModPow(bBi, N).Modulus(N);
|
||||
Console.WriteLine("Ss (trad): " + NetUtility.ToHexString(retval.GetBytes()));
|
||||
BigInteger f1 = verBi.ModPow(uBi, N);
|
||||
Console.WriteLine("f1 (trad): " + NetUtility.ToHexString(f1.GetBytes()));
|
||||
|
||||
//return retval.GetBytes();
|
||||
|
||||
|
||||
// own
|
||||
// BigInteger tmp1 = verBi.ModPow(uBi, N).ModPow(bBi, N).Modulus(N);
|
||||
BigInteger tmp1 = (ABi * verBi.ModPow(uBi, N)).ModPow(bBi, N);
|
||||
Console.WriteLine("Ss (own): " + NetUtility.ToHexString(tmp1.GetBytes()));
|
||||
|
||||
|
||||
|
||||
// bc
|
||||
BigIntegerBC verBi2 = new BigIntegerBC(verifier);
|
||||
BigIntegerBC ABi2 = new BigIntegerBC(clientPublicKey); // A
|
||||
BigIntegerBC uBi2 = new BigIntegerBC(u);
|
||||
BigIntegerBC bBi2 = new BigIntegerBC(serverPrivateKey);
|
||||
BigIntegerBC N2 = new BigIntegerBC(N.GetBytes());
|
||||
|
||||
BigIntegerBC retval2 = verBi2.ModPow(uBi2, N2).Multiply(ABi2).Modulus(N2).ModPow(bBi2, N2).Modulus(N2);
|
||||
Console.WriteLine("Ss (bc): " + NetUtility.ToHexString(retval2.ToByteArray()));
|
||||
BigIntegerBC f12 = verBi2.ModPow(uBi2, N2);
|
||||
Console.WriteLine("f1 (bc): " + NetUtility.ToHexString(f12.ToByteArray()));
|
||||
|
||||
|
||||
// own bc
|
||||
BigIntegerBC tmp2 = verBi2.ModPow(uBi2, N2).ModPow(bBi2, N2).Modulus(N2);
|
||||
Console.WriteLine("Ss (ownBC): " + NetUtility.ToHexString(tmp2.ToByteArray()));
|
||||
|
||||
|
||||
|
||||
return retval.GetBytes();
|
||||
|
||||
//return NetSha.Hash(retval.GetBytes());
|
||||
}
|
||||
|
||||
public static byte[] ComputeClientSessionKey(byte[] serverPublicKey, byte[] x, byte[] u, byte[] clientPrivateKey) // Sc
|
||||
{
|
||||
BigInteger xBi = new BigInteger(x);
|
||||
BigInteger BBi = new BigInteger(serverPublicKey); // B
|
||||
BigInteger uBi = new BigInteger(u);
|
||||
BigInteger aBi = new BigInteger(clientPrivateKey); // a
|
||||
|
||||
BigInteger retval = (BBi + (N - ((k * g.ModPow(xBi, N)) % N))).ModPow(aBi + uBi * xBi, N);
|
||||
|
||||
return retval.GetBytes();
|
||||
|
||||
//return NetSha.Hash(retval.GetBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
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.Diagnostics;
|
||||
@@ -23,6 +22,9 @@ using System.Runtime.Serialization;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown in the Lidgren Network Library
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class NetException : Exception
|
||||
{
|
||||
|
||||
174
Lidgren.Network/NetFragmentationHelper.cs
Normal file
174
Lidgren.Network/NetFragmentationHelper.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal static class NetFragmentationHelper
|
||||
{
|
||||
internal static int WriteHeader(
|
||||
byte[] destination,
|
||||
int ptr,
|
||||
int group,
|
||||
int totalBits,
|
||||
int chunkByteSize,
|
||||
int chunkNumber)
|
||||
{
|
||||
uint num1 = (uint)group;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num1 | 0x80);
|
||||
num1 = num1 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num1;
|
||||
|
||||
// write variable length fragment total bits
|
||||
uint num2 = (uint)totalBits;
|
||||
while (num2 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num2 | 0x80);
|
||||
num2 = num2 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num2;
|
||||
|
||||
// write variable length fragment chunk size
|
||||
uint num3 = (uint)chunkByteSize;
|
||||
while (num3 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num3 | 0x80);
|
||||
num3 = num3 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num3;
|
||||
|
||||
// write variable length fragment chunk number
|
||||
uint num4 = (uint)chunkNumber;
|
||||
while (num4 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num4 | 0x80);
|
||||
num4 = num4 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num4;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal static int ReadHeader(byte[] buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber)
|
||||
{
|
||||
int num1 = 0;
|
||||
int num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
group = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
totalBits = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
chunkByteSize = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
chunkNumber = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal static int GetFragmentationHeaderSize(int groupId, int totalBytes, int chunkByteSize, int numChunks)
|
||||
{
|
||||
int len = 4;
|
||||
|
||||
// write variable length fragment group id
|
||||
uint num1 = (uint)groupId;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num1 = num1 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment total bits
|
||||
uint num2 = (uint)(totalBytes * 8);
|
||||
while (num2 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num2 = num2 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment chunk byte size
|
||||
uint num3 = (uint)chunkByteSize;
|
||||
while (num3 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num3 = num3 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment chunk number
|
||||
uint num4 = (uint)numChunks;
|
||||
while (num4 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num4 = num4 >> 7;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
internal static int GetBestChunkSize(int group, int totalBytes, int mtu)
|
||||
{
|
||||
int tryNumChunks = (totalBytes / (mtu - 8)) + 1;
|
||||
int tryChunkSize = (totalBytes / tryNumChunks) + 1; // +1 since we immediately decrement it in the loop
|
||||
|
||||
int headerSize = 0;
|
||||
do
|
||||
{
|
||||
tryChunkSize--; // keep reducing chunk size until it fits within MTU including header
|
||||
|
||||
int numChunks = totalBytes / tryChunkSize;
|
||||
if (numChunks * tryChunkSize < totalBytes)
|
||||
numChunks++;
|
||||
|
||||
headerSize = GetFragmentationHeaderSize(group, totalBytes, tryChunkSize, numChunks);
|
||||
|
||||
} while (tryChunkSize + headerSize + 5 + 1 >= mtu);
|
||||
|
||||
return tryChunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Lidgren.Network
|
||||
Type tp = target.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
SortMembersList(fields);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
foreach (FieldInfo fi in fields)
|
||||
{
|
||||
@@ -81,7 +81,7 @@ namespace Lidgren.Network
|
||||
Type tp = target.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
SortMembersList(fields);
|
||||
NetUtility.SortMembersList(fields);
|
||||
foreach (PropertyInfo fi in fields)
|
||||
{
|
||||
object value;
|
||||
@@ -99,44 +99,5 @@ namespace Lidgren.Network
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shell sort
|
||||
internal static void SortMembersList(MemberInfo[] list)
|
||||
{
|
||||
int h;
|
||||
int j;
|
||||
MemberInfo tmp;
|
||||
|
||||
h = 1;
|
||||
while (h * 3 + 1 <= list.Length)
|
||||
h = 3 * h + 1;
|
||||
|
||||
while (h > 0)
|
||||
{
|
||||
for (int i = h - 1; i < list.Length; i++)
|
||||
{
|
||||
tmp = list[i];
|
||||
j = i;
|
||||
while (true)
|
||||
{
|
||||
if (j >= h)
|
||||
{
|
||||
if (string.Compare(list[j - h].Name, tmp.Name, StringComparison.InvariantCulture) > 0)
|
||||
{
|
||||
list[j] = list[j - h];
|
||||
j -= h;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
list[j] = tmp;
|
||||
}
|
||||
h /= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ namespace Lidgren.Network
|
||||
|
||||
private static readonly Dictionary<Type, MethodInfo> s_readMethods;
|
||||
|
||||
private int m_readPosition;
|
||||
internal int m_readPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the read position in the buffer, in bits (not bytes)
|
||||
/// </summary>
|
||||
public override long Position // override of Stream property
|
||||
public long Position
|
||||
{
|
||||
get { return (long)m_readPosition; }
|
||||
set { m_readPosition = (int)value; }
|
||||
@@ -62,7 +62,7 @@ namespace Lidgren.Network
|
||||
string n = mi.Name.Substring(4);
|
||||
foreach (Type it in integralTypes)
|
||||
{
|
||||
if (it.Name == n && mi.ReturnType.Name == it.Name)
|
||||
if (it.Name == n)
|
||||
s_readMethods[it] = mi;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ namespace Lidgren.Network
|
||||
//
|
||||
// 8 bit
|
||||
//
|
||||
public new byte ReadByte()
|
||||
public byte ReadByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetIncomingMessage : Stream
|
||||
{
|
||||
public override bool CanRead { get { return true; } }
|
||||
public override bool CanSeek { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
// no op
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length in bytes
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { return LengthBytes; }
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
// limit amount to remaining
|
||||
int remainingBytes = NetUtility.BytesToHoldBits(m_bitLength - m_readPosition);
|
||||
if (count > remainingBytes)
|
||||
count = remainingBytes;
|
||||
if (count < 1)
|
||||
return 0;
|
||||
|
||||
ReadBytes(buffer, offset, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the position in the stream, in bytes
|
||||
/// </summary>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
Position = (offset * 8);
|
||||
break;
|
||||
case SeekOrigin.Current:
|
||||
Position = Position + (offset * 8);
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
Position = (LengthBytes - offset) * 8;
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Bad SeekOrigin");
|
||||
}
|
||||
return Position;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NetException("It's not possible to set the length of the NetIncomingMessage");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,12 +88,7 @@ namespace Lidgren.Network
|
||||
m_bitLength += bits;
|
||||
}
|
||||
|
||||
public override void Write(byte[] source, int offsetInBytes, int numberOfBytes)
|
||||
{
|
||||
throw new NetException("NetIncomingMessage does not support writing!");
|
||||
}
|
||||
|
||||
internal void WriteBytes(byte[] source, int offsetInBytes, int numberOfBytes)
|
||||
internal void Write(byte[] source, int offsetInBytes, int numberOfBytes)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
|
||||
@@ -17,77 +17,40 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR TH
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal enum NetIncomingMessageReleaseStatus
|
||||
{
|
||||
NotReleased = 0,
|
||||
ReleasedToApplication,
|
||||
RecycledByApplication
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{m_readPosition} of {m_bitLength} bits ({LengthBytes} bytes) read")]
|
||||
/// <summary>
|
||||
/// Incoming message either sent from a remote peer or generated within the library
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Type={MessageType} LengthBits={LengthBits}")]
|
||||
public partial class NetIncomingMessage
|
||||
{
|
||||
internal byte[] m_data;
|
||||
internal int m_bitLength;
|
||||
internal NetMessageType m_messageType; // NetDeliveryMethod and sequence channel can be derived from this
|
||||
internal ushort m_sequenceNumber;
|
||||
internal NetIncomingMessageReleaseStatus m_status;
|
||||
|
||||
internal NetIncomingMessageType m_incomingType;
|
||||
internal NetIncomingMessageType m_incomingMessageType;
|
||||
internal IPEndPoint m_senderEndpoint;
|
||||
internal NetConnection m_senderConnection;
|
||||
|
||||
internal NetFragmentationInfo m_fragmentationInfo;
|
||||
internal int m_sequenceNumber;
|
||||
internal NetMessageType m_receivedMessageType;
|
||||
internal bool m_isFragment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data in number of bytes
|
||||
/// Gets the type of this incoming message
|
||||
/// </summary>
|
||||
public int LengthBytes
|
||||
{
|
||||
get { return ((m_bitLength + 7) >> 3); }
|
||||
}
|
||||
public NetIncomingMessageType MessageType { get { return m_incomingMessageType; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data in number of bits
|
||||
/// Gets the delivery method this message was sent with (if user data)
|
||||
/// </summary>
|
||||
public int LengthBits
|
||||
{
|
||||
get { return m_bitLength; }
|
||||
}
|
||||
public NetDeliveryMethod DeliveryMethod { get { return m_receivedMessageType.GetDeliveryMethod(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the internal data buffer, don't modify
|
||||
/// Gets the sequence channel this message was sent with (if user data)
|
||||
/// </summary>
|
||||
public byte[] PeekDataBuffer()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NetDeliveryMethod used by this message
|
||||
/// </summary>
|
||||
public NetDeliveryMethod DeliveryMethod
|
||||
{
|
||||
get { return NetPeer.GetDeliveryMethod(m_messageType); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets which sequence channel this message was sent in
|
||||
/// </summary>
|
||||
public int SequenceChannel
|
||||
{
|
||||
get { return (int)m_messageType - (int)NetPeer.GetDeliveryMethod(m_messageType); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of data contained in this message
|
||||
/// </summary>
|
||||
public NetIncomingMessageType MessageType { get { return m_incomingType; } }
|
||||
public int SequenceChannel { get { return (int)m_receivedMessageType - (int)m_receivedMessageType.GetDeliveryMethod(); } }
|
||||
|
||||
/// <summary>
|
||||
/// IPEndPoint of sender, if any
|
||||
@@ -99,22 +62,40 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
public NetConnection SenderConnection { get { return m_senderConnection; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the message payload in bytes
|
||||
/// </summary>
|
||||
public int LengthBytes
|
||||
{
|
||||
get { return ((m_bitLength + 7) >> 3); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the message payload in bits
|
||||
/// </summary>
|
||||
public int LengthBits
|
||||
{
|
||||
get { return m_bitLength; }
|
||||
internal set { m_bitLength = value; }
|
||||
}
|
||||
|
||||
internal NetIncomingMessage()
|
||||
{
|
||||
}
|
||||
|
||||
internal NetIncomingMessage(byte[] data, int dataLength)
|
||||
internal NetIncomingMessage(NetIncomingMessageType tp)
|
||||
{
|
||||
m_data = data;
|
||||
m_bitLength = dataLength * 8;
|
||||
m_incomingMessageType = tp;
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_bitLength = 0;
|
||||
m_incomingMessageType = NetIncomingMessageType.Error;
|
||||
m_readPosition = 0;
|
||||
m_status = NetIncomingMessageReleaseStatus.NotReleased;
|
||||
m_fragmentationInfo = null;
|
||||
m_receivedMessageType = NetMessageType.LibraryError;
|
||||
m_senderConnection = null;
|
||||
m_bitLength = 0;
|
||||
m_isFragment = false;
|
||||
}
|
||||
|
||||
public void Decrypt(NetXtea tea)
|
||||
@@ -130,34 +111,9 @@ namespace Lidgren.Network
|
||||
m_data = result;
|
||||
}
|
||||
|
||||
public NetIncomingMessage Clone()
|
||||
{
|
||||
NetIncomingMessage retval = new NetIncomingMessage();
|
||||
|
||||
// copy content
|
||||
retval.m_data = new byte[LengthBytes];
|
||||
Buffer.BlockCopy(m_data, 0, retval.m_data, 0, LengthBytes);
|
||||
|
||||
retval.m_bitLength = m_bitLength;
|
||||
retval.m_messageType = m_messageType;
|
||||
retval.m_sequenceNumber = m_sequenceNumber;
|
||||
retval.m_status = m_status;
|
||||
retval.m_incomingType = m_incomingType;
|
||||
retval.m_senderEndpoint = m_senderEndpoint;
|
||||
retval.m_senderConnection = m_senderConnection;
|
||||
retval.m_fragmentationInfo = m_fragmentationInfo;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[NetIncomingMessage {0}, {1}|{2}, {3} bits]",
|
||||
m_incomingType,
|
||||
m_messageType,
|
||||
m_sequenceNumber,
|
||||
m_bitLength
|
||||
);
|
||||
return "[NetIncomingMessage #" + m_sequenceNumber + " " + this.LengthBytes + " bytes]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of incoming message
|
||||
/// The type of a NetIncomingMessage
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
|
||||
public enum NetIncomingMessageType
|
||||
|
||||
@@ -20,148 +20,175 @@ using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
// publicly visible subset of NetMessageType
|
||||
|
||||
/// <summary>
|
||||
/// How the library deals with dropped and delayed messages
|
||||
/// </summary>
|
||||
public enum NetDeliveryMethod : byte
|
||||
{
|
||||
Unknown = 0,
|
||||
Unreliable = 2,
|
||||
UnreliableSequenced = 3,
|
||||
ReliableUnordered = 35,
|
||||
ReliableSequenced = 36,
|
||||
ReliableOrdered = 68,
|
||||
}
|
||||
|
||||
internal enum NetMessageLibraryType : byte
|
||||
{
|
||||
Error = 0,
|
||||
KeepAlive = 1, // used for piggybacking acks
|
||||
Ping = 2, // used for RTT calculation
|
||||
Pong = 3, // used for RTT calculation
|
||||
Connect = 4,
|
||||
ConnectResponse = 5,
|
||||
ConnectionEstablished = 6,
|
||||
Acknowledge = 7,
|
||||
Disconnect = 8,
|
||||
Discovery = 9,
|
||||
DiscoveryResponse = 10,
|
||||
NatPunchMessage = 11, // send between peers
|
||||
NatIntroduction = 12, // send to master server
|
||||
}
|
||||
|
||||
internal enum NetMessageType : byte
|
||||
{
|
||||
Error = 0,
|
||||
Unconnected = 0,
|
||||
|
||||
Library = 1, // NetMessageLibraryType byte follows
|
||||
UserUnreliable = 1,
|
||||
|
||||
UserUnreliable = 2,
|
||||
UserSequenced1 = 2,
|
||||
UserSequenced2 = 3,
|
||||
UserSequenced3 = 4,
|
||||
UserSequenced4 = 5,
|
||||
UserSequenced5 = 6,
|
||||
UserSequenced6 = 7,
|
||||
UserSequenced7 = 8,
|
||||
UserSequenced8 = 9,
|
||||
UserSequenced9 = 10,
|
||||
UserSequenced10 = 11,
|
||||
UserSequenced11 = 12,
|
||||
UserSequenced12 = 13,
|
||||
UserSequenced13 = 14,
|
||||
UserSequenced14 = 15,
|
||||
UserSequenced15 = 16,
|
||||
UserSequenced16 = 17,
|
||||
UserSequenced17 = 18,
|
||||
UserSequenced18 = 19,
|
||||
UserSequenced19 = 20,
|
||||
UserSequenced20 = 21,
|
||||
UserSequenced21 = 22,
|
||||
UserSequenced22 = 23,
|
||||
UserSequenced23 = 24,
|
||||
UserSequenced24 = 25,
|
||||
UserSequenced25 = 26,
|
||||
UserSequenced26 = 27,
|
||||
UserSequenced27 = 28,
|
||||
UserSequenced28 = 29,
|
||||
UserSequenced29 = 30,
|
||||
UserSequenced30 = 31,
|
||||
UserSequenced31 = 32,
|
||||
UserSequenced32 = 33,
|
||||
|
||||
// 3 to 34 = UserSequenced 0 to 31
|
||||
UserSequenced = 3,
|
||||
UserSequenced1 = 4,
|
||||
UserSequenced2 = 5,
|
||||
UserSequenced3 = 6,
|
||||
UserSequenced4 = 7,
|
||||
UserSequenced5 = 8,
|
||||
UserSequenced6 = 9,
|
||||
UserSequenced7 = 10,
|
||||
UserSequenced8 = 11,
|
||||
UserSequenced9 = 12,
|
||||
UserSequenced10 = 13,
|
||||
UserSequenced11 = 14,
|
||||
UserSequenced12 = 15,
|
||||
UserSequenced13 = 16,
|
||||
UserSequenced14 = 17,
|
||||
UserSequenced15 = 18,
|
||||
UserSequenced16 = 19,
|
||||
UserSequenced17 = 20,
|
||||
UserSequenced18 = 21,
|
||||
UserSequenced19 = 22,
|
||||
UserSequenced20 = 23,
|
||||
UserSequenced21 = 24,
|
||||
UserSequenced22 = 25,
|
||||
UserSequenced23 = 26,
|
||||
UserSequenced24 = 27,
|
||||
UserSequenced25 = 28,
|
||||
UserSequenced26 = 29,
|
||||
UserSequenced27 = 30,
|
||||
UserSequenced28 = 31,
|
||||
UserSequenced29 = 32,
|
||||
UserSequenced30 = 33,
|
||||
UserSequenced31 = 34,
|
||||
UserReliableUnordered = 34,
|
||||
|
||||
UserReliableUnordered = 35,
|
||||
UserReliableSequenced1 = 35,
|
||||
UserReliableSequenced2 = 36,
|
||||
UserReliableSequenced3 = 37,
|
||||
UserReliableSequenced4 = 38,
|
||||
UserReliableSequenced5 = 39,
|
||||
UserReliableSequenced6 = 40,
|
||||
UserReliableSequenced7 = 41,
|
||||
UserReliableSequenced8 = 42,
|
||||
UserReliableSequenced9 = 43,
|
||||
UserReliableSequenced10 = 44,
|
||||
UserReliableSequenced11 = 45,
|
||||
UserReliableSequenced12 = 46,
|
||||
UserReliableSequenced13 = 47,
|
||||
UserReliableSequenced14 = 48,
|
||||
UserReliableSequenced15 = 49,
|
||||
UserReliableSequenced16 = 50,
|
||||
UserReliableSequenced17 = 51,
|
||||
UserReliableSequenced18 = 52,
|
||||
UserReliableSequenced19 = 53,
|
||||
UserReliableSequenced20 = 54,
|
||||
UserReliableSequenced21 = 55,
|
||||
UserReliableSequenced22 = 56,
|
||||
UserReliableSequenced23 = 57,
|
||||
UserReliableSequenced24 = 58,
|
||||
UserReliableSequenced25 = 59,
|
||||
UserReliableSequenced26 = 60,
|
||||
UserReliableSequenced27 = 61,
|
||||
UserReliableSequenced28 = 62,
|
||||
UserReliableSequenced29 = 63,
|
||||
UserReliableSequenced30 = 64,
|
||||
UserReliableSequenced31 = 65,
|
||||
UserReliableSequenced32 = 66,
|
||||
|
||||
// 36 to 67 = UserReliableSequenced 0 to 31
|
||||
UserReliableSequenced = 36,
|
||||
UserReliableSequenced1 = 37,
|
||||
UserReliableSequenced2 = 38,
|
||||
UserReliableSequenced3 = 39,
|
||||
UserReliableSequenced4 = 40,
|
||||
UserReliableSequenced5 = 41,
|
||||
UserReliableSequenced6 = 42,
|
||||
UserReliableSequenced7 = 43,
|
||||
UserReliableSequenced8 = 44,
|
||||
UserReliableSequenced9 = 45,
|
||||
UserReliableSequenced10 = 46,
|
||||
UserReliableSequenced11 = 47,
|
||||
UserReliableSequenced12 = 48,
|
||||
UserReliableSequenced13 = 49,
|
||||
UserReliableSequenced14 = 50,
|
||||
UserReliableSequenced15 = 51,
|
||||
UserReliableSequenced16 = 52,
|
||||
UserReliableSequenced17 = 53,
|
||||
UserReliableSequenced18 = 54,
|
||||
UserReliableSequenced19 = 55,
|
||||
UserReliableSequenced20 = 56,
|
||||
UserReliableSequenced21 = 57,
|
||||
UserReliableSequenced22 = 58,
|
||||
UserReliableSequenced23 = 59,
|
||||
UserReliableSequenced24 = 60,
|
||||
UserReliableSequenced25 = 61,
|
||||
UserReliableSequenced26 = 62,
|
||||
UserReliableSequenced27 = 63,
|
||||
UserReliableSequenced28 = 64,
|
||||
UserReliableSequenced29 = 65,
|
||||
UserReliableSequenced30 = 66,
|
||||
UserReliableSequenced31 = 67,
|
||||
UserReliableOrdered1 = 67,
|
||||
UserReliableOrdered2 = 68,
|
||||
UserReliableOrdered3 = 69,
|
||||
UserReliableOrdered4 = 70,
|
||||
UserReliableOrdered5 = 71,
|
||||
UserReliableOrdered6 = 72,
|
||||
UserReliableOrdered7 = 73,
|
||||
UserReliableOrdered8 = 74,
|
||||
UserReliableOrdered9 = 75,
|
||||
UserReliableOrdered10 = 76,
|
||||
UserReliableOrdered11 = 77,
|
||||
UserReliableOrdered12 = 78,
|
||||
UserReliableOrdered13 = 79,
|
||||
UserReliableOrdered14 = 80,
|
||||
UserReliableOrdered15 = 81,
|
||||
UserReliableOrdered16 = 82,
|
||||
UserReliableOrdered17 = 83,
|
||||
UserReliableOrdered18 = 84,
|
||||
UserReliableOrdered19 = 85,
|
||||
UserReliableOrdered20 = 86,
|
||||
UserReliableOrdered21 = 87,
|
||||
UserReliableOrdered22 = 88,
|
||||
UserReliableOrdered23 = 89,
|
||||
UserReliableOrdered24 = 90,
|
||||
UserReliableOrdered25 = 91,
|
||||
UserReliableOrdered26 = 92,
|
||||
UserReliableOrdered27 = 93,
|
||||
UserReliableOrdered28 = 94,
|
||||
UserReliableOrdered29 = 95,
|
||||
UserReliableOrdered30 = 96,
|
||||
UserReliableOrdered31 = 97,
|
||||
UserReliableOrdered32 = 98,
|
||||
|
||||
// 68 to 99 = UserReliableOrdered 0 to 31
|
||||
UserReliableOrdered = 68,
|
||||
UserReliableOrdered1 = 69,
|
||||
UserReliableOrdered2 = 70,
|
||||
UserReliableOrdered3 = 71,
|
||||
UserReliableOrdered4 = 72,
|
||||
UserReliableOrdered5 = 73,
|
||||
UserReliableOrdered6 = 74,
|
||||
UserReliableOrdered7 = 75,
|
||||
UserReliableOrdered8 = 76,
|
||||
UserReliableOrdered9 = 77,
|
||||
UserReliableOrdered10 = 78,
|
||||
UserReliableOrdered11 = 79,
|
||||
UserReliableOrdered12 = 80,
|
||||
UserReliableOrdered13 = 81,
|
||||
UserReliableOrdered14 = 82,
|
||||
UserReliableOrdered15 = 83,
|
||||
UserReliableOrdered16 = 84,
|
||||
UserReliableOrdered17 = 85,
|
||||
UserReliableOrdered18 = 86,
|
||||
UserReliableOrdered19 = 87,
|
||||
UserReliableOrdered20 = 88,
|
||||
UserReliableOrdered21 = 89,
|
||||
UserReliableOrdered22 = 90,
|
||||
UserReliableOrdered23 = 91,
|
||||
UserReliableOrdered24 = 92,
|
||||
UserReliableOrdered25 = 93,
|
||||
UserReliableOrdered26 = 94,
|
||||
UserReliableOrdered27 = 95,
|
||||
UserReliableOrdered28 = 96,
|
||||
UserReliableOrdered29 = 97,
|
||||
UserReliableOrdered30 = 98,
|
||||
UserReliableOrdered31 = 99,
|
||||
Unused1 = 99,
|
||||
Unused2 = 100,
|
||||
Unused3 = 101,
|
||||
Unused4 = 102,
|
||||
Unused5 = 103,
|
||||
Unused6 = 104,
|
||||
Unused7 = 105,
|
||||
Unused8 = 106,
|
||||
Unused9 = 107,
|
||||
Unused10 = 108,
|
||||
Unused11 = 109,
|
||||
Unused12 = 110,
|
||||
Unused13 = 111,
|
||||
Unused14 = 112,
|
||||
Unused15 = 113,
|
||||
Unused16 = 114,
|
||||
Unused17 = 115,
|
||||
Unused18 = 116,
|
||||
Unused19 = 117,
|
||||
Unused20 = 118,
|
||||
Unused21 = 119,
|
||||
Unused22 = 120,
|
||||
Unused23 = 121,
|
||||
Unused24 = 122,
|
||||
Unused25 = 123,
|
||||
Unused26 = 124,
|
||||
Unused27 = 125,
|
||||
Unused28 = 126,
|
||||
Unused29 = 127,
|
||||
|
||||
LibraryError = 128,
|
||||
Ping = 129, // used for RTT calculation
|
||||
Pong = 130, // used for RTT calculation
|
||||
Connect = 131,
|
||||
ConnectResponse = 132,
|
||||
ConnectionEstablished = 133,
|
||||
Acknowledge = 134,
|
||||
Disconnect = 135,
|
||||
Discovery = 136,
|
||||
DiscoveryResponse = 137,
|
||||
NatPunchMessage = 138, // send between peers
|
||||
NatIntroduction = 139, // send to master server
|
||||
}
|
||||
|
||||
internal static class NetMessageTypeExtensions
|
||||
{
|
||||
internal static bool IsLibrary(this NetMessageType tp)
|
||||
{
|
||||
return tp >= NetMessageType.LibraryError;
|
||||
}
|
||||
|
||||
internal static NetDeliveryMethod GetDeliveryMethod(this NetMessageType mtp)
|
||||
{
|
||||
if (mtp >= NetMessageType.UserReliableOrdered1)
|
||||
return NetDeliveryMethod.ReliableOrdered;
|
||||
else if (mtp >= NetMessageType.UserReliableSequenced1)
|
||||
return NetDeliveryMethod.ReliableSequenced;
|
||||
else if (mtp >= NetMessageType.UserReliableUnordered)
|
||||
return NetDeliveryMethod.ReliableUnordered;
|
||||
else if (mtp >= NetMessageType.UserSequenced1)
|
||||
return NetDeliveryMethod.UnreliableSequenced;
|
||||
return NetDeliveryMethod.Unreliable;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,23 +18,23 @@ namespace Lidgren.Network
|
||||
{
|
||||
// send message to client
|
||||
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1);
|
||||
msg.m_libType = NetMessageLibraryType.NatIntroduction;
|
||||
msg.m_messageType = NetMessageType.NatIntroduction;
|
||||
msg.Write(false);
|
||||
msg.WritePadBits();
|
||||
msg.Write(hostInternal);
|
||||
msg.Write(hostExternal);
|
||||
msg.Write(token);
|
||||
SendUnconnectedLibrary(msg, clientExternal);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(clientExternal, msg));
|
||||
|
||||
// send message to host
|
||||
msg = CreateMessage(10 + token.Length + 1);
|
||||
msg.m_libType = NetMessageLibraryType.NatIntroduction;
|
||||
msg.m_messageType = NetMessageType.NatIntroduction;
|
||||
msg.Write(true);
|
||||
msg.WritePadBits();
|
||||
msg.Write(clientInternal);
|
||||
msg.Write(clientExternal);
|
||||
msg.Write(token);
|
||||
SendUnconnectedLibrary(msg, hostExternal);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(hostExternal, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,8 +45,8 @@ namespace Lidgren.Network
|
||||
VerifyNetworkThread();
|
||||
|
||||
// read intro
|
||||
NetIncomingMessage tmp = new NetIncomingMessage(m_receiveBuffer, 1000); // never mind length
|
||||
tmp.Position = (ptr * 8);
|
||||
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
|
||||
|
||||
byte hostByte = tmp.ReadByte();
|
||||
IPEndPoint remoteInternal = tmp.ReadIPEndpoint();
|
||||
IPEndPoint remoteExternal = tmp.ReadIPEndpoint();
|
||||
@@ -62,17 +62,17 @@ namespace Lidgren.Network
|
||||
|
||||
// send internal punch
|
||||
punch = CreateMessage(1);
|
||||
punch.m_libType = NetMessageLibraryType.NatPunchMessage;
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write(hostByte);
|
||||
punch.Write(token);
|
||||
SendUnconnectedLibrary(punch, remoteInternal);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteInternal, punch));
|
||||
|
||||
// send external punch
|
||||
punch = CreateMessage(1);
|
||||
punch.m_libType = NetMessageLibraryType.NatPunchMessage;
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write(hostByte);
|
||||
punch.Write(token);
|
||||
SendUnconnectedLibrary(punch, remoteExternal);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteExternal, punch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,8 +80,7 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
private void HandleNatPunch(int ptr, IPEndPoint senderEndpoint)
|
||||
{
|
||||
NetIncomingMessage tmp = new NetIncomingMessage(m_receiveBuffer, 1000); // never mind length
|
||||
tmp.Position = (ptr * 8);
|
||||
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
|
||||
|
||||
byte fromHostByte = tmp.ReadByte();
|
||||
if (fromHostByte == 0)
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetOutgoingMessage : Stream
|
||||
{
|
||||
public override bool CanRead { get { return false; } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanWrite { get { return true; } }
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
// no op
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the stream, in bytes
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get { return (long)LengthBytes; }
|
||||
}
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NetException("Position in bytes is not relevant since the bit count can vary");
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new NetException("It's not possible to seek in this message");
|
||||
}
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NetException("It's not possible to read from this message");
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NetException("It's not possible to seek in this message");
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NetException("It's not possible to set the length of this message");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Lidgren.Network
|
||||
Type tp = ob.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetIncomingMessage.SortMembersList(fields);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
foreach (FieldInfo fi in fields)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ namespace Lidgren.Network
|
||||
Type tp = ob.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetIncomingMessage.SortMembersList(fields);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
foreach (PropertyInfo fi in fields)
|
||||
{
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace Lidgren.Network
|
||||
m_bitLength += bits;
|
||||
}
|
||||
|
||||
public override void Write(byte[] source, int offsetInBytes, int numberOfBytes)
|
||||
public void Write(byte[] source, int offsetInBytes, int numberOfBytes)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
|
||||
@@ -15,182 +15,103 @@ PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
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.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Outgoing message used to send data to remote peer(s)
|
||||
/// </summary>
|
||||
[DebuggerDisplay("LengthBits={LengthBits}")]
|
||||
public sealed partial class NetOutgoingMessage
|
||||
{
|
||||
// reference count before message can be recycled
|
||||
internal int m_numUnfinishedSendings;
|
||||
internal NetMessageType m_messageType;
|
||||
internal bool m_isSent;
|
||||
internal int m_recyclingCount;
|
||||
|
||||
internal bool m_wasSent; // true is SendMessage() public method has been called
|
||||
internal NetMessageLibraryType m_libType = NetMessageLibraryType.Error;
|
||||
|
||||
//internal int m_fragmentGroupId;
|
||||
//internal int m_fragmentNumber;
|
||||
//internal int m_fragmentTotalCount;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this message has been passed to SendMessage() already
|
||||
/// </summary>
|
||||
public bool IsSent { get { return m_wasSent; } }
|
||||
internal int m_fragmentGroup; // which group of fragments ths belongs to
|
||||
internal int m_fragmentGroupTotalBits; // total number of bits in this group
|
||||
internal int m_fragmentChunkByteSize; // size, in bytes, of every chunk but the last one
|
||||
internal int m_fragmentChunkNumber; // which number chunk this is, starting with 0
|
||||
|
||||
internal NetOutgoingMessage()
|
||||
{
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
public void Reset()
|
||||
{
|
||||
NetException.Assert(m_numUnfinishedSendings == 0, "Ouch! Resetting NetOutgoingMessage still in some queue!");
|
||||
NetException.Assert(m_wasSent == true, "Ouch! Resetting unsent message!");
|
||||
|
||||
m_messageType = NetMessageType.LibraryError;
|
||||
m_bitLength = 0;
|
||||
m_libType = NetMessageLibraryType.Error;
|
||||
m_wasSent = false;
|
||||
m_isSent = false;
|
||||
m_recyclingCount = 0;
|
||||
m_fragmentGroup = 0;
|
||||
}
|
||||
|
||||
internal static int EncodeAcksMessage(byte[] buffer, int ptr, NetConnection conn, int maxBytesPayload)
|
||||
internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
|
||||
{
|
||||
// TODO: if appropriate; make bit vector of adjacent acks
|
||||
// 8 bits - NetMessageType
|
||||
// 1 bit - Fragment?
|
||||
// 15 bits - Sequence number
|
||||
// 16 bits - Payload length in bits
|
||||
|
||||
buffer[ptr++] = (byte)NetMessageType.Library;
|
||||
buffer[ptr++] = (byte)NetMessageLibraryType.Acknowledge;
|
||||
intoBuffer[ptr++] = (byte)m_messageType;
|
||||
|
||||
Queue<int> acks = conn.m_acknowledgesToSend;
|
||||
byte low = (byte)((sequenceNumber << 1) | (m_fragmentGroup == 0 ? 0 : 1));
|
||||
intoBuffer[ptr++] = low;
|
||||
intoBuffer[ptr++] = (byte)(sequenceNumber >> 7);
|
||||
|
||||
int maxAcks = maxBytesPayload / 3;
|
||||
int acksToEncode = (acks.Count < maxAcks ? acks.Count : maxAcks);
|
||||
|
||||
int payloadBitsLength = acksToEncode * 3 * 8;
|
||||
if (payloadBitsLength < 127)
|
||||
if (m_fragmentGroup == 0)
|
||||
{
|
||||
buffer[ptr++] = (byte)payloadBitsLength;
|
||||
intoBuffer[ptr++] = (byte)m_bitLength;
|
||||
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
|
||||
|
||||
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
|
||||
if (byteLen > 0)
|
||||
{
|
||||
Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
|
||||
ptr += byteLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[ptr++] = (byte)((payloadBitsLength & 127) | 128);
|
||||
buffer[ptr++] = (byte)(payloadBitsLength >> 7);
|
||||
}
|
||||
int wasPtr = ptr;
|
||||
intoBuffer[ptr++] = (byte)m_bitLength;
|
||||
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
|
||||
|
||||
for (int i = 0; i < acksToEncode; i++)
|
||||
//
|
||||
// write fragmentation header
|
||||
//
|
||||
ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
|
||||
int hdrLen = ptr - wasPtr - 2;
|
||||
|
||||
// update length
|
||||
int realBitLength = m_bitLength + (hdrLen * 8);
|
||||
intoBuffer[wasPtr] = (byte)realBitLength;
|
||||
intoBuffer[wasPtr + 1] = (byte)(realBitLength >> 8);
|
||||
|
||||
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
|
||||
if (byteLen > 0)
|
||||
{
|
||||
int ack = acks.Dequeue();
|
||||
buffer[ptr++] = (byte)ack; // message type
|
||||
buffer[ptr++] = (byte)(ack >> 8); // seqnr low
|
||||
buffer[ptr++] = (byte)(ack >> 16); // seqnr high
|
||||
Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
|
||||
ptr += byteLen;
|
||||
}
|
||||
}
|
||||
|
||||
NetException.Assert(ptr > 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal int EncodeUnfragmented(byte[] buffer, int ptr, NetMessageType tp, ushort sequenceNumber)
|
||||
internal int GetEncodedSize()
|
||||
{
|
||||
// message type
|
||||
buffer[ptr++] = (byte)tp; // | (m_fragmentGroupId == -1 ? 0 : 128));
|
||||
int retval = 5; // regular headers
|
||||
if (m_fragmentGroup != 0)
|
||||
retval += NetFragmentationHelper.GetFragmentationHeaderSize(m_fragmentGroup, m_fragmentGroupTotalBits / 8, m_fragmentChunkByteSize, m_fragmentChunkNumber);
|
||||
retval += this.LengthBytes;
|
||||
|
||||
if (tp == NetMessageType.Library)
|
||||
buffer[ptr++] = (byte)m_libType;
|
||||
|
||||
// channel sequence number
|
||||
if (tp >= NetMessageType.UserSequenced)
|
||||
{
|
||||
ushort seqNr = (ushort)sequenceNumber;
|
||||
buffer[ptr++] = (byte)seqNr;
|
||||
buffer[ptr++] = (byte)(seqNr >> 8);
|
||||
}
|
||||
|
||||
// payload length
|
||||
int payloadBitsLength = LengthBits;
|
||||
int payloadBytesLength = NetUtility.BytesToHoldBits(payloadBitsLength);
|
||||
if (payloadBitsLength < 127)
|
||||
{
|
||||
buffer[ptr++] = (byte)payloadBitsLength;
|
||||
}
|
||||
else if (payloadBitsLength < 32768)
|
||||
{
|
||||
buffer[ptr++] = (byte)((payloadBitsLength & 127) | 128);
|
||||
buffer[ptr++] = (byte)(payloadBitsLength >> 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NetException("Packet content too large; 4095 bytes maximum");
|
||||
}
|
||||
|
||||
// payload
|
||||
if (payloadBitsLength > 0)
|
||||
{
|
||||
// zero out last byte
|
||||
buffer[ptr + payloadBytesLength] = 0;
|
||||
|
||||
Buffer.BlockCopy(m_data, 0, buffer, ptr, payloadBytesLength);
|
||||
ptr += payloadBytesLength;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal int EncodeFragmented(byte[] buffer, int ptr, NetSending send, int mtu)
|
||||
{
|
||||
NetException.Assert(send.MessageType != NetMessageType.Library, "Library messages cant be fragmented");
|
||||
|
||||
// message type
|
||||
buffer[ptr++] = (byte)((int)send.MessageType | 128);
|
||||
|
||||
// channel sequence number
|
||||
if (send.MessageType >= NetMessageType.UserSequenced)
|
||||
{
|
||||
ushort seqNr = (ushort)send.SequenceNumber;
|
||||
buffer[ptr++] = (byte)seqNr;
|
||||
buffer[ptr++] = (byte)(seqNr >> 8);
|
||||
}
|
||||
|
||||
// calculate fragment payload length
|
||||
mtu -= NetConstants.FragmentHeaderSize; // size of fragmentation info
|
||||
int thisFragmentLength = (send.FragmentNumber == send.FragmentTotalCount - 1 ? (send.Message.LengthBytes - (mtu * (send.FragmentTotalCount - 1))) : mtu);
|
||||
|
||||
int payloadBitsLength = thisFragmentLength * 8;
|
||||
if (payloadBitsLength < 127)
|
||||
{
|
||||
buffer[ptr++] = (byte)payloadBitsLength;
|
||||
}
|
||||
else if (payloadBitsLength < 32768)
|
||||
{
|
||||
buffer[ptr++] = (byte)((payloadBitsLength & 127) | 128);
|
||||
buffer[ptr++] = (byte)(payloadBitsLength >> 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NetException("Packet content too large; 4095 bytes maximum");
|
||||
}
|
||||
|
||||
// fragmentation info
|
||||
buffer[ptr++] = (byte)send.FragmentGroupId;
|
||||
buffer[ptr++] = (byte)(send.FragmentGroupId >> 8);
|
||||
buffer[ptr++] = (byte)send.FragmentTotalCount;
|
||||
buffer[ptr++] = (byte)(send.FragmentTotalCount >> 8);
|
||||
buffer[ptr++] = (byte)send.FragmentNumber;
|
||||
buffer[ptr++] = (byte)(send.FragmentNumber >> 8);
|
||||
|
||||
// payload
|
||||
if (payloadBitsLength > 0)
|
||||
{
|
||||
// zero out last byte
|
||||
buffer[ptr + thisFragmentLength] = 0;
|
||||
|
||||
int offset = (mtu * send.FragmentNumber);
|
||||
|
||||
Buffer.BlockCopy(m_data, offset, buffer, ptr, thisFragmentLength);
|
||||
ptr += thisFragmentLength;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
return retval;
|
||||
}
|
||||
|
||||
public void Encrypt(NetXtea tea)
|
||||
@@ -204,14 +125,14 @@ namespace Lidgren.Network
|
||||
Write((byte)0);
|
||||
|
||||
byte[] result = new byte[m_data.Length];
|
||||
for(int i=0;i<blocksNeeded;i++)
|
||||
for (int i = 0; i < blocksNeeded; i++)
|
||||
tea.EncryptBlock(m_data, (i * 8), result, (i * 8));
|
||||
m_data = result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetOutgoingMessage " + LengthBytes + " bytes]";
|
||||
return "[NetOutgoingMessage " + m_messageType + " " + this.LengthBytes + " bytes]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace Lidgren.Network
|
||||
public void DiscoverLocalPeers(int serverPort)
|
||||
{
|
||||
NetOutgoingMessage om = CreateMessage(0);
|
||||
om.m_libType = NetMessageLibraryType.Discovery;
|
||||
SendUnconnectedLibrary(om, new IPEndPoint(IPAddress.Broadcast, serverPort));
|
||||
om.m_messageType = NetMessageType.Discovery;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(IPAddress.Broadcast, serverPort), om));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -23,18 +23,35 @@ namespace Lidgren.Network
|
||||
IPAddress address = NetUtility.Resolve(host);
|
||||
if (address == null)
|
||||
return false;
|
||||
return DiscoverKnownPeer(new IPEndPoint(address, serverPort));
|
||||
DiscoverKnownPeer(new IPEndPoint(address, serverPort));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a discovery signal to a single known host
|
||||
/// </summary>
|
||||
public bool DiscoverKnownPeer(IPEndPoint endpoint)
|
||||
public void DiscoverKnownPeer(IPEndPoint endpoint)
|
||||
{
|
||||
NetOutgoingMessage om = CreateMessage(0);
|
||||
om.m_libType = NetMessageLibraryType.Discovery;
|
||||
SendUnconnectedLibrary(om, endpoint);
|
||||
return true;
|
||||
om.m_messageType = NetMessageType.Discovery;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(endpoint, om));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a discovery response message
|
||||
/// </summary>
|
||||
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg == null)
|
||||
msg = CreateMessage(0);
|
||||
else if (msg.m_isSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
|
||||
msg.m_messageType = NetMessageType.DiscoveryResponse;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
150
Lidgren.Network/NetPeer.Fragmentation.cs
Normal file
150
Lidgren.Network/NetPeer.Fragmentation.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal class ReceivedFragmentGroup
|
||||
{
|
||||
public float LastReceived;
|
||||
public byte[] Data;
|
||||
public NetBitVector ReceivedChunks;
|
||||
}
|
||||
|
||||
public partial class NetPeer
|
||||
{
|
||||
private int m_lastUsedFragmentGroup;
|
||||
|
||||
private Dictionary<int, ReceivedFragmentGroup> m_receivedFragmentGroups;
|
||||
|
||||
// on user thread
|
||||
private void SendFragmentedMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
int group = Interlocked.Increment(ref m_lastUsedFragmentGroup);
|
||||
if (group >= NetConstants.MaxFragmentationGroups)
|
||||
{
|
||||
// TODO: not thread safe; but in practice probably not an issue
|
||||
m_lastUsedFragmentGroup = 1;
|
||||
group = 1;
|
||||
}
|
||||
msg.m_fragmentGroup = group;
|
||||
|
||||
// do not send msg; but set fragmentgroup in case user tries to recycle it immediately
|
||||
|
||||
// create fragmentation specifics
|
||||
int totalBytes = msg.LengthBytes;
|
||||
int mtu = m_configuration.MaximumTransmissionUnit;
|
||||
|
||||
int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);
|
||||
|
||||
int numChunks = totalBytes / bytesPerChunk;
|
||||
if (numChunks * bytesPerChunk < totalBytes)
|
||||
numChunks++;
|
||||
|
||||
int bitsPerChunk = bytesPerChunk * 8;
|
||||
int bitsLeft = msg.LengthBits;
|
||||
for (int i = 0; i < numChunks; i++)
|
||||
{
|
||||
NetOutgoingMessage chunk = CreateMessage(mtu);
|
||||
|
||||
chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
|
||||
chunk.m_data = msg.m_data;
|
||||
chunk.m_fragmentGroup = group;
|
||||
chunk.m_fragmentGroupTotalBits = totalBytes * 8;
|
||||
chunk.m_fragmentChunkByteSize = bytesPerChunk;
|
||||
chunk.m_fragmentChunkNumber = i;
|
||||
|
||||
NetException.Assert(chunk.m_bitLength != 0);
|
||||
NetException.Assert(chunk.GetEncodedSize() < mtu);
|
||||
|
||||
Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);
|
||||
|
||||
foreach (NetConnection recipient in recipients)
|
||||
recipient.EnqueueMessage(chunk, method, sequenceChannel);
|
||||
|
||||
bitsLeft -= bitsPerChunk;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void HandleReleasedFragment(NetIncomingMessage im)
|
||||
{
|
||||
//
|
||||
// read fragmentation header and combine fragments
|
||||
//
|
||||
int group;
|
||||
int totalBits;
|
||||
int chunkByteSize;
|
||||
int chunkNumber;
|
||||
int ptr = NetFragmentationHelper.ReadHeader(
|
||||
im.m_data, 0,
|
||||
out group,
|
||||
out totalBits,
|
||||
out chunkByteSize,
|
||||
out chunkNumber
|
||||
);
|
||||
|
||||
NetException.Assert(im.LengthBytes > ptr);
|
||||
|
||||
NetException.Assert(group > 0);
|
||||
NetException.Assert(totalBits > 0);
|
||||
NetException.Assert(chunkByteSize > 0);
|
||||
|
||||
int totalBytes = NetUtility.BytesToHoldBits((int)totalBits);
|
||||
int totalNumChunks = totalBytes / chunkByteSize;
|
||||
if (totalNumChunks * chunkByteSize < totalBytes)
|
||||
totalNumChunks++;
|
||||
|
||||
NetException.Assert(chunkNumber < totalNumChunks);
|
||||
|
||||
if (chunkNumber >= totalNumChunks)
|
||||
{
|
||||
LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
ReceivedFragmentGroup info;
|
||||
if (!m_receivedFragmentGroups.TryGetValue(group, out info))
|
||||
{
|
||||
info = new ReceivedFragmentGroup();
|
||||
info.Data = new byte[totalBytes];
|
||||
info.ReceivedChunks = new NetBitVector(totalNumChunks);
|
||||
m_receivedFragmentGroups[group] = info;
|
||||
}
|
||||
|
||||
info.ReceivedChunks[chunkNumber] = true;
|
||||
info.LastReceived = (float)NetTime.Now;
|
||||
|
||||
// copy to data
|
||||
int offset = (chunkNumber * chunkByteSize);
|
||||
Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr);
|
||||
|
||||
int cnt = info.ReceivedChunks.Count();
|
||||
//Console.WriteLine("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")");
|
||||
|
||||
LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");
|
||||
|
||||
if (info.ReceivedChunks.Count() == totalNumChunks)
|
||||
{
|
||||
// Done! Transform this incoming message
|
||||
im.m_data = info.Data;
|
||||
im.m_bitLength = (int)totalBits;
|
||||
im.m_isFragment = false;
|
||||
|
||||
LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");
|
||||
|
||||
ReleaseMessage(im);
|
||||
}
|
||||
else
|
||||
{
|
||||
// data has been copied; recycle this incoming message
|
||||
Recycle(im);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +1,66 @@
|
||||
/* 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.
|
||||
|
||||
*/
|
||||
#define IS_MAC_AVAILABLE
|
||||
#define IS_MAC_AVAILABLE
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Diagnostics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Net.Sockets;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
private EndPoint m_senderRemote;
|
||||
internal byte[] m_receiveBuffer;
|
||||
private NetPeerStatus m_status;
|
||||
private Thread m_networkThread;
|
||||
private Socket m_socket;
|
||||
internal byte[] m_sendBuffer;
|
||||
internal Socket m_socket;
|
||||
internal byte[] m_macAddressBytes;
|
||||
private int m_listenPort;
|
||||
internal byte[] m_receiveBuffer;
|
||||
internal NetIncomingMessage m_readHelperMessage;
|
||||
private EndPoint m_senderRemote;
|
||||
private object m_initializeLock = new object();
|
||||
|
||||
internal readonly NetPeerConfiguration m_configuration;
|
||||
private readonly NetQueue<NetIncomingMessage> m_releasedIncomingMessages;
|
||||
internal readonly NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>> m_unsentUnconnectedMessages;
|
||||
|
||||
internal Dictionary<IPEndPoint, NetConnection> m_handshakes;
|
||||
|
||||
internal readonly NetPeerStatistics m_statistics;
|
||||
internal long m_uniqueIdentifier;
|
||||
|
||||
private AutoResetEvent m_messageReceivedEvent = new AutoResetEvent(false);
|
||||
|
||||
private readonly NetQueue<NetIncomingMessage> m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(8);
|
||||
private readonly NetQueue<NetSending> m_unsentUnconnectedMessage = new NetQueue<NetSending>(2);
|
||||
|
||||
/// <summary>
|
||||
/// Signalling event which can be waited on to determine when a message is queued for reading.
|
||||
/// Note that there is no guarantee that after the event is signaled the blocked thread will
|
||||
/// find the message in the queue. Other user created threads could be preempted and dequeue
|
||||
/// the message before the waiting thread wakes up.
|
||||
/// </summary>
|
||||
public AutoResetEvent MessageReceivedEvent { get { return m_messageReceivedEvent; } }
|
||||
|
||||
internal void ReleaseMessage(NetIncomingMessage msg)
|
||||
{
|
||||
NetException.Assert(msg.m_status != NetIncomingMessageReleaseStatus.ReleasedToApplication, "Message released to application twice!");
|
||||
NetException.Assert(msg.m_incomingMessageType != NetIncomingMessageType.Error);
|
||||
|
||||
NetException.Assert(msg.m_fragmentationInfo == null, "Fragment released to application!");
|
||||
if (msg.m_isFragment)
|
||||
{
|
||||
HandleReleasedFragment(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
msg.m_status = NetIncomingMessageReleaseStatus.ReleasedToApplication;
|
||||
m_releasedIncomingMessages.Enqueue(msg);
|
||||
if (m_messageReceivedEvent != null)
|
||||
m_messageReceivedEvent.Set();
|
||||
}
|
||||
|
||||
[System.Diagnostics.Conditional("DEBUG")]
|
||||
internal void VerifyNetworkThread()
|
||||
{
|
||||
Thread ct = System.Threading.Thread.CurrentThread;
|
||||
if (ct != m_networkThread)
|
||||
throw new NetException("Executing on wrong thread! Should be library system thread (is " + ct.Name + " mId " + ct.ManagedThreadId + ")");
|
||||
}
|
||||
|
||||
private void InitializeNetwork()
|
||||
{
|
||||
//
|
||||
// Initialize
|
||||
//
|
||||
|
||||
InitializeRecycling();
|
||||
|
||||
System.Net.NetworkInformation.PhysicalAddress pa = null;
|
||||
#if IS_MAC_AVAILABLE
|
||||
pa = NetUtility.GetMacAddress();
|
||||
if (pa != null)
|
||||
{
|
||||
m_macAddressBytes = pa.GetAddressBytes();
|
||||
LogVerbose("Mac address is " + NetUtility.ToHexString(m_macAddressBytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogWarning("Failed to get Mac address");
|
||||
}
|
||||
#else
|
||||
// random bytes is better than nothing
|
||||
m_macAddressBytes = new byte[6];
|
||||
NetRandom.Instance.NextBytes(m_macAddressBytes);
|
||||
#endif
|
||||
LogDebug("Initializing Network");
|
||||
|
||||
lock (m_initializeLock)
|
||||
{
|
||||
m_configuration.Lock();
|
||||
|
||||
if (m_status == NetPeerStatus.Running)
|
||||
return;
|
||||
|
||||
m_statistics.Reset();
|
||||
InitializePools();
|
||||
|
||||
m_releasedIncomingMessages.Clear();
|
||||
m_unsentUnconnectedMessages.Clear();
|
||||
m_handshakes.Clear();
|
||||
|
||||
// bind to socket
|
||||
IPEndPoint iep = null;
|
||||
@@ -115,36 +76,38 @@ namespace Lidgren.Network
|
||||
|
||||
IPEndPoint boundEp = m_socket.LocalEndPoint as IPEndPoint;
|
||||
LogDebug("Socket bound to " + boundEp + ": " + m_socket.IsBound);
|
||||
|
||||
m_listenPort = boundEp.Port;
|
||||
|
||||
int first = (pa == null ? this.GetHashCode() : pa.GetHashCode());
|
||||
int second = boundEp.GetHashCode();
|
||||
|
||||
byte[] raw = new byte[8];
|
||||
raw[0] = (byte)first;
|
||||
raw[1] = (byte)(first << 8);
|
||||
raw[2] = (byte)(first << 16);
|
||||
raw[3] = (byte)(first << 24);
|
||||
raw[4] = (byte)second;
|
||||
raw[5] = (byte)(second << 8);
|
||||
raw[6] = (byte)(second << 16);
|
||||
raw[7] = (byte)(second << 24);
|
||||
m_uniqueIdentifier = BitConverter.ToInt64(NetSha.Hash(raw), 0);
|
||||
|
||||
m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize];
|
||||
m_sendBuffer = new byte[m_configuration.SendBufferSize];
|
||||
m_readHelperMessage = new NetIncomingMessage(NetIncomingMessageType.Error);
|
||||
m_readHelperMessage.m_data = m_receiveBuffer;
|
||||
|
||||
LogVerbose("Initialization done");
|
||||
byte[] macBytes = new byte[8];
|
||||
NetRandom.Instance.NextBytes(macBytes);
|
||||
|
||||
#if IS_MAC_AVAILABLE
|
||||
System.Net.NetworkInformation.PhysicalAddress pa = NetUtility.GetMacAddress();
|
||||
if (pa != null)
|
||||
{
|
||||
macBytes = pa.GetAddressBytes();
|
||||
LogVerbose("Mac address is " + NetUtility.ToHexString(macBytes));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogWarning("Failed to get Mac address");
|
||||
}
|
||||
#endif
|
||||
byte[] epBytes = BitConverter.GetBytes(boundEp.GetHashCode());
|
||||
byte[] combined = new byte[epBytes.Length + macBytes.Length];
|
||||
Array.Copy(epBytes, 0, combined, 0, epBytes.Length);
|
||||
Array.Copy(macBytes, 0, combined, epBytes.Length, macBytes.Length);
|
||||
m_uniqueIdentifier = BitConverter.ToInt64(SHA1.Create().ComputeHash(combined), 0);
|
||||
|
||||
// only set Running if everything succeeds
|
||||
m_status = NetPeerStatus.Running;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Network loop
|
||||
//
|
||||
private void NetworkLoop()
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
@@ -169,14 +132,26 @@ namespace Lidgren.Network
|
||||
//
|
||||
// perform shutdown
|
||||
//
|
||||
ExecutePeerShutdown();
|
||||
}
|
||||
|
||||
private void ExecutePeerShutdown()
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
LogDebug("Shutting down...");
|
||||
|
||||
// disconnect and make one final heartbeat
|
||||
lock (m_connections)
|
||||
{
|
||||
foreach (NetConnection conn in m_connections)
|
||||
if (conn.m_status == NetConnectionStatus.Connected || conn.m_status == NetConnectionStatus.Connecting)
|
||||
conn.Disconnect(m_shutdownReason);
|
||||
conn.Shutdown(m_shutdownReason);
|
||||
}
|
||||
|
||||
lock (m_handshakes)
|
||||
{
|
||||
foreach (NetConnection conn in m_handshakes.Values)
|
||||
conn.Shutdown(m_shutdownReason);
|
||||
}
|
||||
|
||||
// one final heartbeat, will send stuff and do disconnect
|
||||
@@ -203,6 +178,12 @@ namespace Lidgren.Network
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
LogDebug("Shutdown complete");
|
||||
}
|
||||
|
||||
m_receiveBuffer = null;
|
||||
m_sendBuffer = null;
|
||||
m_unsentUnconnectedMessages.Clear();
|
||||
m_connections.Clear();
|
||||
m_handshakes.Clear();
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -212,78 +193,60 @@ namespace Lidgren.Network
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
float now = (float)NetTime.Now;
|
||||
|
||||
// do handshake heartbeats
|
||||
foreach (NetConnection conn in m_handshakes.Values)
|
||||
{
|
||||
conn.UnconnectedHeartbeat(now);
|
||||
if (conn.m_status == NetConnectionStatus.Connected || conn.m_status == NetConnectionStatus.Disconnected)
|
||||
break; // collection is modified
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
// send delayed packets
|
||||
SendDelayedPackets();
|
||||
#endif
|
||||
|
||||
// connection approval
|
||||
CheckPendingConnections();
|
||||
|
||||
double now = NetTime.Now;
|
||||
|
||||
// do connection heartbeats
|
||||
lock (m_connections)
|
||||
{
|
||||
foreach (NetConnection conn in m_connections)
|
||||
{
|
||||
conn.Heartbeat(now);
|
||||
if (conn.m_status == NetConnectionStatus.Disconnected)
|
||||
{
|
||||
RemoveConnection(conn);
|
||||
//
|
||||
// remove connection
|
||||
//
|
||||
m_connections.Remove(conn);
|
||||
m_connectionLookup.Remove(conn.RemoteEndpoint);
|
||||
break; // can't continue iteration here
|
||||
}
|
||||
}
|
||||
|
||||
// send unconnected sends
|
||||
NetSending uncSend;
|
||||
while (m_unsentUnconnectedMessage.TryDequeue(out uncSend))
|
||||
{
|
||||
//
|
||||
// TODO: use throttling here
|
||||
//
|
||||
|
||||
int ptr = uncSend.Message.EncodeUnfragmented(m_sendBuffer, 0, uncSend.MessageType, uncSend.SequenceNumber);
|
||||
bool connectionReset = false;
|
||||
|
||||
if (uncSend.Recipient.Address.Equals(IPAddress.Broadcast))
|
||||
{
|
||||
// send using broadcast
|
||||
try
|
||||
{
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
SendPacket(ptr, uncSend.Recipient, 1, out connectionReset);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// send normally
|
||||
SendPacket(ptr, uncSend.Recipient, 1, out connectionReset);
|
||||
}
|
||||
|
||||
if (connectionReset)
|
||||
LogWarning(NetConstants.ConnResetMessage);
|
||||
// send unsent unconnected messages
|
||||
NetTuple<IPEndPoint, NetOutgoingMessage> unsent;
|
||||
while (m_unsentUnconnectedMessages.TryDequeue(out unsent))
|
||||
{
|
||||
NetOutgoingMessage om = unsent.Item2;
|
||||
|
||||
int unfin = uncSend.Message.m_numUnfinishedSendings;
|
||||
uncSend.Message.m_numUnfinishedSendings = unfin - 1;
|
||||
if (unfin <= 1)
|
||||
Recycle(uncSend.Message);
|
||||
bool connReset;
|
||||
int len = om.Encode(m_sendBuffer, 0, 0);
|
||||
SendPacket(len, unsent.Item1, 1, out connReset);
|
||||
|
||||
Interlocked.Decrement(ref om.m_recyclingCount);
|
||||
if (om.m_recyclingCount <= 0)
|
||||
Recycle(om);
|
||||
}
|
||||
|
||||
// check if we need to reduce the recycled pool
|
||||
ReduceStoragePool();
|
||||
|
||||
//
|
||||
// read from socket
|
||||
//
|
||||
do
|
||||
{
|
||||
if (m_socket == null)
|
||||
return;
|
||||
|
||||
if (!m_socket.Poll(1000, SelectMode.SelectRead)) // wait up to 1 ms for data to arrive
|
||||
if (!m_socket.Poll(500, SelectMode.SelectRead)) // wait up to 1/2 ms for data to arrive
|
||||
return;
|
||||
|
||||
//if (m_socket == null || m_socket.Available < 1)
|
||||
@@ -304,15 +267,14 @@ namespace Lidgren.Network
|
||||
//LogWarning("Connection reset by peer, seemingly from " + m_senderRemote);
|
||||
lock (m_connections)
|
||||
{
|
||||
if (m_connections.Count == 1)
|
||||
if (m_connections.Count + m_handshakes.Count == 1)
|
||||
{
|
||||
// only one connection; let's shut it down, unless already in progress
|
||||
m_connections[0].Disconnect("Connection forcibly closed");
|
||||
m_connections[0].ExecuteDisconnect(false);
|
||||
m_connections[0].FinishDisconnect();
|
||||
foreach (var kvp in m_handshakes)
|
||||
kvp.Value.ExecuteDisconnect("Connection forcibly closed", true);
|
||||
foreach (var conn in m_connections)
|
||||
conn.ExecuteDisconnect("Connection forcibly closed", true);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -320,12 +282,9 @@ namespace Lidgren.Network
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytesReceived < NetPeer.kMinPacketHeaderSize)
|
||||
if (bytesReceived < NetConstants.HeaderByteSize)
|
||||
return;
|
||||
|
||||
// renew current time; we might have waited in Poll
|
||||
now = NetTime.Now;
|
||||
|
||||
//LogVerbose("Received " + bytesReceived + " bytes");
|
||||
|
||||
IPEndPoint ipsender = (IPEndPoint)m_senderRemote;
|
||||
@@ -333,328 +292,201 @@ namespace Lidgren.Network
|
||||
NetConnection sender = null;
|
||||
m_connectionLookup.TryGetValue(ipsender, out sender);
|
||||
|
||||
int ptr = 0;
|
||||
NetMessageType msgType;
|
||||
NetMessageLibraryType libType = NetMessageLibraryType.Error;
|
||||
|
||||
//
|
||||
// parse packet into messages
|
||||
//
|
||||
int numMessagesReceived = 0;
|
||||
while ((bytesReceived - ptr) >= NetPeer.kMinPacketHeaderSize)
|
||||
int ptr = 0;
|
||||
while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize)
|
||||
{
|
||||
// get NetMessageType
|
||||
byte top = m_receiveBuffer[ptr++];
|
||||
bool isFragment = (top & 128) == 128;
|
||||
msgType = (NetMessageType)(top & 127);
|
||||
// decode header
|
||||
// 8 bits - NetMessageType
|
||||
// 1 bit - Fragment?
|
||||
// 15 bits - Sequence number
|
||||
// 16 bits - Payload length in bits
|
||||
|
||||
// get NetmessageLibraryType?
|
||||
if (msgType == NetMessageType.Library)
|
||||
libType = (NetMessageLibraryType)m_receiveBuffer[ptr++];
|
||||
NetMessageType tp = (NetMessageType)m_receiveBuffer[ptr++];
|
||||
|
||||
// get sequence number?
|
||||
ushort sequenceNumber;
|
||||
if (msgType >= NetMessageType.UserSequenced)
|
||||
sequenceNumber = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8));
|
||||
else
|
||||
sequenceNumber = 0;
|
||||
byte low = m_receiveBuffer[ptr++];
|
||||
byte high = m_receiveBuffer[ptr++];
|
||||
|
||||
// get payload length
|
||||
int payloadLengthBits = (int)m_receiveBuffer[ptr++];
|
||||
if ((payloadLengthBits & 128) == 128) // large payload
|
||||
payloadLengthBits = (payloadLengthBits & 127) | (m_receiveBuffer[ptr++] << 7);
|
||||
bool isFragment = ((low & 1) == 1);
|
||||
ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7));
|
||||
|
||||
int payloadLengthBytes = NetUtility.BytesToHoldBits(payloadLengthBits);
|
||||
ushort payloadBitLength = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8));
|
||||
int payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength);
|
||||
|
||||
if ((ptr + payloadLengthBytes) > bytesReceived)
|
||||
if (bytesReceived - ptr < payloadByteLength)
|
||||
{
|
||||
LogWarning("Malformed message from " + ipsender.ToString() + "; not enough bytes");
|
||||
break;
|
||||
LogWarning("Malformed packet; stated payload length " + payloadByteLength + ", remaining bytes " + (bytesReceived - ptr));
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// handle incoming message
|
||||
//
|
||||
|
||||
if (msgType == NetMessageType.Error)
|
||||
{
|
||||
LogError("Malformed message; no message type!");
|
||||
continue;
|
||||
}
|
||||
|
||||
numMessagesReceived++;
|
||||
|
||||
if (msgType == NetMessageType.Library)
|
||||
{
|
||||
if (sender == null)
|
||||
HandleUnconnectedLibraryMessage(libType, ptr, payloadLengthBits, ipsender);
|
||||
else
|
||||
sender.HandleLibraryMessage(now, libType, ptr, payloadLengthBits);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sender == null)
|
||||
{
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData))
|
||||
HandleUnconnectedUserMessage(ptr, payloadLengthBits, ipsender);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.Data))
|
||||
sender.HandleUserMessage(now, msgType, isFragment, sequenceNumber, ptr, payloadLengthBits);
|
||||
}
|
||||
}
|
||||
|
||||
if (isFragment)
|
||||
ptr += NetConstants.FragmentHeaderSize;
|
||||
|
||||
ptr += payloadLengthBytes;
|
||||
}
|
||||
|
||||
m_statistics.PacketReceived(bytesReceived, numMessagesReceived);
|
||||
|
||||
if (sender != null)
|
||||
{
|
||||
sender.m_lastHeardFrom = now;
|
||||
sender.m_statistics.PacketReceived(bytesReceived, numMessagesReceived);
|
||||
}
|
||||
|
||||
if (ptr < bytesReceived)
|
||||
{
|
||||
// malformed packet
|
||||
LogWarning("Malformed packet from " + sender + " (" + ipsender + "); " + (ptr - bytesReceived) + " stray bytes");
|
||||
continue;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
private void HandleUnconnectedLibraryMessage(NetMessageLibraryType libType, int ptr, int payloadLengthBits, IPEndPoint senderEndpoint)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
int payloadLengthBytes = NetUtility.BytesToHoldBits(payloadLengthBits);
|
||||
|
||||
switch (libType)
|
||||
{
|
||||
case NetMessageLibraryType.NatPunchMessage:
|
||||
HandleNatPunch(ptr, senderEndpoint);
|
||||
break;
|
||||
case NetMessageLibraryType.NatIntroduction:
|
||||
HandleNatIntroduction(ptr);
|
||||
break;
|
||||
case NetMessageLibraryType.Discovery:
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryRequest))
|
||||
{
|
||||
NetIncomingMessage dm = CreateIncomingMessage(NetIncomingMessageType.DiscoveryRequest, payloadLengthBytes);
|
||||
if (payloadLengthBytes > 0)
|
||||
Buffer.BlockCopy(m_receiveBuffer, ptr, dm.m_data, 0, payloadLengthBytes);
|
||||
dm.m_bitLength = payloadLengthBits;
|
||||
dm.m_senderEndpoint = senderEndpoint;
|
||||
ReleaseMessage(dm);
|
||||
}
|
||||
|
||||
break;
|
||||
case NetMessageLibraryType.DiscoveryResponse:
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryResponse))
|
||||
{
|
||||
NetIncomingMessage dr = CreateIncomingMessage(NetIncomingMessageType.DiscoveryResponse, payloadLengthBytes);
|
||||
if (payloadLengthBytes > 0)
|
||||
Buffer.BlockCopy(m_receiveBuffer, ptr, dr.m_data, 0, payloadLengthBytes);
|
||||
dr.m_bitLength = payloadLengthBits;
|
||||
dr.m_senderEndpoint = senderEndpoint;
|
||||
ReleaseMessage(dr);
|
||||
}
|
||||
break;
|
||||
|
||||
case NetMessageLibraryType.Connect:
|
||||
|
||||
if (!m_configuration.m_acceptIncomingConnections)
|
||||
{
|
||||
LogWarning("Connect received; but we're not accepting incoming connections!");
|
||||
break;
|
||||
}
|
||||
|
||||
string appIdent;
|
||||
long remoteUniqueIdentifier = 0;
|
||||
NetIncomingMessage approval = null;
|
||||
try
|
||||
{
|
||||
NetIncomingMessage reader = new NetIncomingMessage();
|
||||
NetException.Assert(tp < NetMessageType.Unused1 || tp > NetMessageType.Unused29);
|
||||
|
||||
reader.m_data = GetStorage(payloadLengthBytes);
|
||||
Buffer.BlockCopy(m_receiveBuffer, ptr, reader.m_data, 0, payloadLengthBytes);
|
||||
ptr += payloadLengthBytes;
|
||||
reader.m_bitLength = payloadLengthBits;
|
||||
appIdent = reader.ReadString();
|
||||
remoteUniqueIdentifier = reader.ReadInt64();
|
||||
if (tp >= NetMessageType.LibraryError)
|
||||
{
|
||||
if (sender != null)
|
||||
sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength);
|
||||
else
|
||||
ReceivedUnconnectedLibraryMessage(ipsender, tp, ptr, payloadByteLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sender == null && !m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData))
|
||||
return; // dropping unconnected message since it's not enabled
|
||||
|
||||
int approvalBitLength = (int)reader.ReadVariableUInt32();
|
||||
if (approvalBitLength > 0)
|
||||
NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength);
|
||||
msg.m_isFragment = isFragment;
|
||||
msg.m_sequenceNumber = sequenceNumber;
|
||||
msg.m_receivedMessageType = tp;
|
||||
msg.m_senderConnection = sender;
|
||||
msg.m_senderEndpoint = ipsender;
|
||||
msg.m_bitLength = payloadBitLength;
|
||||
Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength);
|
||||
if (sender != null)
|
||||
{
|
||||
int approvalByteLength = NetUtility.BytesToHoldBits(approvalBitLength);
|
||||
if (approvalByteLength < m_configuration.MaximumTransmissionUnit)
|
||||
if (tp == NetMessageType.Unconnected)
|
||||
{
|
||||
approval = CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, approvalByteLength);
|
||||
reader.ReadBits(approval.m_data, 0, approvalBitLength);
|
||||
approval.m_bitLength = approvalBitLength;
|
||||
// We're connected; but we can still send unconnected messages to this peer
|
||||
msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
|
||||
ReleaseMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// connected application (non-library) message
|
||||
sender.ReceivedMessage(msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// at this point we know the message type is enabled
|
||||
// unconnected application (non-library) message
|
||||
msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData;
|
||||
ReleaseMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// malformed connect packet
|
||||
LogWarning("Malformed connect packet from " + senderEndpoint + " - " + ex.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
if (appIdent.Equals(m_configuration.AppIdentifier, StringComparison.InvariantCulture) == false)
|
||||
{
|
||||
// wrong app ident
|
||||
LogWarning("Connect received with wrong appidentifier (need '" + m_configuration.AppIdentifier + "' found '" + appIdent + "') from " + senderEndpoint);
|
||||
|
||||
NetOutgoingMessage bye = CreateLibraryMessage(NetMessageLibraryType.Disconnect, "Wrong app identifier!");
|
||||
SendUnconnectedLibrary(bye, senderEndpoint);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// ok, someone wants to connect to us, and we're accepting connections!
|
||||
int reservedSlots = m_connections.Count;
|
||||
if (m_pendingConnections != null)
|
||||
reservedSlots += m_pendingConnections.Count;
|
||||
if (reservedSlots >= m_configuration.MaximumConnections)
|
||||
{
|
||||
HandleServerFull(senderEndpoint);
|
||||
break;
|
||||
}
|
||||
|
||||
bool isAlreadyPending = false;
|
||||
if (m_pendingConnections != null)
|
||||
{
|
||||
// check so we don't already have a pending connection to this endpoint
|
||||
foreach (NetConnection conn in m_pendingConnections)
|
||||
{
|
||||
if (conn.RemoteEndpoint.Equals(senderEndpoint))
|
||||
{
|
||||
// Yes, we do.
|
||||
isAlreadyPending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAlreadyPending)
|
||||
{
|
||||
NetConnection conn = new NetConnection(this, senderEndpoint);
|
||||
conn.m_connectionInitiator = false;
|
||||
conn.m_connectInitationTime = NetTime.Now;
|
||||
conn.m_remoteUniqueIdentifier = remoteUniqueIdentifier;
|
||||
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
|
||||
{
|
||||
// do connection approval before accepting this connection
|
||||
AddPendingConnection(conn, approval);
|
||||
break;
|
||||
}
|
||||
|
||||
AcceptConnection(conn);
|
||||
}
|
||||
LogError("Packet parsing error: " + ex.Message);
|
||||
}
|
||||
ptr += payloadByteLength;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReceivedUnconnectedLibraryMessage(IPEndPoint senderEndpoint, NetMessageType tp, int ptr, int payloadByteLength)
|
||||
{
|
||||
NetConnection shake;
|
||||
if (m_handshakes.TryGetValue(senderEndpoint, out shake))
|
||||
{
|
||||
shake.ReceivedHandshake(tp, ptr, payloadByteLength);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Library message from a completely unknown sender; lets just accept Connect
|
||||
//
|
||||
switch (tp)
|
||||
{
|
||||
case NetMessageType.Discovery:
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryRequest))
|
||||
{
|
||||
NetIncomingMessage dm = CreateIncomingMessage(NetIncomingMessageType.DiscoveryRequest, payloadByteLength);
|
||||
if (payloadByteLength > 0)
|
||||
Buffer.BlockCopy(m_receiveBuffer, ptr, dm.m_data, 0, payloadByteLength);
|
||||
dm.m_bitLength = payloadByteLength * 8;
|
||||
dm.m_senderEndpoint = senderEndpoint;
|
||||
ReleaseMessage(dm);
|
||||
}
|
||||
return;
|
||||
|
||||
case NetMessageType.DiscoveryResponse:
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DiscoveryResponse))
|
||||
{
|
||||
NetIncomingMessage dr = CreateIncomingMessage(NetIncomingMessageType.DiscoveryResponse, payloadByteLength);
|
||||
if (payloadByteLength > 0)
|
||||
Buffer.BlockCopy(m_receiveBuffer, ptr, dr.m_data, 0, payloadByteLength);
|
||||
dr.m_bitLength = payloadByteLength * 8;
|
||||
dr.m_senderEndpoint = senderEndpoint;
|
||||
ReleaseMessage(dr);
|
||||
}
|
||||
return;
|
||||
case NetMessageType.NatIntroduction:
|
||||
HandleNatIntroduction(ptr);
|
||||
return;
|
||||
case NetMessageType.NatPunchMessage:
|
||||
HandleNatPunch(ptr, senderEndpoint);
|
||||
return;
|
||||
case NetMessageType.Connect:
|
||||
// proceed
|
||||
break;
|
||||
case NetMessageType.Disconnect:
|
||||
// this is probably ok
|
||||
LogVerbose("Received Disconnect from unconnected source: " + senderEndpoint);
|
||||
return;
|
||||
default:
|
||||
LogWarning("Received unconnected library message of type " + libType);
|
||||
break;
|
||||
}
|
||||
LogWarning("Received unhandled library message " + tp + " from " + senderEndpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
private void HandleUnconnectedUserMessage(int ptr, int payloadLengthBits, IPEndPoint senderEndpoint)
|
||||
// It's someone wanting to shake hands with us!
|
||||
|
||||
int reservedSlots = m_handshakes.Count + m_connections.Count;
|
||||
if (reservedSlots >= m_configuration.m_maximumConnections)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
NetIncomingMessage ium = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, m_receiveBuffer, ptr, NetUtility.BytesToHoldBits(payloadLengthBits));
|
||||
ium.m_bitLength = payloadLengthBits;
|
||||
ium.m_senderEndpoint = senderEndpoint;
|
||||
ReleaseMessage(ium);
|
||||
// server full
|
||||
NetOutgoingMessage full = CreateMessage("Server full");
|
||||
full.m_messageType = NetMessageType.Disconnect;
|
||||
SendLibrary(full, senderEndpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
private void AcceptConnection(NetConnection conn)
|
||||
{
|
||||
lock (m_connections)
|
||||
{
|
||||
m_connections.Add(conn);
|
||||
m_connectionLookup[conn.m_remoteEndpoint] = conn;
|
||||
}
|
||||
conn.SetStatus(NetConnectionStatus.Connecting, "Connecting");
|
||||
|
||||
// send connection response
|
||||
conn.SendConnectResponse();
|
||||
|
||||
conn.m_connectInitationTime = NetTime.Now;
|
||||
// Ok, start handshake!
|
||||
NetConnection conn = new NetConnection(this, senderEndpoint);
|
||||
m_handshakes.Add(senderEndpoint, conn);
|
||||
conn.ReceivedHandshake(tp, ptr, payloadByteLength);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
internal void RemoveConnection(NetConnection conn)
|
||||
internal void AcceptConnection(NetConnection conn)
|
||||
{
|
||||
// LogDebug("Accepted connection " + conn);
|
||||
|
||||
if (m_handshakes.Remove(conn.m_remoteEndpoint) == false)
|
||||
LogWarning("AcceptConnection called but m_handshakes did not contain it!");
|
||||
|
||||
lock (m_connections)
|
||||
{
|
||||
m_connections.Remove(conn);
|
||||
m_connectionLookup.Remove(conn.m_remoteEndpoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleServerFull(IPEndPoint connecter)
|
||||
if (m_connections.Contains(conn))
|
||||
{
|
||||
const string rejectMessage = "Server is full!"; // TODO: put in configuration
|
||||
NetOutgoingMessage reply = CreateLibraryMessage(NetMessageLibraryType.Disconnect, rejectMessage);
|
||||
SendLibraryImmediately(reply, connecter);
|
||||
LogWarning("AcceptConnection called but m_connection already contains it!");
|
||||
}
|
||||
|
||||
// called by user and network thread
|
||||
private void EnqueueUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
else
|
||||
{
|
||||
NetSending send = new NetSending(msg, NetMessageType.UserUnreliable, 0);
|
||||
send.Recipient = recipient;
|
||||
|
||||
msg.m_numUnfinishedSendings++;
|
||||
m_unsentUnconnectedMessage.Enqueue(send);
|
||||
m_connections.Add(conn);
|
||||
m_connectionLookup.Add(conn.m_remoteEndpoint, conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// called by user and network thread
|
||||
private void SendUnconnectedLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
[Conditional("DEBUG")]
|
||||
internal void VerifyNetworkThread()
|
||||
{
|
||||
msg.m_wasSent = true;
|
||||
NetSending send = new NetSending(msg, NetMessageType.Library, 0);
|
||||
send.Recipient = recipient;
|
||||
|
||||
msg.m_numUnfinishedSendings++;
|
||||
m_unsentUnconnectedMessage.Enqueue(send);
|
||||
Thread ct = Thread.CurrentThread;
|
||||
if (Thread.CurrentThread != m_networkThread)
|
||||
throw new NetException("Executing on wrong thread! Should be library system thread (is " + ct.Name + " mId " + ct.ManagedThreadId + ")");
|
||||
}
|
||||
|
||||
internal static NetDeliveryMethod GetDeliveryMethod(NetMessageType mtp)
|
||||
internal NetIncomingMessage SetupReadHelperMessage(int ptr, int payloadLength)
|
||||
{
|
||||
if (mtp >= NetMessageType.UserReliableOrdered)
|
||||
return NetDeliveryMethod.ReliableOrdered;
|
||||
else if (mtp >= NetMessageType.UserReliableSequenced)
|
||||
return NetDeliveryMethod.ReliableSequenced;
|
||||
else if (mtp >= NetMessageType.UserReliableUnordered)
|
||||
return NetDeliveryMethod.ReliableUnordered;
|
||||
else if (mtp >= NetMessageType.UserSequenced)
|
||||
return NetDeliveryMethod.UnreliableSequenced;
|
||||
return NetDeliveryMethod.Unreliable;
|
||||
VerifyNetworkThread();
|
||||
|
||||
m_readHelperMessage.m_bitLength = (ptr + payloadLength) * 8;
|
||||
m_readHelperMessage.m_readPosition = (ptr * 8);
|
||||
return m_readHelperMessage;
|
||||
}
|
||||
|
||||
internal void SendLibraryImmediately(NetOutgoingMessage msg, IPEndPoint destination)
|
||||
{
|
||||
msg.m_wasSent = true;
|
||||
int len = msg.EncodeUnfragmented(m_sendBuffer, 0, NetMessageType.Library, 0);
|
||||
|
||||
bool connectionReset;
|
||||
SendPacket(len, destination, 1, out connectionReset);
|
||||
|
||||
// TODO: handle connectionReset
|
||||
|
||||
Recycle(msg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
@@ -45,9 +46,9 @@ namespace Lidgren.Network
|
||||
float loss = m_configuration.m_loss;
|
||||
if (loss > 0.0f)
|
||||
{
|
||||
if (NetRandom.Instance.Chance(m_configuration.m_loss))
|
||||
if ((float)NetRandom.Instance.NextDouble() < loss)
|
||||
{
|
||||
//LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
|
||||
LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
|
||||
return; // packet "lost"
|
||||
}
|
||||
}
|
||||
@@ -60,19 +61,20 @@ namespace Lidgren.Network
|
||||
if (m == 0.0f && r == 0.0f)
|
||||
{
|
||||
// no latency simulation
|
||||
//LogVerbose("Sending packet " + numBytes + " bytes");
|
||||
ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
|
||||
// LogVerbose("Sending packet " + numBytes + " bytes");
|
||||
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
|
||||
// TODO: handle wasSent == false?
|
||||
return;
|
||||
}
|
||||
|
||||
int num = 1;
|
||||
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.Chance(m_configuration.m_duplicates))
|
||||
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
|
||||
num++;
|
||||
|
||||
float delay = 0;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextFloat() * m_configuration.m_randomOneWayLatency);
|
||||
delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextSingle() * m_configuration.m_randomOneWayLatency);
|
||||
|
||||
// Enqueue delayed packet
|
||||
DelayedPacket p = new DelayedPacket();
|
||||
@@ -108,7 +110,7 @@ namespace Lidgren.Network
|
||||
}
|
||||
}
|
||||
|
||||
internal void ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target, out bool connectionReset)
|
||||
internal bool ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target, out bool connectionReset)
|
||||
{
|
||||
connectionReset = false;
|
||||
try
|
||||
@@ -122,6 +124,56 @@ namespace Lidgren.Network
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return false;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
|
||||
connectionReset = true;
|
||||
return false;
|
||||
}
|
||||
LogError("Failed to send packet: " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
//
|
||||
// Release - just send the packet straight away
|
||||
//
|
||||
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
|
||||
{
|
||||
connectionReset = false;
|
||||
try
|
||||
{
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
|
||||
@@ -139,35 +191,13 @@ namespace Lidgren.Network
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
//
|
||||
// Release - just send the packet straight away
|
||||
//
|
||||
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
|
||||
{
|
||||
connectionReset = false;
|
||||
try
|
||||
{
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
|
||||
connectionReset = true;
|
||||
return;
|
||||
}
|
||||
LogError("Failed to send packet: " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
private void SendCallBack(IAsyncResult res)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
NetException.Assert(res.IsCompleted == true);
|
||||
m_socket.EndSendTo(res);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
194
Lidgren.Network/NetPeer.MessagePools.cs
Normal file
194
Lidgren.Network/NetPeer.MessagePools.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
private List<byte[]> m_storagePool; // sorted smallest to largest
|
||||
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool;
|
||||
private NetQueue<NetIncomingMessage> m_incomingMessagesPool;
|
||||
|
||||
internal int m_storagePoolBytes;
|
||||
|
||||
private void InitializePools()
|
||||
{
|
||||
if (m_configuration.UseMessageRecycling)
|
||||
{
|
||||
m_storagePool = new List<byte[]>(16);
|
||||
m_outgoingMessagesPool = new NetQueue<NetOutgoingMessage>(4);
|
||||
m_incomingMessagesPool = new NetQueue<NetIncomingMessage>(4);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_storagePool = null;
|
||||
m_outgoingMessagesPool = null;
|
||||
m_incomingMessagesPool = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] GetStorage(int minimumCapacity)
|
||||
{
|
||||
if (m_storagePool == null)
|
||||
return new byte[minimumCapacity];
|
||||
|
||||
lock (m_storagePool)
|
||||
{
|
||||
for (int i = 0; i < m_storagePool.Count; i++)
|
||||
{
|
||||
byte[] retval = m_storagePool[i];
|
||||
if (retval != null && retval.Length >= minimumCapacity)
|
||||
{
|
||||
m_storagePool[i] = null;
|
||||
m_storagePoolBytes -= retval.Length;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_statistics.m_bytesAllocated += minimumCapacity;
|
||||
return new byte[minimumCapacity];
|
||||
}
|
||||
|
||||
internal void Recycle(byte[] storage)
|
||||
{
|
||||
if (m_storagePool == null)
|
||||
return;
|
||||
|
||||
int len = storage.Length;
|
||||
lock (m_storagePool)
|
||||
{
|
||||
for (int i = 0; i < m_storagePool.Count; i++)
|
||||
{
|
||||
if (m_storagePool[i] == null)
|
||||
{
|
||||
m_storagePoolBytes += storage.Length;
|
||||
m_storagePool[i] = storage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_storagePoolBytes += storage.Length;
|
||||
m_storagePool.Add(storage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">initial capacity in bytes</param>
|
||||
public NetOutgoingMessage CreateMessage()
|
||||
{
|
||||
return CreateMessage(m_configuration.m_defaultOutgoingMessageCapacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending and writes the provided string to it
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">initial capacity in bytes</param>
|
||||
public NetOutgoingMessage CreateMessage(string content)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
||||
NetOutgoingMessage om = CreateMessage(2 + bytes.Length);
|
||||
om.WriteVariableUInt32((uint)bytes.Length);
|
||||
om.Write(bytes);
|
||||
return om;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">initial capacity in bytes</param>
|
||||
public NetOutgoingMessage CreateMessage(int initialCapacity)
|
||||
{
|
||||
NetOutgoingMessage retval;
|
||||
if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetOutgoingMessage();
|
||||
|
||||
byte[] storage = GetStorage(initialCapacity);
|
||||
retval.m_data = storage;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetIncomingMessage(tp);
|
||||
else
|
||||
retval.m_incomingMessageType = tp;
|
||||
retval.m_data = useStorageData;
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int minimumByteSize)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetIncomingMessage(tp);
|
||||
else
|
||||
retval.m_incomingMessageType = tp;
|
||||
retval.m_data = GetStorage(minimumByteSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles a NetIncomingMessage instance for reuse; taking pressure off the garbage collector
|
||||
/// </summary>
|
||||
public void Recycle(NetIncomingMessage msg)
|
||||
{
|
||||
if (m_incomingMessagesPool == null)
|
||||
return;
|
||||
#if DEBUG
|
||||
if (m_incomingMessagesPool.Contains(msg))
|
||||
throw new NetException("Recyling already recycled message! Thread race?");
|
||||
#endif
|
||||
byte[] storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
Recycle(storage);
|
||||
msg.Reset();
|
||||
m_incomingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
internal void Recycle(NetOutgoingMessage msg)
|
||||
{
|
||||
if (m_outgoingMessagesPool == null)
|
||||
return;
|
||||
#if DEBUG
|
||||
if (m_outgoingMessagesPool.Contains(msg))
|
||||
throw new NetException("Recyling already recycled message! Thread race?");
|
||||
#endif
|
||||
|
||||
byte[] storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
|
||||
// message fragments cannot be recycled
|
||||
// TODO: find a way to recycle large message after all fragments has been acknowledged
|
||||
if (msg.m_fragmentGroup == 0)
|
||||
Recycle(storage);
|
||||
|
||||
msg.Reset();
|
||||
m_outgoingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an incoming message with the required capacity for releasing to the application
|
||||
/// </summary>
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, string text)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
retval = CreateIncomingMessage(tp, 1);
|
||||
retval.Write("");
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(text);
|
||||
retval = CreateIncomingMessage(tp, bytes.Length + (bytes.Length > 127 ? 2 : 1));
|
||||
retval.Write(text);
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
154
Lidgren.Network/NetPeer.Send.cs
Normal file
154
Lidgren.Network/NetPeer.Send.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Send a message to a specific connection
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipient">The recipient connection</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method)
|
||||
{
|
||||
return SendMessage(msg, recipient, method, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a specific connection
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipient">The recipient connection</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
|
||||
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
|
||||
int len = msg.LengthBytes;
|
||||
if (len <= m_configuration.MaximumTransmissionUnit)
|
||||
{
|
||||
Interlocked.Increment(ref msg.m_recyclingCount);
|
||||
return recipient.EnqueueMessage(msg, method, sequenceChannel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// message must be fragmented!
|
||||
SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
|
||||
return NetSendResult.Queued; // could be different for each connection; Queued is "most true"
|
||||
}
|
||||
}
|
||||
|
||||
public void SendMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
|
||||
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
|
||||
int len = msg.LengthBytes;
|
||||
if (len <= m_configuration.MaximumTransmissionUnit)
|
||||
{
|
||||
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
|
||||
foreach (NetConnection conn in recipients)
|
||||
{
|
||||
if (conn == null)
|
||||
{
|
||||
Interlocked.Decrement(ref msg.m_recyclingCount);
|
||||
continue;
|
||||
}
|
||||
NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
|
||||
if (res == NetSendResult.Dropped)
|
||||
Interlocked.Decrement(ref msg.m_recyclingCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// message must be fragmented!
|
||||
SendFragmentedMessage(msg, recipients, method, sequenceChannel);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
IPAddress adr = NetUtility.Resolve(host);
|
||||
if (adr == null)
|
||||
throw new NetException("Failed to resolve " + host);
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
|
||||
// TODO: Interlocked.Add(ref msg.m_recyclingCount, recipients.Count); ?
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(adr, port), msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
|
||||
// TODO: Interlocked.Add(ref msg.m_recyclingCount, recipients.Count); ?
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> recipients)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
|
||||
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
|
||||
foreach(IPEndPoint ep in recipients)
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(ep, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +1,56 @@
|
||||
/* 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;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
//
|
||||
// This partial file holds public netpeer methods accessible to the application
|
||||
//
|
||||
[DebuggerDisplay("Status={m_status}")]
|
||||
/// <summary>
|
||||
/// Represents a local peer capable of holding zero, one or more connections to remote peers
|
||||
/// </summary>
|
||||
public partial class NetPeer
|
||||
{
|
||||
private static int s_peerCount = 0;
|
||||
private static int s_initializedPeersCount;
|
||||
|
||||
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;
|
||||
private string m_networkThreadName;
|
||||
private int m_listenPort;
|
||||
|
||||
internal readonly List<NetConnection> m_connections;
|
||||
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
|
||||
|
||||
private string m_shutdownReason;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the NetPeer
|
||||
/// </summary>
|
||||
public NetPeerStatus Status { get { return m_status; } }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the network thread for this NetPeer
|
||||
/// Signalling event which can be waited on to determine when a message is queued for reading.
|
||||
/// Note that there is no guarantee that after the event is signaled the blocked thread will
|
||||
/// find the message in the queue. Other user created threads could be preempted and dequeue
|
||||
/// the message before the waiting thread wakes up.
|
||||
/// </summary>
|
||||
public string NetworkThreadName
|
||||
{
|
||||
get { return m_networkThreadName; }
|
||||
set
|
||||
{
|
||||
if (m_networkThreadName != value)
|
||||
{
|
||||
m_networkThreadName = value;
|
||||
if (m_networkThread != null)
|
||||
m_networkThread.Name = m_networkThreadName;
|
||||
}
|
||||
}
|
||||
}
|
||||
public AutoResetEvent MessageReceivedEvent { get { return m_messageReceivedEvent; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique identifier for this NetPeer based on Mac address and ip/port. Note! Not available until Start has been called!
|
||||
/// </summary>
|
||||
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number this NetPeer is listening and sending on
|
||||
/// </summary>
|
||||
public int Port { get { return m_listenPort; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the list of connections
|
||||
/// </summary>
|
||||
public NetConnection[] Connections
|
||||
public List<NetConnection> Connections
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (m_connections)
|
||||
return m_connections.ToArray();
|
||||
return new List<NetConnection>(m_connections);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,48 +75,22 @@ namespace Lidgren.Network
|
||||
/// </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)
|
||||
public NetPeer(NetPeerConfiguration config)
|
||||
{
|
||||
if (configuration == null)
|
||||
throw new ArgumentNullException("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_configuration = config;
|
||||
m_statistics = new NetPeerStatistics(this);
|
||||
|
||||
int pc = Interlocked.Increment(ref s_peerCount);
|
||||
m_networkThreadName = "Lidgren network thread " + pc.ToString();
|
||||
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4);
|
||||
m_unsentUnconnectedMessages = new NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>>(2);
|
||||
m_connections = new List<NetConnection>();
|
||||
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
m_receivedFragmentGroups = new Dictionary<int, ReceivedFragmentGroup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the connection to a remote endpoint; if it exists
|
||||
/// </summary>
|
||||
public NetConnection GetConnection(IPEndPoint remoteEndPoint)
|
||||
{
|
||||
if (remoteEndPoint == null)
|
||||
throw new ArgumentNullException("remoteEndPoint");
|
||||
|
||||
NetConnection retval;
|
||||
if (m_connectionLookup.TryGetValue(remoteEndPoint, out retval))
|
||||
return retval;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds to socket
|
||||
/// Binds to socket and spawns networking thread
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
@@ -158,16 +103,18 @@ namespace Lidgren.Network
|
||||
|
||||
m_status = NetPeerStatus.Starting;
|
||||
|
||||
m_releasedIncomingMessages.Clear();
|
||||
m_unsentUnconnectedMessage.Clear();
|
||||
|
||||
m_configuration.VerifyAndLock();
|
||||
// fix network thread name
|
||||
if (m_configuration.NetworkThreadName == "Lidgren network thread")
|
||||
{
|
||||
int pc = Interlocked.Increment(ref s_initializedPeersCount);
|
||||
m_configuration.NetworkThreadName = "Lidgren network thread " + pc.ToString();
|
||||
}
|
||||
|
||||
InitializeNetwork();
|
||||
|
||||
// start network thread
|
||||
m_networkThread = new Thread(new ThreadStart(NetworkLoop));
|
||||
m_networkThread.Name = m_networkThreadName;
|
||||
m_networkThread.Name = m_configuration.NetworkThreadName;
|
||||
m_networkThread.IsBackground = true;
|
||||
m_networkThread.Start();
|
||||
|
||||
@@ -175,17 +122,11 @@ namespace Lidgren.Network
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a queued message available to read using ReadMessage()
|
||||
/// </summary>
|
||||
public bool MessageAvailable
|
||||
internal NetConnection GetConnection(IPEndPoint ep)
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning)
|
||||
return false;
|
||||
return (m_releasedIncomingMessages.Count > 0);
|
||||
}
|
||||
NetConnection retval;
|
||||
m_connectionLookup.TryGetValue(ep, out retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -193,9 +134,6 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
public NetIncomingMessage ReadMessage()
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning)
|
||||
return null;
|
||||
|
||||
NetIncomingMessage retval;
|
||||
if (m_releasedIncomingMessages.TryDequeue(out retval))
|
||||
{
|
||||
@@ -208,13 +146,15 @@ namespace Lidgren.Network
|
||||
return retval;
|
||||
}
|
||||
|
||||
public NetIncomingMessage WaitMessage(int maxMillis)
|
||||
// send message immediately
|
||||
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (m_messageReceivedEvent != null)
|
||||
m_messageReceivedEvent.WaitOne(maxMillis);
|
||||
NetIncomingMessage retval;
|
||||
m_releasedIncomingMessages.TryDequeue(out retval);
|
||||
return retval;
|
||||
VerifyNetworkThread();
|
||||
NetException.Assert(msg.m_isSent == false);
|
||||
|
||||
bool connReset;
|
||||
int len = msg.Encode(m_sendBuffer, 0, 0);
|
||||
SendPacket(len, recipient, 1, out connReset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,6 +165,14 @@ namespace Lidgren.Network
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
@@ -236,34 +184,7 @@ namespace Lidgren.Network
|
||||
/// <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>
|
||||
/// Tries to create a connection to a remote endpoint; returns true on success
|
||||
/// Note that the connection attempt may still fail; the returning value only indicates that the connection procedure initiated successfully
|
||||
/// </summary>
|
||||
public bool TryConnect(IPEndPoint remoteEndpoint, NetOutgoingMessage approvalMessage, out NetConnection connection)
|
||||
{
|
||||
lock (m_connections)
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning || m_connectionLookup.ContainsKey(remoteEndpoint))
|
||||
{
|
||||
connection = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
connection = Connect(remoteEndpoint, approvalMessage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public virtual NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage approvalMessage)
|
||||
public virtual NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
if (remoteEndpoint == null)
|
||||
throw new ArgumentNullException("remoteEndpoint");
|
||||
@@ -276,158 +197,49 @@ namespace Lidgren.Network
|
||||
if (m_connectionLookup.ContainsKey(remoteEndpoint))
|
||||
throw new NetException("Already connected to that endpoint!");
|
||||
|
||||
NetConnection hs;
|
||||
if (m_handshakes.TryGetValue(remoteEndpoint, out hs))
|
||||
{
|
||||
// already trying to connect to that endpoint; make another try
|
||||
switch (hs.Status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// send another connect
|
||||
hs.m_connectRequested = true;
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// send another response
|
||||
hs.SendConnectResponse(false);
|
||||
break;
|
||||
default:
|
||||
// weird
|
||||
LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.Status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NetConnection conn = new NetConnection(this, remoteEndpoint);
|
||||
conn.m_approvalMessage = approvalMessage;
|
||||
conn.m_localHailMessage = hailMessage;
|
||||
|
||||
// handle on network thread
|
||||
conn.m_connectRequested = true;
|
||||
conn.m_connectionInitiator = true;
|
||||
|
||||
m_connections.Add(conn);
|
||||
m_connectionLookup[remoteEndpoint] = conn;
|
||||
m_handshakes.Add(remoteEndpoint, conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an existing connection
|
||||
/// </summary>
|
||||
public bool SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod deliveryMethod)
|
||||
#if DEBUG
|
||||
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
|
||||
{
|
||||
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 == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent! To send to multiple recipients use SendMessage(... IEnumerable<NetConnection...)");
|
||||
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 == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
|
||||
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 == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
|
||||
IPAddress adr = NetUtility.Resolve(host);
|
||||
if (adr == null)
|
||||
throw new NetException("Failed to resolve " + host);
|
||||
|
||||
SendUnconnectedMessage(msg, new IPEndPoint(adr, port));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_wasSent = true;
|
||||
|
||||
EnqueueUnconnectedMessage(msg, recipient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a number of unconnected hosts
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IEnumerable<IPEndPoint> recipients)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_wasSent = true;
|
||||
|
||||
foreach (IPEndPoint rec in recipients)
|
||||
EnqueueUnconnectedMessage(msg, rec);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a discovery response message
|
||||
/// </summary>
|
||||
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg == null)
|
||||
msg = CreateMessage(0);
|
||||
else if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
|
||||
msg.m_libType = NetMessageLibraryType.DiscoveryResponse;
|
||||
SendUnconnectedLibrary(msg, recipient);
|
||||
// wrong thread - this miiiight crash with network thread... but what's a boy to do.
|
||||
Array.Copy(arr, offset, m_sendBuffer, 0, length);
|
||||
bool unused;
|
||||
SendPacket(length, destination, 1, out unused);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects all active connections and closes the socket
|
||||
@@ -435,7 +247,6 @@ namespace Lidgren.Network
|
||||
public void Shutdown(string bye)
|
||||
{
|
||||
// called on user thread
|
||||
|
||||
if (m_socket == null)
|
||||
return; // already shut down
|
||||
|
||||
@@ -443,12 +254,5 @@ namespace Lidgren.Network
|
||||
m_shutdownReason = bye;
|
||||
m_status = NetPeerStatus.ShutdownRequested;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (m_socket == null)
|
||||
return "[NetPeer unbound]";
|
||||
return "[NetPeer bound to " + m_socket.LocalEndPoint + " " + ConnectionsCount + " connections]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,28 +30,21 @@ namespace Lidgren.Network
|
||||
private const string c_isLockedMessage = "You may not modify the NetPeerConfiguration after it has been used to initialize a NetPeer";
|
||||
|
||||
private bool m_isLocked;
|
||||
private readonly string m_appIdentifier;
|
||||
private string m_networkThreadName;
|
||||
private IPAddress m_localAddress;
|
||||
internal bool m_acceptIncomingConnections;
|
||||
internal string m_appIdentifier;
|
||||
internal IPAddress m_localAddress;
|
||||
internal int m_port;
|
||||
internal int m_receiveBufferSize, m_sendBufferSize;
|
||||
internal int m_defaultOutgoingMessageCapacity;
|
||||
internal int m_maximumTransmissionUnit;
|
||||
internal bool m_useMessageCoalescing;
|
||||
internal int m_maximumConnections;
|
||||
internal NetIncomingMessageType m_disabledTypes;
|
||||
internal int m_throttleBytesPerSecond;
|
||||
internal int m_throttlePeakBytes;
|
||||
internal int m_maxRecycledBytesKept;
|
||||
|
||||
// handshake, timeout and keepalive
|
||||
internal float m_handshakeAttemptDelay;
|
||||
internal int m_handshakeMaxAttempts;
|
||||
internal int m_maximumTransmissionUnit;
|
||||
internal int m_defaultOutgoingMessageCapacity;
|
||||
internal float m_pingInterval;
|
||||
internal bool m_useMessageRecycling;
|
||||
internal float m_connectionTimeout;
|
||||
internal float m_pingFrequency;
|
||||
|
||||
// reliability
|
||||
internal float m_maxAckDelayTime;
|
||||
internal NetIncomingMessageType m_disabledTypes;
|
||||
internal int m_port;
|
||||
internal int m_receiveBufferSize;
|
||||
internal int m_sendBufferSize;
|
||||
|
||||
// bad network simulation
|
||||
internal float m_loss;
|
||||
@@ -65,108 +58,47 @@ namespace Lidgren.Network
|
||||
throw new NetException("App identifier must be at least one character long");
|
||||
m_appIdentifier = appIdentifier.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
// defaults
|
||||
m_isLocked = false;
|
||||
m_acceptIncomingConnections = true;
|
||||
//
|
||||
// default values
|
||||
//
|
||||
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage;
|
||||
m_networkThreadName = "Lidgren network thread";
|
||||
m_localAddress = IPAddress.Any;
|
||||
m_port = 0;
|
||||
m_receiveBufferSize = 131071;
|
||||
m_sendBufferSize = 131071;
|
||||
m_connectionTimeout = 25;
|
||||
m_maximumConnections = 16;
|
||||
m_defaultOutgoingMessageCapacity = 8;
|
||||
m_pingFrequency = 6.0f;
|
||||
m_throttleBytesPerSecond = 1024 * 256;
|
||||
m_throttlePeakBytes = 8192;
|
||||
m_maxAckDelayTime = 0.01f;
|
||||
m_handshakeAttemptDelay = 1.0f;
|
||||
m_handshakeMaxAttempts = 7;
|
||||
m_maxRecycledBytesKept = 128 * 1024;
|
||||
m_useMessageCoalescing = true;
|
||||
m_acceptIncomingConnections = false;
|
||||
m_maximumConnections = 32;
|
||||
m_defaultOutgoingMessageCapacity = 16;
|
||||
m_pingInterval = 3.0f;
|
||||
m_connectionTimeout = 25.0f;
|
||||
m_useMessageRecycling = true;
|
||||
|
||||
// Maximum transmission unit
|
||||
// Ethernet can take 1500 bytes of payload, so lets stay below that.
|
||||
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
|
||||
// -20 bytes IP header
|
||||
// -8 bytes UDP header
|
||||
// -4 bytes to be on the safe side and align to 8-byte boundary
|
||||
// Total 1408 bytes
|
||||
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
|
||||
m_maximumTransmissionUnit = 1408;
|
||||
|
||||
m_loss = 0.0f;
|
||||
m_minimumOneWayLatency = 0.0f;
|
||||
m_randomOneWayLatency = 0.0f;
|
||||
m_duplicates = 0.0f;
|
||||
|
||||
// default disabled types
|
||||
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage;
|
||||
|
||||
// Maximum transmission unit
|
||||
// Ethernet can take 1500 bytes of payload, so lets stay below that.
|
||||
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
|
||||
// 20 bytes IP header
|
||||
// 8 bytes UDP header
|
||||
// 5 bytes lidgren header for one message
|
||||
// 1 byte just to be on the safe side
|
||||
// Totals 1440 minus 34 = 1406 bytes free for payload
|
||||
m_maximumTransmissionUnit = 1406;
|
||||
m_isLocked = false;
|
||||
}
|
||||
|
||||
public NetPeerConfiguration Clone()
|
||||
internal void Lock()
|
||||
{
|
||||
NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration;
|
||||
retval.m_isLocked = false;
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal void VerifyAndLock()
|
||||
{
|
||||
if (m_throttleBytesPerSecond != 0 && m_throttleBytesPerSecond < m_maximumTransmissionUnit)
|
||||
m_throttleBytesPerSecond = m_maximumTransmissionUnit;
|
||||
|
||||
m_isLocked = true;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f
|
||||
/// </summary>
|
||||
public float SimulatedLoss
|
||||
{
|
||||
get { return m_loss; }
|
||||
set { m_loss = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum simulated amount of one way latency for sent packets in seconds
|
||||
/// </summary>
|
||||
public float SimulatedMinimumLatency
|
||||
{
|
||||
get { return m_minimumOneWayLatency; }
|
||||
set { m_minimumOneWayLatency = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the simulated added random amount of one way latency for sent packets in seconds
|
||||
/// </summary>
|
||||
public float SimulatedRandomLatency
|
||||
{
|
||||
get { return m_randomOneWayLatency; }
|
||||
set { m_randomOneWayLatency = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the average simulated one way latency in seconds
|
||||
/// </summary>
|
||||
public float SimulatedAverageLatency
|
||||
{
|
||||
get { return m_minimumOneWayLatency + (m_randomOneWayLatency * 0.5f); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the simulated amount of duplicated packets from 0.0f to 1.0f
|
||||
/// </summary>
|
||||
public float SimulatedDuplicatesChance
|
||||
{
|
||||
get { return m_duplicates; }
|
||||
set { m_duplicates = value; }
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the identifier of this application; the library can only connect to matching app identifier peers
|
||||
/// Gets the identifier of this application; the library can only connect to matching app identifier peers
|
||||
/// </summary>
|
||||
public string AppIdentifier
|
||||
{
|
||||
@@ -209,28 +141,19 @@ namespace Lidgren.Network
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers
|
||||
/// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int MaximumTransmissionUnit
|
||||
public string NetworkThreadName
|
||||
{
|
||||
get { return m_maximumTransmissionUnit; }
|
||||
get { return m_networkThreadName; }
|
||||
set
|
||||
{
|
||||
if (value < 1 || value >= 4096)
|
||||
throw new NetException("MaximumTransmissionUnit must be between 1 and 4095 bytes");
|
||||
m_maximumTransmissionUnit = value;
|
||||
if (m_isLocked)
|
||||
throw new NetException("NetworkThreadName may not be set after the NetPeer which uses the configuration has been started");
|
||||
m_networkThreadName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if message coalescing (sending multiple messages in a single packet) should be used. Normally this should be true.
|
||||
/// </summary>
|
||||
public bool UseMessageCoalescing
|
||||
{
|
||||
get { return m_useMessageCoalescing; }
|
||||
set { m_useMessageCoalescing = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of connections this peer can hold. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
@@ -246,12 +169,17 @@ namespace Lidgren.Network
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the NetPeer should accept incoming connections. This is automatically set to true in NetServer and false in NetClient.
|
||||
/// Gets or sets the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers
|
||||
/// </summary>
|
||||
public bool AcceptIncomingConnections
|
||||
public int MaximumTransmissionUnit
|
||||
{
|
||||
get { return m_acceptIncomingConnections; }
|
||||
set { m_acceptIncomingConnections = value; }
|
||||
get { return m_maximumTransmissionUnit; }
|
||||
set
|
||||
{
|
||||
if (value < 1 || value >= ((ushort.MaxValue + 1) / 8))
|
||||
throw new NetException("MaximumTransmissionUnit must be between 1 and " + (((ushort.MaxValue + 1) / 8) - 1) + " bytes");
|
||||
m_maximumTransmissionUnit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -263,6 +191,43 @@ namespace Lidgren.Network
|
||||
set { m_defaultOutgoingMessageCapacity = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the time between latency calculating pings
|
||||
/// </summary>
|
||||
public float PingInterval
|
||||
{
|
||||
get { return m_pingInterval; }
|
||||
set { m_pingInterval = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the library should recycling messages to avoid excessive garbage collection. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public bool UseMessageRecycling
|
||||
{
|
||||
get { return m_useMessageRecycling; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_useMessageRecycling = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong
|
||||
/// </summary>
|
||||
public float ConnectionTimeout
|
||||
{
|
||||
get { return m_connectionTimeout; }
|
||||
set
|
||||
{
|
||||
if (value < m_pingInterval)
|
||||
throw new NetException("Connection timeout cannot be lower than ping interval!");
|
||||
m_connectionTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
@@ -320,86 +285,65 @@ namespace Lidgren.Network
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds of non-response before disconnecting because of time out. Cannot be changed once NetPeer is initialized.
|
||||
/// Gets or sets if the NetPeer should accept incoming connections. This is automatically set to true in NetServer and false in NetClient.
|
||||
/// </summary>
|
||||
public float ConnectionTimeout
|
||||
public bool AcceptIncomingConnections
|
||||
{
|
||||
get { return m_connectionTimeout; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_connectionTimeout = value;
|
||||
get { return m_acceptIncomingConnections; }
|
||||
set { m_acceptIncomingConnections = value; }
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f
|
||||
/// </summary>
|
||||
public float SimulatedLoss
|
||||
{
|
||||
get { return m_loss; }
|
||||
set { m_loss = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds between latency calculation (rtt) pings
|
||||
/// Gets or sets the minimum simulated amount of one way latency for sent packets in seconds
|
||||
/// </summary>
|
||||
public float PingFrequency
|
||||
public float SimulatedMinimumLatency
|
||||
{
|
||||
get { return m_pingFrequency; }
|
||||
set { m_pingFrequency = value; }
|
||||
get { return m_minimumOneWayLatency; }
|
||||
set { m_minimumOneWayLatency = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of allowed bytes to be sent per second per connection; 0 means unlimited (throttling disabled)
|
||||
/// Gets or sets the simulated added random amount of one way latency for sent packets in seconds
|
||||
/// </summary>
|
||||
public int ThrottleBytesPerSecond
|
||||
public float SimulatedRandomLatency
|
||||
{
|
||||
get { return m_throttleBytesPerSecond; }
|
||||
set
|
||||
{
|
||||
if (m_throttleBytesPerSecond != 0 && m_throttleBytesPerSecond < m_maximumTransmissionUnit)
|
||||
throw new NetException("ThrottleBytesPerSecond can not be lower than MaximumTransmissionUnit");
|
||||
m_throttleBytesPerSecond = value;
|
||||
}
|
||||
get { return m_randomOneWayLatency; }
|
||||
set { m_randomOneWayLatency = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the peak number of bytes sent before throttling kicks in, if enabled
|
||||
/// Gets the average simulated one way latency in seconds
|
||||
/// </summary>
|
||||
public int ThrottlePeakBytes
|
||||
public float SimulatedAverageLatency
|
||||
{
|
||||
get { return m_throttlePeakBytes; }
|
||||
set
|
||||
{
|
||||
if (m_throttlePeakBytes < m_maximumTransmissionUnit)
|
||||
throw new NetException("ThrottlePeakBytes can not be lower than MaximumTransmissionUnit");
|
||||
m_throttlePeakBytes = value;
|
||||
}
|
||||
get { return m_minimumOneWayLatency + (m_randomOneWayLatency * 0.5f); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number between handshake attempts in seconds
|
||||
/// Gets or sets the simulated amount of duplicated packets from 0.0f to 1.0f
|
||||
/// </summary>
|
||||
public float HandshakeAttemptDelay
|
||||
public float SimulatedDuplicatesChance
|
||||
{
|
||||
get { return m_handshakeAttemptDelay; }
|
||||
set { m_handshakeAttemptDelay = value; }
|
||||
get { return m_duplicates; }
|
||||
set { m_duplicates = value; }
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of handshake attempts before declaring failure to shake hands
|
||||
/// </summary>
|
||||
public int HandshakeMaxAttempts
|
||||
public NetPeerConfiguration Clone()
|
||||
{
|
||||
get { return m_handshakeMaxAttempts; }
|
||||
set { m_handshakeMaxAttempts = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of bytes kept in the recycle pool. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int MaxRecycledBytesKept
|
||||
{
|
||||
get { return m_maxRecycledBytesKept; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_maxRecycledBytesKept = value;
|
||||
}
|
||||
NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration;
|
||||
retval.m_isLocked = false;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Statistics for a NetPeer instance
|
||||
/// </summary>
|
||||
public sealed class NetPeerStatistics
|
||||
{
|
||||
private readonly NetPeer m_peer;
|
||||
@@ -96,7 +99,7 @@ namespace Lidgren.Network
|
||||
/// <summary>
|
||||
/// Gets the number of bytes in the recycled pool
|
||||
/// </summary>
|
||||
public int BytesInRecyclePool { get { return m_peer.m_storedBytes; } }
|
||||
public int BytesInRecyclePool { get { return m_peer.m_storagePoolBytes; } }
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
@@ -121,7 +124,7 @@ namespace Lidgren.Network
|
||||
bdr.AppendLine("Sent " + m_sentBytes + " bytes in " + m_sentMessages + " messages in " + m_sentPackets + " packets");
|
||||
bdr.AppendLine("Received " + m_receivedBytes + " bytes in " + m_receivedMessages + " messages in " + m_receivedPackets + " packets");
|
||||
bdr.AppendLine("Allocated " + m_bytesAllocated + " bytes");
|
||||
bdr.AppendLine("Recycled pool " + m_peer.m_storedBytes + " bytes");
|
||||
bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes");
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,9 @@ using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Status for a NetPeer instance
|
||||
/// </summary>
|
||||
public enum NetPeerStatus
|
||||
{
|
||||
NotRunning = 0,
|
||||
|
||||
@@ -62,15 +62,6 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
#if DEBUG
|
||||
if (typeof(T) == typeof(NetSending))
|
||||
{
|
||||
NetSending om = item as NetSending;
|
||||
if (om != null)
|
||||
if (om.MessageType == NetMessageType.Error)
|
||||
throw new NetException("Enqueuing NetSending with MessageType.Error!");
|
||||
}
|
||||
#endif
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == m_items.Length)
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// A fast random number generator for .NET
|
||||
/// Colin Green, January 2005
|
||||
///
|
||||
/// </summary>
|
||||
/// September 4th 2005
|
||||
/// Added NextBytesUnsafe() - commented out by default.
|
||||
/// Fixed bug in Reinitialise() - y,z and w variables were not being reset.
|
||||
@@ -21,7 +19,7 @@ namespace Lidgren.Network
|
||||
/// how this can be easily extened if you need a longer period. At the time of writing I could find no
|
||||
/// information on the period of System.Random for comparison.
|
||||
///
|
||||
/// 2) Faster than System.Random. Up to 15x faster, depending on which methods are called.
|
||||
/// 2) Faster than System.Random. Up to 8x faster, depending on which methods are called.
|
||||
///
|
||||
/// 3) Direct replacement for System.Random. This class implements all of the methods that System.Random
|
||||
/// does plus some additional methods. The like named methods are functionally equivalent.
|
||||
@@ -37,49 +35,28 @@ namespace Lidgren.Network
|
||||
/// A further performance improvement can be obtained by declaring local variables as static, thus avoiding
|
||||
/// re-allocation of variables on each call. However care should be taken if multiple instances of
|
||||
/// FastRandom are in use or if being used in a multi-threaded environment.
|
||||
///
|
||||
/// </summary>
|
||||
public sealed class NetRandom : Random
|
||||
public class NetRandom
|
||||
{
|
||||
public static NetRandom Instance = new NetRandom();
|
||||
|
||||
protected override double Sample()
|
||||
{
|
||||
return NextDouble();
|
||||
}
|
||||
public static readonly NetRandom Instance = new NetRandom();
|
||||
|
||||
// The +1 ensures NextDouble doesn't generate 1.0
|
||||
private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0);
|
||||
private const double c_realUnitUint = 1.0 / ((double)uint.MaxValue + 1.0);
|
||||
private const uint c_y = 842502087, c_z = 3579807591, c_w = 273326509;
|
||||
const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
|
||||
const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
|
||||
const uint Y = 842502087, Z = 3579807591, W = 273326509;
|
||||
|
||||
private static int s_extraSeed = 42;
|
||||
|
||||
uint m_x, m_y, m_z, m_w;
|
||||
uint x, y, z, w;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random seed based on time and working set
|
||||
/// </summary>
|
||||
public static int GetRandomSeed(object forObject)
|
||||
{
|
||||
// mix some semi-random properties
|
||||
int seed = (int)Environment.TickCount;
|
||||
seed ^= forObject.GetHashCode();
|
||||
seed ^= (int)(Stopwatch.GetTimestamp());
|
||||
seed ^= (int)(Environment.WorkingSet); // will return 0 on mono
|
||||
|
||||
int extraSeed = Interlocked.Increment(ref s_extraSeed);
|
||||
|
||||
return seed + extraSeed;
|
||||
}
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance using time dependent seed.
|
||||
/// </summary>
|
||||
public NetRandom()
|
||||
{
|
||||
// Initialise using the system tick count
|
||||
Reinitialise(GetRandomSeed(this));
|
||||
// Initialise using the system tick count.
|
||||
Reinitialise(GetSeed(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,6 +69,23 @@ namespace Lidgren.Network
|
||||
Reinitialise(seed);
|
||||
}
|
||||
|
||||
public int GetSeed(object forObject)
|
||||
{
|
||||
// mix some semi-random properties
|
||||
int seed = (int)Environment.TickCount;
|
||||
seed ^= forObject.GetHashCode();
|
||||
//seed ^= (int)(Stopwatch.GetTimestamp());
|
||||
//seed ^= (int)(Environment.WorkingSet); // will return 0 on mono
|
||||
|
||||
int extraSeed = System.Threading.Interlocked.Increment(ref s_extraSeed);
|
||||
|
||||
return seed + extraSeed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Reinitialisation]
|
||||
|
||||
/// <summary>
|
||||
/// Reinitialises using an int value as a seed.
|
||||
/// </summary>
|
||||
@@ -101,88 +95,97 @@ namespace Lidgren.Network
|
||||
// The only stipulation stated for the xorshift RNG is that at least one of
|
||||
// the seeds x,y,z,w is non-zero. We fulfill that requirement by only allowing
|
||||
// resetting of the x seed
|
||||
m_x = (uint)seed;
|
||||
m_y = c_y;
|
||||
m_z = c_z;
|
||||
m_w = c_w;
|
||||
x = (uint)seed;
|
||||
y = Y;
|
||||
z = Z;
|
||||
w = W;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a uint. Values returned are over the full range of a uint,
|
||||
/// uint.MinValue to uint.MaxValue, including the min and max values.
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
return (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods [System.Random functionally equivalent methods]
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int. Values returned are over the range 0 to int.MaxValue-1.
|
||||
/// MaxValue is not generated to remain functionally equivalent to System.Random.Next().
|
||||
/// If you require an int from the full range, including negative values then call
|
||||
/// NextUint() and cast the value to an int.
|
||||
/// Generates a random int over the range 0 to int.MaxValue-1.
|
||||
/// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next().
|
||||
/// This does slightly eat into some of the performance gain over System.Random, but not much.
|
||||
/// For better performance see:
|
||||
///
|
||||
/// Call NextInt() for an int over the range 0 to int.MaxValue.
|
||||
///
|
||||
/// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range
|
||||
/// including negative values.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int Next()
|
||||
public int Next()
|
||||
{
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
return (int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8))));
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
// Handle the special case where the value int.MaxValue is generated. This is outside of
|
||||
// the range of permitted values, so we therefore call Next() to try again.
|
||||
uint rtn = w & 0x7FFFFFFF;
|
||||
if (rtn == 0x7FFFFFFF)
|
||||
return Next();
|
||||
return (int)rtn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
|
||||
/// </summary>
|
||||
public override int Next(int maxValue)
|
||||
/// <param name="upperBound"></param>
|
||||
/// <returns></returns>
|
||||
public int Next(int upperBound)
|
||||
{
|
||||
if (maxValue < 0)
|
||||
throw new ArgumentOutOfRangeException("maxValue", maxValue, "maxValue must be >=0");
|
||||
if (upperBound < 0)
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
|
||||
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
return (int)((c_realUnitInt * (int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8))))) * maxValue);
|
||||
return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range minValue to maxValue-1, and not including maxValue.
|
||||
/// maxValue must be >= minValue. minValue may be negative.
|
||||
/// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
|
||||
/// upperBound must be >= lowerBound. lowerBound may be negative.
|
||||
/// </summary>
|
||||
public override int Next(int minValue, int maxValue)
|
||||
/// <param name="lowerBound"></param>
|
||||
/// <param name="upperBound"></param>
|
||||
/// <returns></returns>
|
||||
public int Next(int lowerBound, int upperBound)
|
||||
{
|
||||
if (minValue > maxValue)
|
||||
throw new ArgumentOutOfRangeException("maxValue", maxValue, "maxValue must be >=minValue");
|
||||
if (lowerBound > upperBound)
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
|
||||
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
int range = maxValue - minValue;
|
||||
int range = upperBound - lowerBound;
|
||||
if (range < 0)
|
||||
{ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
|
||||
// We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
|
||||
return minValue + (int)((c_realUnitUint * (double)(m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)maxValue - (long)minValue));
|
||||
return lowerBound + (int)((REAL_UNIT_UINT * (double)(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
|
||||
}
|
||||
|
||||
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int anf gain
|
||||
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
|
||||
// a little more performance.
|
||||
return minValue + (int)((c_realUnitInt * (double)(int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
|
||||
return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double. Values returned are from 0.0 up to but not including 1.0.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override double NextDouble()
|
||||
public double NextDouble()
|
||||
{
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
@@ -190,117 +193,45 @@ namespace Lidgren.Network
|
||||
// this final cast is a lot faster than casting from a uint to a double. The extra cast
|
||||
// to an int is very fast (the allocated bits remain the same) and so the overall effect
|
||||
// of the extra cast is a significant performance improvement.
|
||||
return (c_realUnitInt * (int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
//
|
||||
// Also note that the loss of one bit of precision is equivalent to what occurs within
|
||||
// System.Random.
|
||||
return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double. Values returned are from 0.0 up to but not including 1.0.
|
||||
/// Generates a random single. Values returned are from 0.0 up to but not including 1.0.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public float NextFloat()
|
||||
public float NextSingle()
|
||||
{
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
// int the compiler will then cast the int to a double to perform the multiplication,
|
||||
// this final cast is a lot faster than casting from a uint to a double. The extra cast
|
||||
// to an int is very fast (the allocated bits remain the same) and so the overall effect
|
||||
// of the extra cast is a significant performance improvement.
|
||||
return (float)(c_realUnitInt * (int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double. Values returned are from 0.0 up to but not including roof
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public float NextFloat(float roof)
|
||||
{
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
// int the compiler will then cast the int to a double to perform the multiplication,
|
||||
// this final cast is a lot faster than casting from a uint to a double. The extra cast
|
||||
// to an int is very fast (the allocated bits remain the same) and so the overall effect
|
||||
// of the extra cast is a significant performance improvement.
|
||||
float f = (float)(c_realUnitInt * (int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
|
||||
return f * roof;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double. Values returned are from min up to but not including min + variance
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public float NextFloat(float min, float variance)
|
||||
{
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
// int the compiler will then cast the int to a double to perform the multiplication,
|
||||
// this final cast is a lot faster than casting from a uint to a double. The extra cast
|
||||
// to an int is very fast (the allocated bits remain the same) and so the overall effect
|
||||
// of the extra cast is a significant performance improvement.
|
||||
float f = (float)(c_realUnitInt * (int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
|
||||
return min + f * variance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If passed 0.7f it will return true 7 times out of 10
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool Chance(float percentChance)
|
||||
{
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
// int the compiler will then cast the int to a double to perform the multiplication,
|
||||
// this final cast is a lot faster than casting from a uint to a double. The extra cast
|
||||
// to an int is very fast (the allocated bits remain the same) and so the overall effect
|
||||
// of the extra cast is a significant performance improvement.
|
||||
double hit = (c_realUnitInt * (int)(0x7FFFFFFF & (m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
return (hit < percentChance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a System.Single larger or equal to 0 and smaller than 1.0f - gaussian distributed!
|
||||
/// </summary>
|
||||
public float NextGaussian()
|
||||
{
|
||||
return (float)((NextDouble() + NextDouble() + NextDouble()) / 3.0);
|
||||
return (float)NextDouble();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the provided byte array with random bytes.
|
||||
/// Increased performance is achieved by dividing and packaging bits directly from the
|
||||
/// random number generator and storing them in 4 byte 'chunks'.
|
||||
/// This method is functionally equivalent to System.Random.NextBytes().
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
public override void NextBytes(byte[] buffer)
|
||||
public void NextBytes(byte[] buffer)
|
||||
{
|
||||
// Fill up the bulk of the buffer in chunks of 4 bytes at a time.
|
||||
uint x = this.m_x, y = this.m_y, z = this.m_z, w = this.m_w;
|
||||
uint x = this.x, y = this.y, z = this.z, w = this.w;
|
||||
int i = 0;
|
||||
uint t;
|
||||
for (; i < buffer.Length - 3; )
|
||||
for (int bound = buffer.Length - 3; i < bound; )
|
||||
{
|
||||
// Generate 4 bytes.
|
||||
// Increased performance is achieved by generating 4 random bytes per loop.
|
||||
// Also note that no mask needs to be applied to zero out the higher order bytes before
|
||||
// casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out.
|
||||
t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte)(w & 0x000000FF);
|
||||
buffer[i++] = (byte)((w & 0x0000FF00) >> 8);
|
||||
buffer[i++] = (byte)((w & 0x00FF0000) >> 16);
|
||||
buffer[i++] = (byte)((w & 0xFF000000) >> 24);
|
||||
buffer[i++] = (byte)w;
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
buffer[i++] = (byte)(w >> 24);
|
||||
}
|
||||
|
||||
// Fill up any remaining bytes in the buffer.
|
||||
@@ -311,80 +242,126 @@ namespace Lidgren.Network
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte)(w & 0x000000FF);
|
||||
buffer[i++] = (byte)w;
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)((w & 0x0000FF00) >> 8);
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)((w & 0x00FF0000) >> 16);
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i] = (byte)((w & 0xFF000000) >> 24);
|
||||
buffer[i] = (byte)(w >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.m_x = x; this.m_y = y; this.m_z = z; this.m_w = w;
|
||||
this.x = x; this.y = y; this.z = z; this.w = w;
|
||||
}
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// A version of NextBytes that uses a pointer to set 4 bytes of the byte buffer in one operation
|
||||
// /// thus providing a nice speedup. Note that this requires the unsafe compilation flag to be specified
|
||||
// /// and so is commented out by default.
|
||||
// /// thus providing a nice speedup. The loop is also partially unrolled to allow out-of-order-execution,
|
||||
// /// this results in about a x2 speedup on an AMD Athlon. Thus performance may vary wildly on different CPUs
|
||||
// /// depending on the number of execution units available.
|
||||
// ///
|
||||
// /// Another significant speedup is obtained by setting the 4 bytes by indexing pDWord (e.g. pDWord[i++]=w)
|
||||
// /// instead of adjusting it dereferencing it (e.g. *pDWord++=w).
|
||||
// ///
|
||||
// /// Note that this routine requires the unsafe compilation flag to be specified and so is commented out by default.
|
||||
// /// </summary>
|
||||
// /// <param name="buffer"></param>
|
||||
// public unsafe void NextBytesUnsafe(byte[] buffer)
|
||||
// {
|
||||
// if(buffer.Length % 4 != 0)
|
||||
// throw new ArgumentException("Buffer length must be divisible by 4", "buffer");
|
||||
// if(buffer.Length % 8 != 0)
|
||||
// throw new ArgumentException("Buffer length must be divisible by 8", "buffer");
|
||||
//
|
||||
// uint x=this.x, y=this.y, z=this.z, w=this.w;
|
||||
// uint t;
|
||||
//
|
||||
// fixed(byte* pByte0 = buffer)
|
||||
// {
|
||||
// uint* pDWord = (uint*)pByte0;
|
||||
// for(int i = 0, len = buffer.Length>>2; i < len; i++)
|
||||
// for(int i=0, len=buffer.Length>>2; i < len; i+=2)
|
||||
// {
|
||||
// uint t=(x^(x<<11));
|
||||
// x=y; y=z; z=w;
|
||||
// pDWord[i] = w = (w^(w>>19))^(t^(t>>8));
|
||||
//
|
||||
// t=(x^(x<<11));
|
||||
// x=y; y=z; z=w;
|
||||
// *pDWord++ = w = (w^(w>>19))^(t^(t>>8));
|
||||
// pDWord[i+1] = w = (w^(w>>19))^(t^(t>>8));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.x=x; this.y=y; this.z=z; this.w=w;
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Methods not present on System.Random]
|
||||
|
||||
/// <summary>
|
||||
/// Generates a uint. Values returned are over the full range of a uint,
|
||||
/// uint.MinValue to uint.MaxValue, inclusive.
|
||||
///
|
||||
/// This is the fastest method for generating a single random number because the underlying
|
||||
/// random number generator algorithm generates 32 random bits that can be cast directly to
|
||||
/// a uint.
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to int.MaxValue, inclusive.
|
||||
/// This method differs from Next() only in that the range is 0 to int.MaxValue
|
||||
/// and not 0 to int.MaxValue-1.
|
||||
///
|
||||
/// The slight difference in range means this method is slightly faster than Next()
|
||||
/// but is not functionally equivalent to System.Random.Next().
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int NextInt()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
|
||||
}
|
||||
|
||||
|
||||
// Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
|
||||
// with bitBufferIdx.
|
||||
uint bitBuffer;
|
||||
int bitBufferIdx = 32;
|
||||
uint bitMask = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Generates random bool.
|
||||
/// Increased performance is achieved by buffering 32 random bits for
|
||||
/// future calls. Thus the random number generator is only invoked once
|
||||
/// in every 32 calls.
|
||||
/// Generates a single random bit.
|
||||
/// This method's performance is improved by generating 32 bits in one operation and storing them
|
||||
/// ready for future calls.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool NextBool()
|
||||
{
|
||||
if (bitBufferIdx == 32)
|
||||
if (bitMask == 1)
|
||||
{
|
||||
// Generate 32 more bits.
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
bitBuffer = m_w = (m_w ^ (m_w >> 19)) ^ (t ^ (t >> 8));
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
// Reset the idx that tells us which bit to read next.
|
||||
bitBufferIdx = 1;
|
||||
return (bitBuffer & 0x1) == 1;
|
||||
// Reset the bitMask that tells us which bit to read next.
|
||||
bitMask = 0x80000000;
|
||||
return (bitBuffer & bitMask) == 0;
|
||||
}
|
||||
|
||||
bitBufferIdx++;
|
||||
return ((bitBuffer >>= 1) & 0x1) == 1;
|
||||
return (bitBuffer & (bitMask >>= 1)) == 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
18
Lidgren.Network/NetReceiverChannelBase.cs
Normal file
18
Lidgren.Network/NetReceiverChannelBase.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class NetReceiverChannelBase
|
||||
{
|
||||
internal NetPeer m_peer;
|
||||
internal NetConnection m_connection;
|
||||
|
||||
public NetReceiverChannelBase(NetConnection connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_peer = connection.m_peer;
|
||||
}
|
||||
|
||||
internal abstract void ReceiveMessage(NetIncomingMessage msg);
|
||||
}
|
||||
}
|
||||
87
Lidgren.Network/NetReliableOrderedReceiver.cs
Normal file
87
Lidgren.Network/NetReliableOrderedReceiver.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableOrderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private NetBitVector m_earlyReceived;
|
||||
internal NetIncomingMessage[] m_withheldMessages;
|
||||
|
||||
public NetReliableOrderedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
m_withheldMessages = new NetIncomingMessage[windowSize];
|
||||
m_earlyReceived = new NetBitVector(windowSize);
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
// release withheld messages
|
||||
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
while (m_earlyReceived[nextSeqNr % m_windowSize])
|
||||
{
|
||||
message = m_withheldMessages[nextSeqNr % m_windowSize];
|
||||
NetException.Assert(message != null);
|
||||
|
||||
// remove it from withheld messages
|
||||
m_withheldMessages[nextSeqNr % m_windowSize] = null;
|
||||
|
||||
m_peer.LogVerbose("Releasing withheld message #" + message);
|
||||
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
AdvanceWindow();
|
||||
nextSeqNr++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
|
||||
// duplicate
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
|
||||
m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
|
||||
m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Lidgren.Network/NetReliableSenderChannel.cs
Normal file
236
Lidgren.Network/NetReliableSenderChannel.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Sender part of Selective repeat ARQ for a particular NetChannel
|
||||
/// </summary>
|
||||
internal sealed class NetReliableSenderChannel : NetSenderChannelBase
|
||||
{
|
||||
private NetConnection m_connection;
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private int m_sendStart;
|
||||
|
||||
private NetBitVector m_receivedAcks;
|
||||
internal NetStoredReliableMessage[] m_storedMessages;
|
||||
|
||||
internal override int WindowSize { get { return m_windowSize; } }
|
||||
|
||||
internal NetReliableSenderChannel(NetConnection connection, int windowSize)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_windowSize = windowSize;
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
|
||||
m_storedMessages = new NetStoredReliableMessage[m_windowSize];
|
||||
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
|
||||
}
|
||||
|
||||
internal override int GetAllowedSends()
|
||||
{
|
||||
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
NetException.Assert(retval >= 0 && retval <= m_windowSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
m_receivedAcks.Clear();
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
m_storedMessages[i].Reset();
|
||||
m_queuedSends.Clear();
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
}
|
||||
|
||||
internal override NetSendResult Enqueue(NetOutgoingMessage message)
|
||||
{
|
||||
m_queuedSends.Enqueue(message);
|
||||
|
||||
int queueLen = m_queuedSends.Count;
|
||||
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
if (queueLen <= left)
|
||||
return NetSendResult.Sent;
|
||||
return NetSendResult.Queued;
|
||||
}
|
||||
|
||||
// call this regularely
|
||||
internal override void SendQueuedMessages(float now)
|
||||
{
|
||||
// resends
|
||||
float resendDelay = m_connection.GetResendDelay();
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
{
|
||||
NetOutgoingMessage om = m_storedMessages[i].Message;
|
||||
if (om == null)
|
||||
continue;
|
||||
|
||||
float t = m_storedMessages[i].LastSent;
|
||||
if (t > 0 && (now - t) > resendDelay)
|
||||
{
|
||||
// deduce sequence number
|
||||
int startSlot = m_windowStart % m_windowSize;
|
||||
int seqNr = m_windowStart;
|
||||
while (startSlot != i)
|
||||
{
|
||||
startSlot--;
|
||||
if (startSlot < 0)
|
||||
startSlot = m_windowSize - 1;
|
||||
seqNr--;
|
||||
}
|
||||
|
||||
m_connection.m_peer.LogVerbose("Resending due to delay #" + seqNr + " " + om.ToString());
|
||||
m_connection.m_statistics.MessageResent();
|
||||
|
||||
m_connection.QueueSendMessage(om, seqNr);
|
||||
|
||||
m_storedMessages[i].LastSent = now;
|
||||
m_storedMessages[i].NumSent++;
|
||||
}
|
||||
}
|
||||
|
||||
int num = GetAllowedSends();
|
||||
if (num < 1)
|
||||
return;
|
||||
|
||||
// queued sends
|
||||
while (m_queuedSends.Count > 0 && num > 0)
|
||||
{
|
||||
NetOutgoingMessage om;
|
||||
if (m_queuedSends.TryDequeue(out om))
|
||||
ExecuteSend(now, om);
|
||||
num--;
|
||||
NetException.Assert(num == GetAllowedSends());
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteSend(float now, NetOutgoingMessage message)
|
||||
{
|
||||
int seqNr = m_sendStart;
|
||||
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
m_connection.QueueSendMessage(message, seqNr);
|
||||
|
||||
int storeIndex = seqNr % m_windowSize;
|
||||
NetException.Assert(m_storedMessages[storeIndex].Message == null);
|
||||
|
||||
m_storedMessages[storeIndex].NumSent++;
|
||||
m_storedMessages[storeIndex].Message = message;
|
||||
m_storedMessages[storeIndex].LastSent = now;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void DestoreMessage(int storeIndex)
|
||||
{
|
||||
NetOutgoingMessage storedMessage = m_storedMessages[storeIndex].Message;
|
||||
NetException.Assert(storedMessage != null);
|
||||
|
||||
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
|
||||
if (storedMessage.m_recyclingCount <= 0)
|
||||
m_connection.m_peer.Recycle(storedMessage);
|
||||
|
||||
m_storedMessages[storeIndex] = new NetStoredReliableMessage();
|
||||
}
|
||||
|
||||
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
|
||||
// seqNr is the actual nr received
|
||||
internal override void ReceiveAcknowledge(float now, int seqNr)
|
||||
{
|
||||
// late (dupe), on time or early ack?
|
||||
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
|
||||
return; // late/duplicate ack
|
||||
}
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
|
||||
|
||||
// ack arrived right on time
|
||||
NetException.Assert(seqNr == m_windowStart);
|
||||
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
DestoreMessage(m_windowStart % m_windowSize);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
// advance window if we already have early acks
|
||||
while (m_receivedAcks[m_windowStart])
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "...");
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
//m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// early ack... (if it has been sent!)
|
||||
//
|
||||
// If it has been sent either the m_windowStart message was lost
|
||||
// ... or the ack for that message was lost
|
||||
//
|
||||
|
||||
//m_connection.m_peer.LogDebug("Received early ack for #" + seqNr);
|
||||
|
||||
int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart);
|
||||
if (sendRelate <= 0)
|
||||
{
|
||||
// yes, we've sent this message - it's an early (but valid) ack
|
||||
if (m_receivedAcks[seqNr])
|
||||
{
|
||||
// we've already destored/been acked for this message
|
||||
}
|
||||
else
|
||||
{
|
||||
DestoreMessage(seqNr % m_windowSize);
|
||||
m_receivedAcks[seqNr] = true;
|
||||
}
|
||||
}
|
||||
else if (sendRelate > 0)
|
||||
{
|
||||
// uh... we haven't sent this message yet? Weird, dupe or error...
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, lets resend all missing acks
|
||||
int rnr = seqNr;
|
||||
do
|
||||
{
|
||||
rnr--;
|
||||
if (rnr < 0)
|
||||
rnr = NetConstants.NumSequenceNumbers - 1;
|
||||
|
||||
if (m_receivedAcks[rnr])
|
||||
{
|
||||
// m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)");
|
||||
}
|
||||
else
|
||||
{
|
||||
int slot = rnr % m_windowSize;
|
||||
NetException.Assert(m_storedMessages[slot].Message != null);
|
||||
if (m_storedMessages[slot].NumSent == 1)
|
||||
{
|
||||
// just sent once; resend immediately since we found gap in ack sequence
|
||||
NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
|
||||
m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")");
|
||||
m_storedMessages[slot].LastSent = now;
|
||||
m_storedMessages[slot].NumSent++;
|
||||
m_connection.m_statistics.MessageResent();
|
||||
m_connection.QueueSendMessage(rmsg, rnr);
|
||||
}
|
||||
}
|
||||
|
||||
} while (rnr != m_windowStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
Lidgren.Network/NetReliableSequencedReceiver.cs
Normal file
63
Lidgren.Network/NetReliableSequencedReceiver.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableSequencedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
|
||||
public NetReliableSequencedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int nr = message.m_sequenceNumber;
|
||||
|
||||
int relate = NetUtility.RelativeSequenceNumber(nr, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, nr);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE");
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
// ok
|
||||
m_windowStart = (m_windowStart + relate) % NetConstants.NumSequenceNumbers;
|
||||
m_peer.ReleaseMessage(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
Lidgren.Network/NetReliableUnorderedReceiver.cs
Normal file
87
Lidgren.Network/NetReliableUnorderedReceiver.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableUnorderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private NetBitVector m_earlyReceived;
|
||||
|
||||
public NetReliableUnorderedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
m_earlyReceived = new NetBitVector(windowSize);
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
// release withheld messages
|
||||
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
while (m_earlyReceived[nextSeqNr % m_windowSize])
|
||||
{
|
||||
//message = m_withheldMessages[nextSeqNr % m_windowSize];
|
||||
//NetException.Assert(message != null);
|
||||
|
||||
// remove it from withheld messages
|
||||
//m_withheldMessages[nextSeqNr % m_windowSize] = null;
|
||||
|
||||
//m_peer.LogVerbose("Releasing withheld message #" + message);
|
||||
|
||||
//m_peer.ReleaseMessage(message);
|
||||
|
||||
AdvanceWindow();
|
||||
nextSeqNr++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
// duplicate
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
|
||||
//m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
|
||||
//m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
|
||||
|
||||
m_peer.ReleaseMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Lidgren.Network/NetSendResult.cs
Normal file
30
Lidgren.Network/NetSendResult.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of a SendMessage call
|
||||
/// </summary>
|
||||
public enum NetSendResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Message failed to enqueue; for example if there's no connection in place
|
||||
/// </summary>
|
||||
Failed = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Message was immediately sent
|
||||
/// </summary>
|
||||
Sent = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Message was queued for delivery
|
||||
/// </summary>
|
||||
Queued = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Message was dropped immediately since too many message were queued
|
||||
/// </summary>
|
||||
Dropped = 3
|
||||
}
|
||||
}
|
||||
19
Lidgren.Network/NetSenderChannelBase.cs
Normal file
19
Lidgren.Network/NetSenderChannelBase.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class NetSenderChannelBase
|
||||
{
|
||||
// access this directly to queue things in this channel
|
||||
internal NetQueue<NetOutgoingMessage> m_queuedSends;
|
||||
|
||||
internal abstract int WindowSize { get; }
|
||||
|
||||
internal abstract int GetAllowedSends();
|
||||
|
||||
internal abstract NetSendResult Enqueue(NetOutgoingMessage message);
|
||||
internal abstract void SendQueuedMessages(float now);
|
||||
internal abstract void Reset();
|
||||
internal abstract void ReceiveAcknowledge(float now, int sequenceNumber);
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
[DebuggerDisplay("MessageType={MessageType} SequenceNumber={SequenceNumber} NumSends={NumSends}")]
|
||||
internal sealed class NetSending
|
||||
{
|
||||
public NetOutgoingMessage Message;
|
||||
public IPEndPoint Recipient;
|
||||
public NetMessageType MessageType;
|
||||
public ushort SequenceNumber;
|
||||
public double NextResend;
|
||||
public int NumSends; // how many times has this sending been sent
|
||||
|
||||
public int FragmentGroupId;
|
||||
public int FragmentNumber;
|
||||
public int FragmentTotalCount;
|
||||
|
||||
public NetSending(NetOutgoingMessage msg, NetMessageType tp, ushort sequenceNumber)
|
||||
{
|
||||
Message = msg;
|
||||
MessageType = tp;
|
||||
SequenceNumber = sequenceNumber;
|
||||
}
|
||||
|
||||
internal void SetNextResend(NetConnection conn)
|
||||
{
|
||||
float baseDelay;
|
||||
switch(NumSends)
|
||||
{
|
||||
case 0: baseDelay = 0.025f; break;
|
||||
case 1: baseDelay = 0.05f; break;
|
||||
case 2: baseDelay = 0.15f; break;
|
||||
case 3: baseDelay = 0.3f; break;
|
||||
default:
|
||||
baseDelay = (float)(NumSends - 3); // 4: 1 second, 5: 2 seconds, 6: 3 seconds etc
|
||||
break;
|
||||
}
|
||||
|
||||
float rttMultiplier = 1.15f + (0.15f * NumSends);
|
||||
|
||||
float totalDelay = baseDelay + (conn.AverageRoundtripTime * rttMultiplier);
|
||||
|
||||
NextResend = NetTime.Now + totalDelay;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetSending " + MessageType + "#" + SequenceNumber + " NumSends: " + NumSends + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,16 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Specialized version of NetPeer used for "server" peers
|
||||
/// </summary>
|
||||
public class NetServer : NetPeer
|
||||
{
|
||||
public NetServer(NetPeerConfiguration config)
|
||||
: base(config)
|
||||
{
|
||||
// force this to true
|
||||
config.AcceptIncomingConnections = true;
|
||||
}
|
||||
|
||||
public void Disconnect(NetConnection connection, string byeMessage)
|
||||
{
|
||||
if (connection == null)
|
||||
throw new ArgumentNullException("connection");
|
||||
|
||||
connection.Disconnect(byeMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
Lidgren.Network/NetStoredReliableMessage.cs
Normal file
18
Lidgren.Network/NetStoredReliableMessage.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal struct NetStoredReliableMessage
|
||||
{
|
||||
public int NumSent;
|
||||
public float LastSent;
|
||||
public NetOutgoingMessage Message;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
NumSent = 0;
|
||||
LastSent = 0;
|
||||
Message = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,10 +45,6 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)Environment.TickCount / 1000.0; } }
|
||||
#endif
|
||||
public static double GetRemoteNow(NetConnection conn)
|
||||
{
|
||||
return Now - conn.m_remoteToLocalNetTime;
|
||||
}
|
||||
|
||||
public static string ToReadable(double seconds)
|
||||
{
|
||||
|
||||
20
Lidgren.Network/NetTuple.cs
Normal file
20
Lidgren.Network/NetTuple.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
// replace with BCL 4.0 Tuple<> when appropriate
|
||||
internal struct NetTuple<A, B>
|
||||
{
|
||||
public A Item1;
|
||||
public B Item2;
|
||||
|
||||
public NetTuple(A item1, B item2)
|
||||
{
|
||||
Item1 = item1;
|
||||
Item2 = item2;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
Lidgren.Network/NetUnreliableSenderChannel.cs
Normal file
125
Lidgren.Network/NetUnreliableSenderChannel.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Sender part of Selective repeat ARQ for a particular NetChannel
|
||||
/// </summary>
|
||||
internal sealed class NetUnreliableSenderChannel : NetSenderChannelBase
|
||||
{
|
||||
private NetConnection m_connection;
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private int m_sendStart;
|
||||
|
||||
private NetBitVector m_receivedAcks;
|
||||
|
||||
internal override int WindowSize { get { return m_windowSize; } }
|
||||
|
||||
internal NetUnreliableSenderChannel(NetConnection connection, int windowSize)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_windowSize = windowSize;
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
|
||||
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
|
||||
}
|
||||
|
||||
internal override int GetAllowedSends()
|
||||
{
|
||||
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize;
|
||||
NetException.Assert(retval >= 0 && retval <= m_windowSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
m_receivedAcks.Clear();
|
||||
m_queuedSends.Clear();
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
}
|
||||
|
||||
internal override NetSendResult Enqueue(NetOutgoingMessage message)
|
||||
{
|
||||
int queueLen = m_queuedSends.Count + 1;
|
||||
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
if (queueLen > left)
|
||||
return NetSendResult.Dropped;
|
||||
|
||||
m_queuedSends.Enqueue(message);
|
||||
return NetSendResult.Sent;
|
||||
}
|
||||
|
||||
// call this regularely
|
||||
internal override void SendQueuedMessages(float now)
|
||||
{
|
||||
int num = GetAllowedSends();
|
||||
if (num < 1)
|
||||
return;
|
||||
|
||||
// queued sends
|
||||
while (m_queuedSends.Count > 0 && num > 0)
|
||||
{
|
||||
NetOutgoingMessage om;
|
||||
if (m_queuedSends.TryDequeue(out om))
|
||||
ExecuteSend(now, om);
|
||||
num--;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteSend(float now, NetOutgoingMessage message)
|
||||
{
|
||||
m_connection.m_peer.VerifyNetworkThread();
|
||||
|
||||
int seqNr = m_sendStart;
|
||||
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
m_connection.QueueSendMessage(message, seqNr);
|
||||
|
||||
Interlocked.Decrement(ref message.m_recyclingCount);
|
||||
if (message.m_recyclingCount <= 0)
|
||||
m_connection.m_peer.Recycle(message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
|
||||
// seqNr is the actual nr received
|
||||
internal override void ReceiveAcknowledge(float now, int seqNr)
|
||||
{
|
||||
// late (dupe), on time or early ack?
|
||||
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
|
||||
return; // late/duplicate ack
|
||||
}
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
|
||||
|
||||
// ack arrived right on time
|
||||
NetException.Assert(seqNr == m_windowStart);
|
||||
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Advance window to this position
|
||||
m_receivedAcks[seqNr] = true;
|
||||
|
||||
while (m_windowStart != seqNr)
|
||||
{
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
Lidgren.Network/NetUnreliableSequencedReceiver.cs
Normal file
29
Lidgren.Network/NetUnreliableSequencedReceiver.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_lastReceivedSequenceNumber;
|
||||
|
||||
public NetUnreliableSequencedReceiver(NetConnection connection)
|
||||
: base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage msg)
|
||||
{
|
||||
int nr = msg.m_sequenceNumber;
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(msg.m_receivedMessageType, nr);
|
||||
|
||||
int relate = NetUtility.RelativeSequenceNumber(nr, m_lastReceivedSequenceNumber);
|
||||
if (relate < 0)
|
||||
return; // drop if late
|
||||
|
||||
m_lastReceivedSequenceNumber = nr;
|
||||
m_peer.ReleaseMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
Lidgren.Network/NetUnreliableUnorderedReceiver.cs
Normal file
20
Lidgren.Network/NetUnreliableUnorderedReceiver.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetUnreliableUnorderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
public NetUnreliableUnorderedReceiver(NetConnection connection)
|
||||
: base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage msg)
|
||||
{
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(msg.m_receivedMessageType, msg.m_sequenceNumber);
|
||||
|
||||
m_peer.ReleaseMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,5 +261,61 @@ namespace Lidgren.Network
|
||||
retval[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
|
||||
return retval;
|
||||
}
|
||||
|
||||
public static string ToHumanReadable(long bytes)
|
||||
{
|
||||
if (bytes < 4000) // 1-4 kb is printed in bytes
|
||||
return bytes + " bytes";
|
||||
if (bytes < 1000 * 1000) // 4-999 kb is printed in kb
|
||||
return Math.Round(((double)bytes / 1000.0), 2) + " kilobytes";
|
||||
return Math.Round(((double)bytes / (1000.0 * 1000.0)), 2) + " megabytes"; // else megabytes
|
||||
}
|
||||
|
||||
internal static int RelativeSequenceNumber(int nr, int expected)
|
||||
{
|
||||
int retval = ((nr + NetConstants.NumSequenceNumbers) - expected) % NetConstants.NumSequenceNumbers;
|
||||
if (retval > (NetConstants.NumSequenceNumbers / 2))
|
||||
retval -= NetConstants.NumSequenceNumbers;
|
||||
return retval;
|
||||
}
|
||||
|
||||
// shell sort
|
||||
internal static void SortMembersList(System.Reflection.MemberInfo[] list)
|
||||
{
|
||||
int h;
|
||||
int j;
|
||||
System.Reflection.MemberInfo tmp;
|
||||
|
||||
h = 1;
|
||||
while (h * 3 + 1 <= list.Length)
|
||||
h = 3 * h + 1;
|
||||
|
||||
while (h > 0)
|
||||
{
|
||||
for (int i = h - 1; i < list.Length; i++)
|
||||
{
|
||||
tmp = list[i];
|
||||
j = i;
|
||||
while (true)
|
||||
{
|
||||
if (j >= h)
|
||||
{
|
||||
if (string.Compare(list[j - h].Name, tmp.Name, StringComparison.InvariantCulture) > 0)
|
||||
{
|
||||
list[j] = list[j - h];
|
||||
j -= h;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
list[j] = tmp;
|
||||
}
|
||||
h /= 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Lidgren.Network/SenderChannelBase.cs
Normal file
11
Lidgren.Network/SenderChannelBase.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class SenderChannelBase
|
||||
{
|
||||
internal abstract NetSendResult Send(float now, NetOutgoingMessage message);
|
||||
internal abstract void SendQueuedMessages(float now);
|
||||
internal abstract void Reset();
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{BC0CBAEE-70FE-4B1E-A2FA-BCC731F1E48F}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>BarebonesClient</RootNamespace>
|
||||
<AssemblyName>BarebonesClient</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{FA245447-5F23-4AA1-BD5F-8D2DDF33CFBD}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,72 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace BarebonesClient
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("barebones");
|
||||
#if DEBUG
|
||||
config.SimulatedLoss = 0.1f;
|
||||
#endif
|
||||
config.EnableMessageType(NetIncomingMessageType.VerboseDebugMessage);
|
||||
NetClient client = new NetClient(config);
|
||||
client.Start();
|
||||
|
||||
Thread.Sleep(2000);
|
||||
|
||||
client.Connect("localhost", 14242);
|
||||
|
||||
while (Console.KeyAvailable == false || Console.ReadKey().Key != ConsoleKey.Escape)
|
||||
{
|
||||
NetIncomingMessage inc;
|
||||
while ((inc = client.ReadMessage()) != null)
|
||||
{
|
||||
switch (inc.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)inc.ReadByte();
|
||||
string reason = inc.ReadString();
|
||||
Console.WriteLine("New status: " + status + " (" + reason + ")");
|
||||
if (status == NetConnectionStatus.Connected)
|
||||
{
|
||||
//
|
||||
// We're connected - send stuff
|
||||
//
|
||||
NetOutgoingMessage om = client.CreateMessage();
|
||||
|
||||
// temporary code to verify issue with large messages
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
for (int i = 0; i < 400; i++)
|
||||
bdr.Append("Hallonsmurf" + i.ToString());
|
||||
om.Write(bdr.ToString());
|
||||
|
||||
client.SendMessage(om, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
break;
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
Console.WriteLine(inc.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.UnconnectedData:
|
||||
Console.WriteLine("Received unconnected data from " + inc.SenderEndpoint + ", conn is " + inc.SenderConnection);
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
Console.WriteLine("Received " + inc.LengthBytes + " bytes of data from " + inc.SenderConnection + ", endpoint is " + inc.SenderEndpoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
Console.WriteLine("Application exiting");
|
||||
while (true) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("BarebonesClient")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("BarebonesClient")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("30270c13-84c7-4b20-971f-44adbd45635b")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{438173C5-8E95-4AE1-AAAB-5C1009F05302}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>BarebonesServer</RootNamespace>
|
||||
<AssemblyName>BarebonesServer</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{FA245447-5F23-4AA1-BD5F-8D2DDF33CFBD}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
@@ -1,66 +0,0 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace BarebonesServer
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("barebones");
|
||||
config.Port = 14242;
|
||||
#if DEBUG
|
||||
config.SimulatedLoss = 0.1f;
|
||||
#endif
|
||||
config.EnableMessageType(NetIncomingMessageType.VerboseDebugMessage);
|
||||
NetServer server = new NetServer(config);
|
||||
server.Start();
|
||||
|
||||
NetIncomingMessage inc;
|
||||
while (Console.KeyAvailable == false || Console.ReadKey().Key != ConsoleKey.Escape)
|
||||
{
|
||||
while ((inc = server.ReadMessage()) != null)
|
||||
{
|
||||
switch (inc.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
Console.WriteLine(inc.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)inc.ReadByte();
|
||||
string reason = inc.ReadString();
|
||||
Console.WriteLine("New status: " + status + " (" + reason + ")");
|
||||
break;
|
||||
case NetIncomingMessageType.UnconnectedData:
|
||||
Console.WriteLine("Received unconnected data from " + inc.SenderEndpoint + ", conn is " + inc.SenderConnection);
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
Console.WriteLine("Received " + inc.LengthBytes + " bytes of data from " + inc.SenderConnection + ", endpoint is " + inc.SenderEndpoint);
|
||||
|
||||
// temporary code to verify issue with large messages
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
for (int i = 0; i < 400; i++)
|
||||
bdr.Append("Hallonsmurf" + i.ToString());
|
||||
|
||||
string str = inc.ReadString();
|
||||
|
||||
Console.WriteLine("Compare gives: " + str.CompareTo(bdr.ToString()));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
|
||||
Console.WriteLine("Application exiting");
|
||||
while (true) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("BarebonesServer")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("BarebonesServer")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("f0faed82-6c17-41dc-a45e-c9534a29eff8")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
57
Samples/Chat/Chat.sln
Normal file
57
Samples/Chat/Chat.sln
Normal file
@@ -0,0 +1,57 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 10.00
|
||||
# Visual Studio 2008
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SamplesCommon", "..\SamplesCommon\SamplesCommon.csproj", "{773069DA-B66E-4667-ADCB-0D215AD8CF3E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lidgren.Network", "..\..\Lidgren.Network\Lidgren.Network.csproj", "{AE483C29-042E-4226-BA52-D247CE7676DA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatServer", "ChatServer\ChatServer.csproj", "{C5473E52-7601-48CA-BFEB-1AB012F3D4BF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatClient", "ChatClient\ChatClient.csproj", "{B166F0BF-FB75-41F1-A527-B289BDF0FA0C}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SourceCodeControl) = preSolution
|
||||
SccNumberOfProjects = 5
|
||||
SccProjectName0 = Perforce\u0020Project
|
||||
SccLocalPath0 = ..\\..
|
||||
SccProvider0 = MSSCCI:Perforce\u0020SCM
|
||||
SccProjectFilePathRelativizedFromConnection0 = Samples\\Chat\\
|
||||
SccProjectUniqueName1 = ..\\..\\Lidgren.Network\\Lidgren.Network.csproj
|
||||
SccLocalPath1 = ..\\..
|
||||
SccProjectFilePathRelativizedFromConnection1 = Lidgren.Network\\
|
||||
SccProjectUniqueName2 = ChatClient\\ChatClient.csproj
|
||||
SccLocalPath2 = ..\\..
|
||||
SccProjectFilePathRelativizedFromConnection2 = Samples\\Chat\\ChatClient\\
|
||||
SccProjectUniqueName3 = ChatServer\\ChatServer.csproj
|
||||
SccLocalPath3 = ..\\..
|
||||
SccProjectFilePathRelativizedFromConnection3 = Samples\\Chat\\ChatServer\\
|
||||
SccProjectUniqueName4 = ..\\SamplesCommon\\SamplesCommon.csproj
|
||||
SccLocalPath4 = ..\\..
|
||||
SccProjectFilePathRelativizedFromConnection4 = Samples\\SamplesCommon\\
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{773069DA-B66E-4667-ADCB-0D215AD8CF3E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AE483C29-042E-4226-BA52-D247CE7676DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AE483C29-042E-4226-BA52-D247CE7676DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AE483C29-042E-4226-BA52-D247CE7676DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AE483C29-042E-4226-BA52-D247CE7676DA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C5473E52-7601-48CA-BFEB-1AB012F3D4BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C5473E52-7601-48CA-BFEB-1AB012F3D4BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C5473E52-7601-48CA-BFEB-1AB012F3D4BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C5473E52-7601-48CA-BFEB-1AB012F3D4BF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B166F0BF-FB75-41F1-A527-B289BDF0FA0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B166F0BF-FB75-41F1-A527-B289BDF0FA0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B166F0BF-FB75-41F1-A527-B289BDF0FA0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B166F0BF-FB75-41F1-A527-B289BDF0FA0C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -5,13 +5,17 @@
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{321F68AE-7F97-415E-A3F9-7C477EFF95EE}</ProjectGuid>
|
||||
<ProjectGuid>{B166F0BF-FB75-41F1-A527-B289BDF0FA0C}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ChatClient</RootNamespace>
|
||||
<AssemblyName>ChatClient</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SccProjectName>SAK</SccProjectName>
|
||||
<SccLocalPath>SAK</SccLocalPath>
|
||||
<SccAuxPath>SAK</SccAuxPath>
|
||||
<SccProvider>SAK</SccProvider>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -35,11 +39,8 @@
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.cs">
|
||||
@@ -74,11 +75,11 @@
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{FA245447-5F23-4AA1-BD5F-8D2DDF33CFBD}</Project>
|
||||
<ProjectReference Include="..\..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{AE483C29-042E-4226-BA52-D247CE7676DA}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SamplesCommon\SamplesCommon.csproj">
|
||||
<ProjectReference Include="..\..\SamplesCommon\SamplesCommon.csproj">
|
||||
<Project>{773069DA-B66E-4667-ADCB-0D215AD8CF3E}</Project>
|
||||
<Name>SamplesCommon</Name>
|
||||
</ProjectReference>
|
||||
@@ -29,9 +29,12 @@
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.textBox2 = new System.Windows.Forms.TextBox();
|
||||
this.textBox3 = new System.Windows.Forms.TextBox();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.button3 = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// richTextBox1
|
||||
@@ -39,51 +42,87 @@
|
||||
this.richTextBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.richTextBox1.Location = new System.Drawing.Point(12, 12);
|
||||
this.richTextBox1.Location = new System.Drawing.Point(12, 40);
|
||||
this.richTextBox1.Name = "richTextBox1";
|
||||
this.richTextBox1.Size = new System.Drawing.Size(500, 220);
|
||||
this.richTextBox1.Size = new System.Drawing.Size(502, 156);
|
||||
this.richTextBox1.TabIndex = 0;
|
||||
this.richTextBox1.Text = "";
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button1.Enabled = false;
|
||||
this.button1.Location = new System.Drawing.Point(422, 202);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(92, 23);
|
||||
this.button1.TabIndex = 1;
|
||||
this.button1.Text = "Send";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox1.Location = new System.Drawing.Point(12, 238);
|
||||
this.textBox1.Enabled = false;
|
||||
this.textBox1.Location = new System.Drawing.Point(12, 202);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(321, 22);
|
||||
this.textBox1.TabIndex = 1;
|
||||
this.textBox1.Size = new System.Drawing.Size(404, 22);
|
||||
this.textBox1.TabIndex = 2;
|
||||
//
|
||||
// button1
|
||||
// textBox2
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button1.Location = new System.Drawing.Point(339, 236);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(79, 23);
|
||||
this.button1.TabIndex = 2;
|
||||
this.button1.Text = "Send";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
this.textBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox2.Location = new System.Drawing.Point(12, 12);
|
||||
this.textBox2.Name = "textBox2";
|
||||
this.textBox2.Size = new System.Drawing.Size(192, 22);
|
||||
this.textBox2.TabIndex = 3;
|
||||
this.textBox2.Text = "localhost";
|
||||
//
|
||||
// textBox3
|
||||
//
|
||||
this.textBox3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.textBox3.Location = new System.Drawing.Point(210, 12);
|
||||
this.textBox3.Name = "textBox3";
|
||||
this.textBox3.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
|
||||
this.textBox3.Size = new System.Drawing.Size(108, 22);
|
||||
this.textBox3.TabIndex = 4;
|
||||
this.textBox3.Text = "14242";
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button2.Location = new System.Drawing.Point(424, 236);
|
||||
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button2.Location = new System.Drawing.Point(324, 12);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(88, 23);
|
||||
this.button2.TabIndex = 3;
|
||||
this.button2.Text = "Settings";
|
||||
this.button2.Size = new System.Drawing.Size(92, 23);
|
||||
this.button2.TabIndex = 5;
|
||||
this.button2.Text = "Connect";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||
//
|
||||
// button3
|
||||
//
|
||||
this.button3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button3.Location = new System.Drawing.Point(422, 12);
|
||||
this.button3.Name = "button3";
|
||||
this.button3.Size = new System.Drawing.Size(92, 23);
|
||||
this.button3.TabIndex = 6;
|
||||
this.button3.Text = "Settings";
|
||||
this.button3.UseVisualStyleBackColor = true;
|
||||
this.button3.Click += new System.EventHandler(this.button3_Click);
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(524, 272);
|
||||
this.ClientSize = new System.Drawing.Size(526, 236);
|
||||
this.Controls.Add(this.button3);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.textBox3);
|
||||
this.Controls.Add(this.textBox2);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.richTextBox1);
|
||||
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.Name = "Form1";
|
||||
@@ -95,10 +134,13 @@
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.RichTextBox richTextBox1;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
private System.Windows.Forms.Button button2;
|
||||
public System.Windows.Forms.RichTextBox richTextBox1;
|
||||
public System.Windows.Forms.TextBox textBox2;
|
||||
public System.Windows.Forms.TextBox textBox3;
|
||||
private System.Windows.Forms.Button button3;
|
||||
}
|
||||
}
|
||||
|
||||
70
Samples/Chat/ChatClient/Form1.cs
Normal file
70
Samples/Chat/ChatClient/Form1.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ChatClient
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
textBox1.KeyDown += new KeyEventHandler(textBox1_KeyDown);
|
||||
}
|
||||
|
||||
public void EnableInput()
|
||||
{
|
||||
textBox1.Enabled = true;
|
||||
button1.Enabled = true;
|
||||
}
|
||||
|
||||
public void DisableInput()
|
||||
{
|
||||
textBox1.Enabled = false;
|
||||
button1.Enabled = false;
|
||||
}
|
||||
|
||||
void textBox1_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Return || e.KeyCode == Keys.Enter)
|
||||
{
|
||||
// return is equivalent to clicking "send"
|
||||
button1_Click(sender, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void button3_Click(object sender, EventArgs e)
|
||||
{
|
||||
Program.DisplaySettings();
|
||||
}
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (button2.Text == "Connect")
|
||||
{
|
||||
int port;
|
||||
Int32.TryParse(textBox3.Text, out port);
|
||||
Program.Connect(textBox2.Text, port);
|
||||
button2.Text = "Shut down";
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Shutdown();
|
||||
button2.Text = "Connect";
|
||||
}
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
// Send
|
||||
if (!string.IsNullOrEmpty(textBox1.Text))
|
||||
Program.Send(textBox1.Text);
|
||||
textBox1.Text = "";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
115
Samples/Chat/ChatClient/Program.cs
Normal file
115
Samples/Chat/ChatClient/Program.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
using SamplesCommon;
|
||||
|
||||
namespace ChatClient
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
private static NetClient s_client;
|
||||
private static Form1 s_form;
|
||||
private static NetPeerSettingsWindow s_settingsWindow;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
s_form = new Form1();
|
||||
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("chat");
|
||||
s_client = new NetClient(config);
|
||||
|
||||
Application.Idle += new EventHandler(Application_Idle);
|
||||
|
||||
Application.Run(s_form);
|
||||
}
|
||||
|
||||
private static void Output(string text)
|
||||
{
|
||||
NativeMethods.AppendText(s_form.richTextBox1, text);
|
||||
}
|
||||
|
||||
private static void Application_Idle(object sender, EventArgs e)
|
||||
{
|
||||
while (NativeMethods.AppStillIdle)
|
||||
{
|
||||
NetIncomingMessage im;
|
||||
while ((im = s_client.ReadMessage()) != null)
|
||||
{
|
||||
// handle incoming message
|
||||
switch (im.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
string text = im.ReadString();
|
||||
Output(text);
|
||||
break;
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)im.ReadByte();
|
||||
|
||||
if (status == NetConnectionStatus.Connected)
|
||||
s_form.EnableInput();
|
||||
else
|
||||
s_form.DisableInput();
|
||||
string reason = im.ReadString();
|
||||
Output(status.ToString() + ": " + reason);
|
||||
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
string chat = im.ReadString();
|
||||
Output(chat);
|
||||
break;
|
||||
default:
|
||||
Output("Unhandled type: " + im.MessageType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
// called by the UI
|
||||
public static void Connect(string host, int port)
|
||||
{
|
||||
s_client.Start();
|
||||
NetOutgoingMessage hail = s_client.CreateMessage();
|
||||
hail.Write("This is the hail message");
|
||||
s_client.Connect(host, port, hail);
|
||||
}
|
||||
|
||||
// called by the UI
|
||||
public static void Shutdown()
|
||||
{
|
||||
s_client.Shutdown("Requested by user");
|
||||
}
|
||||
|
||||
public static void Send(string text)
|
||||
{
|
||||
NetOutgoingMessage om = s_client.CreateMessage(text);
|
||||
s_client.SendMessage(om, NetDeliveryMethod.ReliableOrdered);
|
||||
Output("Sending '" + text + "'");
|
||||
}
|
||||
|
||||
// called by the UI
|
||||
public static void DisplaySettings()
|
||||
{
|
||||
if (s_settingsWindow != null && s_settingsWindow.Visible)
|
||||
{
|
||||
s_settingsWindow.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_settingsWindow == null || s_settingsWindow.IsDisposed)
|
||||
s_settingsWindow = new NetPeerSettingsWindow("Chat client settings", s_client);
|
||||
s_settingsWindow.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2438660c-dd5f-45ac-bf17-69ed0b1c7dfa")]
|
||||
[assembly: Guid("87051984-f522-47e0-b5ec-fca02571f83f")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:2.0.50727.4927
|
||||
// Runtime Version:2.0.50727.4952
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:2.0.50727.4927
|
||||
// Runtime Version:2.0.50727.4952
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@@ -5,13 +5,17 @@
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{E2711561-B3C9-4580-B054-891CE54E15EE}</ProjectGuid>
|
||||
<ProjectGuid>{C5473E52-7601-48CA-BFEB-1AB012F3D4BF}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ChatServer</RootNamespace>
|
||||
<AssemblyName>ChatServer</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<SccProjectName>SAK</SccProjectName>
|
||||
<SccLocalPath>SAK</SccLocalPath>
|
||||
<SccAuxPath>SAK</SccAuxPath>
|
||||
<SccProvider>SAK</SccProvider>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -35,11 +39,8 @@
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.cs">
|
||||
@@ -74,11 +75,11 @@
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{FA245447-5F23-4AA1-BD5F-8D2DDF33CFBD}</Project>
|
||||
<ProjectReference Include="..\..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{AE483C29-042E-4226-BA52-D247CE7676DA}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SamplesCommon\SamplesCommon.csproj">
|
||||
<ProjectReference Include="..\..\SamplesCommon\SamplesCommon.csproj">
|
||||
<Project>{773069DA-B66E-4667-ADCB-0D215AD8CF3E}</Project>
|
||||
<Name>SamplesCommon</Name>
|
||||
</ProjectReference>
|
||||
128
Samples/Chat/ChatServer/Form1.Designer.cs
generated
Normal file
128
Samples/Chat/ChatServer/Form1.Designer.cs
generated
Normal file
@@ -0,0 +1,128 @@
|
||||
namespace ChatServer
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.listBox1 = new System.Windows.Forms.ListBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
|
||||
this.splitContainer1.Panel1.SuspendLayout();
|
||||
this.splitContainer1.Panel2.SuspendLayout();
|
||||
this.splitContainer1.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// listBox1
|
||||
//
|
||||
this.listBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.listBox1.FormattingEnabled = true;
|
||||
this.listBox1.Location = new System.Drawing.Point(0, 0);
|
||||
this.listBox1.Name = "listBox1";
|
||||
this.listBox1.Size = new System.Drawing.Size(570, 147);
|
||||
this.listBox1.TabIndex = 0;
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.button1.Location = new System.Drawing.Point(12, 327);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
||||
this.button1.TabIndex = 1;
|
||||
this.button1.Text = "Start";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.button2.Location = new System.Drawing.Point(507, 327);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(75, 23);
|
||||
this.button2.TabIndex = 2;
|
||||
this.button2.Text = "Settings";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||
//
|
||||
// splitContainer1
|
||||
//
|
||||
this.splitContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.splitContainer1.Location = new System.Drawing.Point(12, 12);
|
||||
this.splitContainer1.Name = "splitContainer1";
|
||||
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
|
||||
//
|
||||
// splitContainer1.Panel1
|
||||
//
|
||||
this.splitContainer1.Panel1.Controls.Add(this.listBox1);
|
||||
//
|
||||
// splitContainer1.Panel2
|
||||
//
|
||||
this.splitContainer1.Panel2.Controls.Add(this.richTextBox1);
|
||||
this.splitContainer1.Size = new System.Drawing.Size(570, 309);
|
||||
this.splitContainer1.SplitterDistance = 154;
|
||||
this.splitContainer1.TabIndex = 3;
|
||||
//
|
||||
// richTextBox1
|
||||
//
|
||||
this.richTextBox1.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.richTextBox1.Location = new System.Drawing.Point(0, 0);
|
||||
this.richTextBox1.Name = "richTextBox1";
|
||||
this.richTextBox1.Size = new System.Drawing.Size(570, 151);
|
||||
this.richTextBox1.TabIndex = 0;
|
||||
this.richTextBox1.Text = "";
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(594, 358);
|
||||
this.Controls.Add(this.splitContainer1);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.Name = "Form1";
|
||||
this.Text = "Chat server";
|
||||
this.splitContainer1.Panel1.ResumeLayout(false);
|
||||
this.splitContainer1.Panel2.ResumeLayout(false);
|
||||
this.splitContainer1.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Button button2;
|
||||
private System.Windows.Forms.SplitContainer splitContainer1;
|
||||
public System.Windows.Forms.RichTextBox richTextBox1;
|
||||
public System.Windows.Forms.ListBox listBox1;
|
||||
}
|
||||
}
|
||||
|
||||
35
Samples/Chat/ChatServer/Form1.cs
Normal file
35
Samples/Chat/ChatServer/Form1.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace ChatServer
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (button1.Text == "Start")
|
||||
{
|
||||
Program.StartServer();
|
||||
button1.Text = "Shut down";
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Shutdown();
|
||||
button1.Text = "Start";
|
||||
}
|
||||
}
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
Program.DisplaySettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
126
Samples/Chat/ChatServer/Program.cs
Normal file
126
Samples/Chat/ChatServer/Program.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
using SamplesCommon;
|
||||
|
||||
namespace ChatServer
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
private static Form1 s_form;
|
||||
private static NetServer s_server;
|
||||
private static NetPeerSettingsWindow s_settingsWindow;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
s_form = new Form1();
|
||||
|
||||
// set up network
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("chat");
|
||||
config.MaximumConnections = 100;
|
||||
config.Port = 14242;
|
||||
s_server = new NetServer(config);
|
||||
|
||||
Application.Idle += new EventHandler(Application_Idle);
|
||||
Application.Run(s_form);
|
||||
}
|
||||
|
||||
private static void Output(string text)
|
||||
{
|
||||
NativeMethods.AppendText(s_form.richTextBox1, text);
|
||||
}
|
||||
|
||||
private static void Application_Idle(object sender, EventArgs e)
|
||||
{
|
||||
while (NativeMethods.AppStillIdle)
|
||||
{
|
||||
NetIncomingMessage im;
|
||||
while ((im = s_server.ReadMessage()) != null)
|
||||
{
|
||||
// handle incoming message
|
||||
switch (im.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
string text = im.ReadString();
|
||||
Output(text);
|
||||
break;
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)im.ReadByte();
|
||||
string reason = im.ReadString();
|
||||
Output(NetUtility.ToHexString(im.SenderConnection.RemoteUniqueIdentifier) + " " + status + ": " + reason);
|
||||
|
||||
UpdateConnectionsList();
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
// incoming chat message from a client
|
||||
string chat = im.ReadString();
|
||||
|
||||
Output("Broadcasting '" + chat + "'");
|
||||
|
||||
// broadcast this to all connections, except sender
|
||||
List<NetConnection> all = s_server.Connections; // get copy
|
||||
all.Remove(im.SenderConnection);
|
||||
|
||||
NetOutgoingMessage om = s_server.CreateMessage();
|
||||
om.Write(NetUtility.ToHexString(im.SenderConnection.RemoteUniqueIdentifier) + " said: " + chat);
|
||||
|
||||
s_server.SendMessage(om, all, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
break;
|
||||
default:
|
||||
Output("Unhandled type: " + im.MessageType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateConnectionsList()
|
||||
{
|
||||
s_form.listBox1.Items.Clear();
|
||||
|
||||
foreach (NetConnection conn in s_server.Connections)
|
||||
{
|
||||
string str = NetUtility.ToHexString(conn.RemoteUniqueIdentifier) + " from " + conn.RemoteEndpoint.ToString() + " [" + conn.Status + "]";
|
||||
s_form.listBox1.Items.Add(str);
|
||||
}
|
||||
}
|
||||
|
||||
// called by the UI
|
||||
public static void StartServer()
|
||||
{
|
||||
s_server.Start();
|
||||
}
|
||||
|
||||
// called by the UI
|
||||
public static void Shutdown()
|
||||
{
|
||||
s_server.Shutdown("Requested by user");
|
||||
}
|
||||
|
||||
// called by the UI
|
||||
public static void DisplaySettings()
|
||||
{
|
||||
if (s_settingsWindow != null && s_settingsWindow.Visible)
|
||||
{
|
||||
s_settingsWindow.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_settingsWindow == null || s_settingsWindow.IsDisposed)
|
||||
s_settingsWindow = new NetPeerSettingsWindow("Chat server settings", s_server);
|
||||
s_settingsWindow.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2438660c-dd5f-45ac-bf17-69ed0b1c7dfa")]
|
||||
[assembly: Guid("24166e15-7d4e-43eb-9e7d-da6215da8e0f")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:2.0.50727.4927
|
||||
// Runtime Version:2.0.50727.4952
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:2.0.50727.4927
|
||||
// Runtime Version:2.0.50727.4952
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
@@ -1,49 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using SamplesCommon;
|
||||
|
||||
namespace ChatClient
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
textBox1.KeyDown += new KeyEventHandler(textBox1_KeyDown);
|
||||
}
|
||||
|
||||
void textBox1_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
|
||||
{
|
||||
string txt = textBox1.Text.Trim();
|
||||
Program.Input(txt);
|
||||
textBox1.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (Program.SettingsWindow == null || Program.SettingsWindow.IsDisposed)
|
||||
Program.SettingsWindow = new NetPeerSettingsWindow("Client settings", Program.Client);
|
||||
if (Program.SettingsWindow.Visible)
|
||||
Program.SettingsWindow.Hide();
|
||||
else
|
||||
Program.SettingsWindow.Show();
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
string txt = textBox1.Text.Trim();
|
||||
Program.Input(txt);
|
||||
textBox1.Text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
using SamplesCommon;
|
||||
|
||||
namespace ChatClient
|
||||
{
|
||||
public class ChatMessage
|
||||
{
|
||||
public string Sender;
|
||||
public string Text;
|
||||
}
|
||||
|
||||
static class Program
|
||||
{
|
||||
public static Form1 MainForm;
|
||||
public static NetClient Client;
|
||||
public static NetPeerSettingsWindow SettingsWindow;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
MainForm = new Form1();
|
||||
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("Chat");
|
||||
Client = new NetClient(config);
|
||||
Client.Start();
|
||||
|
||||
Display("Type 'connect <host>' to connect to a server");
|
||||
|
||||
Application.Idle += new EventHandler(AppLoop);
|
||||
Application.Run(MainForm);
|
||||
}
|
||||
|
||||
private static void Display(string text)
|
||||
{
|
||||
NativeMethods.AppendText(MainForm.richTextBox1, text);
|
||||
}
|
||||
|
||||
public static void Input(string input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
return;
|
||||
|
||||
if (input.ToLowerInvariant().StartsWith("connect "))
|
||||
{
|
||||
string host = input.Substring(8).Trim();
|
||||
if (string.IsNullOrEmpty(host))
|
||||
host = "localhost";
|
||||
|
||||
Client.Connect(host, 14242);
|
||||
return;
|
||||
}
|
||||
|
||||
// send chat message
|
||||
ChatMessage cm = new ChatMessage();
|
||||
cm.Sender = Client.UniqueIdentifier.ToString();
|
||||
cm.Text = input;
|
||||
|
||||
NetOutgoingMessage om = Client.CreateMessage();
|
||||
om.WriteAllFields(cm);
|
||||
Client.SendMessage(om, NetDeliveryMethod.ReliableOrdered, 1);
|
||||
}
|
||||
|
||||
static void AppLoop(object sender, EventArgs e)
|
||||
{
|
||||
while (NativeMethods.AppStillIdle)
|
||||
{
|
||||
NetIncomingMessage msg = Client.ReadMessage();
|
||||
if (msg != null)
|
||||
{
|
||||
switch (msg.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
// print any diagnostics message
|
||||
Display(msg.ReadString());
|
||||
break;
|
||||
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
// print changes in connection(s) status
|
||||
NetConnectionStatus status = (NetConnectionStatus)msg.ReadByte();
|
||||
string reason = msg.ReadString();
|
||||
Display("Status: " + status + " (" + reason + ")");
|
||||
|
||||
break;
|
||||
|
||||
case NetIncomingMessageType.Data:
|
||||
|
||||
ChatMessage cm = new ChatMessage();
|
||||
msg.ReadAllFields(cm);
|
||||
|
||||
Display("Received from " + cm.Sender + ": " + cm.Text);
|
||||
break;
|
||||
}
|
||||
Client.Recycle(msg);
|
||||
}
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
Samples/ChatServer/Form1.Designer.cs
generated
77
Samples/ChatServer/Form1.Designer.cs
generated
@@ -1,77 +0,0 @@
|
||||
namespace ChatServer
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// richTextBox1
|
||||
//
|
||||
this.richTextBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.richTextBox1.Location = new System.Drawing.Point(12, 12);
|
||||
this.richTextBox1.Name = "richTextBox1";
|
||||
this.richTextBox1.Size = new System.Drawing.Size(500, 221);
|
||||
this.richTextBox1.TabIndex = 0;
|
||||
this.richTextBox1.Text = "";
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||
this.button1.Location = new System.Drawing.Point(12, 239);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(103, 23);
|
||||
this.button1.TabIndex = 1;
|
||||
this.button1.Text = "Settings";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(524, 272);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.richTextBox1);
|
||||
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.Name = "Form1";
|
||||
this.Text = "Chat server";
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.RichTextBox richTextBox1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using SamplesCommon;
|
||||
|
||||
namespace ChatServer
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (Program.SettingsWindow == null || Program.SettingsWindow.IsDisposed)
|
||||
Program.SettingsWindow = new NetPeerSettingsWindow("Client settings", Program.Server);
|
||||
if (Program.SettingsWindow.Visible)
|
||||
Program.SettingsWindow.Hide();
|
||||
else
|
||||
Program.SettingsWindow.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
using SamplesCommon;
|
||||
|
||||
namespace ChatServer
|
||||
{
|
||||
public class ChatMessage
|
||||
{
|
||||
public string Sender { get; set; }
|
||||
public string Text { get; set; }
|
||||
}
|
||||
|
||||
static class Program
|
||||
{
|
||||
public static Form1 MainForm;
|
||||
public static NetServer Server;
|
||||
public static NetPeerSettingsWindow SettingsWindow;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
MainForm = new Form1();
|
||||
|
||||
// create a configuration
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("Chat");
|
||||
config.Port = 14242;
|
||||
|
||||
// create and start server
|
||||
Server = new NetServer(config);
|
||||
Server.Start();
|
||||
|
||||
Application.Idle += new EventHandler(AppLoop);
|
||||
Application.Run(MainForm);
|
||||
}
|
||||
|
||||
private static void Display(string text)
|
||||
{
|
||||
NativeMethods.AppendText(MainForm.richTextBox1, text);
|
||||
}
|
||||
|
||||
static void AppLoop(object sender, EventArgs e)
|
||||
{
|
||||
while (NativeMethods.AppStillIdle)
|
||||
{
|
||||
NetIncomingMessage msg = Server.WaitMessage(100);
|
||||
if (msg != null)
|
||||
{
|
||||
switch (msg.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
// print any library message
|
||||
Display(msg.ReadString());
|
||||
break;
|
||||
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
// print changes in connection(s) status
|
||||
NetConnectionStatus status = (NetConnectionStatus)msg.ReadByte();
|
||||
string reason = msg.ReadString();
|
||||
Display(msg.SenderConnection + " status: " + status + " (" + reason + ")");
|
||||
|
||||
break;
|
||||
|
||||
case NetIncomingMessageType.Data:
|
||||
|
||||
// read chat message
|
||||
ChatMessage cm = new ChatMessage();
|
||||
msg.ReadAllProperties(cm);
|
||||
|
||||
// Forward all data to all clients (including sender for debugging purposes)
|
||||
NetOutgoingMessage om = Server.CreateMessage();
|
||||
om.WriteAllProperties(cm, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
Display("Forwarding text from " + cm.Sender + " (seqchan " + msg.SequenceChannel + ") to all clients: " + cm.Text);
|
||||
Server.SendMessage(om, Server.Connections, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
|
||||
break;
|
||||
}
|
||||
Server.Recycle(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{0B4B02BB-0F43-4466-A369-0682281AF60E}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DurableClient</RootNamespace>
|
||||
<AssemblyName>DurableClient</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Designer.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{FA245447-5F23-4AA1-BD5F-8D2DDF33CFBD}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SamplesCommon\SamplesCommon.csproj">
|
||||
<Project>{773069DA-B66E-4667-ADCB-0D215AD8CF3E}</Project>
|
||||
<Name>SamplesCommon</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
112
Samples/DurableClient/Form1.Designer.cs
generated
112
Samples/DurableClient/Form1.Designer.cs
generated
@@ -1,112 +0,0 @@
|
||||
namespace DurableClient
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.textBox1 = new System.Windows.Forms.TextBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// richTextBox1
|
||||
//
|
||||
this.richTextBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.richTextBox1.Location = new System.Drawing.Point(12, 218);
|
||||
this.richTextBox1.Name = "richTextBox1";
|
||||
this.richTextBox1.Size = new System.Drawing.Size(493, 124);
|
||||
this.richTextBox1.TabIndex = 0;
|
||||
this.richTextBox1.Text = "";
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(232, 10);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
||||
this.button1.TabIndex = 1;
|
||||
this.button1.Text = "Connect";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// textBox1
|
||||
//
|
||||
this.textBox1.Location = new System.Drawing.Point(12, 12);
|
||||
this.textBox1.Name = "textBox1";
|
||||
this.textBox1.Size = new System.Drawing.Size(214, 22);
|
||||
this.textBox1.TabIndex = 2;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(12, 40);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(38, 13);
|
||||
this.label1.TabIndex = 3;
|
||||
this.label1.Text = "label1";
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Location = new System.Drawing.Point(313, 10);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(75, 23);
|
||||
this.button2.TabIndex = 4;
|
||||
this.button2.Text = "Settings";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
this.button2.Click += new System.EventHandler(this.button2_Click);
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(517, 354);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.textBox1);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.richTextBox1);
|
||||
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.Name = "Form1";
|
||||
this.Text = "Form1";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.RichTextBox richTextBox1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.TextBox textBox1;
|
||||
public System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Button button2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using SamplesCommon;
|
||||
|
||||
namespace DurableClient
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
private NetPeerSettingsWindow m_settingsWindow;
|
||||
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (string.IsNullOrEmpty(textBox1.Text))
|
||||
textBox1.Text = "localhost";
|
||||
|
||||
Program.Connect(textBox1.Text);
|
||||
}
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (m_settingsWindow == null)
|
||||
{
|
||||
m_settingsWindow = new NetPeerSettingsWindow("Durable client settings", Program.Client);
|
||||
m_settingsWindow.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settingsWindow.Close();
|
||||
m_settingsWindow = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,149 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using System.Threading;
|
||||
using Lidgren.Network;
|
||||
using SamplesCommon;
|
||||
using System.Text;
|
||||
|
||||
namespace DurableClient
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
public static Form1 MainForm;
|
||||
public static NetClient Client;
|
||||
|
||||
private static bool s_sendStuff;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
MainForm = new Form1();
|
||||
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("durable");
|
||||
Client = new NetClient(config);
|
||||
Client.Start();
|
||||
|
||||
Application.Idle += new EventHandler(AppLoop);
|
||||
Application.Run(MainForm);
|
||||
|
||||
Client.Shutdown("App exiting");
|
||||
}
|
||||
|
||||
public static void Display(string text)
|
||||
{
|
||||
NativeMethods.AppendText(MainForm.richTextBox1, text);
|
||||
}
|
||||
|
||||
private static double s_nextSendReliableOrdered;
|
||||
private static uint[] s_reliableOrderedNr = new uint[3];
|
||||
|
||||
private static double s_nextSendSequenced;
|
||||
private static uint[] s_sequencedNr = new uint[3];
|
||||
|
||||
private static double s_lastLabelUpdate;
|
||||
private const double kLabelUpdateFrequency = 0.25;
|
||||
|
||||
static void AppLoop(object sender, EventArgs e)
|
||||
{
|
||||
while (NativeMethods.AppStillIdle)
|
||||
{
|
||||
NetIncomingMessage msg;
|
||||
while ((msg = Client.WaitMessage(1)) != null)
|
||||
{
|
||||
switch (msg.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
Display(msg.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
Display("Received data?!");
|
||||
break;
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)msg.ReadByte();
|
||||
string reason = msg.ReadString();
|
||||
Display("New status: " + status + " (" + reason + ")");
|
||||
|
||||
if (status == NetConnectionStatus.Connected)
|
||||
{
|
||||
// go
|
||||
s_sendStuff = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
Client.Recycle(msg);
|
||||
}
|
||||
|
||||
if (s_sendStuff)
|
||||
{
|
||||
double now = NetTime.Now;
|
||||
|
||||
float speed = 1.0f;
|
||||
|
||||
float speedMultiplier = 1.0f / speed;
|
||||
|
||||
int r = NetRandom.Instance.Next(3);
|
||||
if (now > s_nextSendReliableOrdered)
|
||||
{
|
||||
NetOutgoingMessage om = Client.CreateMessage(5);
|
||||
|
||||
uint rv = s_reliableOrderedNr[r];
|
||||
s_reliableOrderedNr[r]++;
|
||||
|
||||
om.Write(rv);
|
||||
|
||||
Client.SendMessage(om, NetDeliveryMethod.ReliableOrdered, r);
|
||||
s_nextSendReliableOrdered = now + (NetRandom.Instance.NextFloat() * (0.01f * speedMultiplier)) + (0.005f * speedMultiplier);
|
||||
}
|
||||
|
||||
if (now > s_nextSendSequenced)
|
||||
{
|
||||
NetOutgoingMessage om = Client.CreateMessage();
|
||||
|
||||
uint v = s_sequencedNr[r];
|
||||
s_sequencedNr[r]++;
|
||||
om.Write(v);
|
||||
Client.SendMessage(om, NetDeliveryMethod.UnreliableSequenced, r);
|
||||
s_nextSendSequenced = now + (NetRandom.Instance.NextFloat() * (0.01f * speedMultiplier)) + (0.005f * speedMultiplier);
|
||||
}
|
||||
|
||||
if (now > s_lastLabelUpdate + kLabelUpdateFrequency)
|
||||
{
|
||||
UpdateLabel();
|
||||
s_lastLabelUpdate = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateLabel()
|
||||
{
|
||||
NetConnection conn = Client.ServerConnection;
|
||||
if (conn != null)
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.Append(Client.Statistics.ToString());
|
||||
bdr.Append(conn.Statistics.ToString());
|
||||
|
||||
bdr.AppendLine("SENT Reliable ordered: " + s_reliableOrderedNr[0] + ", " + s_reliableOrderedNr[1] + ", " + s_reliableOrderedNr[2]);
|
||||
bdr.AppendLine("SENT Sequenced: " + s_sequencedNr[0] + ", " + s_sequencedNr[1] + ", " + s_sequencedNr[2]);
|
||||
bdr.AppendLine("Unsent bytes: " + conn.UnsentBytesCount);
|
||||
MainForm.label1.Text = bdr.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Connect(string host)
|
||||
{
|
||||
NetOutgoingMessage approval = Client.CreateMessage();
|
||||
approval.Write("durableschmurable");
|
||||
|
||||
Client.Connect(host, 14242, approval);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("DurableClient")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("DurableClient")]
|
||||
[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2438660c-dd5f-45ac-bf17-69ed0b1c7dfa")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,71 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:2.0.50727.4927
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace DurableClient.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DurableClient.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,30 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:2.0.50727.4927
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace DurableClient.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
@@ -1,94 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.21022</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{034984CA-FB37-44AF-BBF9-EC58ED75F5F3}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>DurableServer</RootNamespace>
|
||||
<AssemblyName>DurableServer</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Designer.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Project>{FA245447-5F23-4AA1-BD5F-8D2DDF33CFBD}</Project>
|
||||
<Name>Lidgren.Network</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SamplesCommon\SamplesCommon.csproj">
|
||||
<Project>{773069DA-B66E-4667-ADCB-0D215AD8CF3E}</Project>
|
||||
<Name>SamplesCommon</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
89
Samples/DurableServer/Form1.Designer.cs
generated
89
Samples/DurableServer/Form1.Designer.cs
generated
@@ -1,89 +0,0 @@
|
||||
namespace DurableServer
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.richTextBox1 = new System.Windows.Forms.RichTextBox();
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// richTextBox1
|
||||
//
|
||||
this.richTextBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
|
||||
| System.Windows.Forms.AnchorStyles.Left)
|
||||
| System.Windows.Forms.AnchorStyles.Right)));
|
||||
this.richTextBox1.Location = new System.Drawing.Point(12, 229);
|
||||
this.richTextBox1.Name = "richTextBox1";
|
||||
this.richTextBox1.Size = new System.Drawing.Size(543, 167);
|
||||
this.richTextBox1.TabIndex = 0;
|
||||
this.richTextBox1.Text = "";
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(12, 9);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(38, 13);
|
||||
this.label1.TabIndex = 1;
|
||||
this.label1.Text = "label1";
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(480, 200);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(75, 23);
|
||||
this.button1.TabIndex = 2;
|
||||
this.button1.Text = "Settings";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(567, 408);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.label1);
|
||||
this.Controls.Add(this.richTextBox1);
|
||||
this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
|
||||
this.Name = "Form1";
|
||||
this.Text = "Durable server";
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public System.Windows.Forms.RichTextBox richTextBox1;
|
||||
public System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Button button1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Forms;
|
||||
using SamplesCommon;
|
||||
|
||||
namespace DurableServer
|
||||
{
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
private NetPeerSettingsWindow m_settingsWindow;
|
||||
|
||||
public Form1()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (m_settingsWindow == null)
|
||||
{
|
||||
m_settingsWindow = new NetPeerSettingsWindow("Durable server settings", Program.Server);
|
||||
m_settingsWindow.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_settingsWindow.Close();
|
||||
m_settingsWindow = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,166 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Lidgren.Network;
|
||||
|
||||
using SamplesCommon;
|
||||
using System.Text;
|
||||
|
||||
namespace DurableServer
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
public static Form1 MainForm;
|
||||
public static NetServer Server;
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
MainForm = new Form1();
|
||||
|
||||
NetPeerConfiguration config = new NetPeerConfiguration("durable");
|
||||
config.Port = 14242;
|
||||
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
|
||||
Server = new NetServer(config);
|
||||
Server.Start();
|
||||
|
||||
m_expectedReliableOrdered = new uint[3];
|
||||
m_reliableOrderedCorrect = new int[3];
|
||||
m_reliableOrderedErrors = new int[3];
|
||||
|
||||
m_expectedSequenced = new uint[3];
|
||||
m_sequencedCorrect = new int[3];
|
||||
m_sequencedErrors = new int[3];
|
||||
|
||||
Application.Idle += new EventHandler(AppLoop);
|
||||
Application.Run(MainForm);
|
||||
|
||||
Server.Shutdown("App exiting");
|
||||
}
|
||||
|
||||
private static void Display(string text)
|
||||
{
|
||||
NativeMethods.AppendText(MainForm.richTextBox1, text);
|
||||
}
|
||||
|
||||
private static double m_lastLabelUpdate;
|
||||
private const double kLabelUpdateFrequency = 0.25;
|
||||
|
||||
private static uint[] m_expectedReliableOrdered;
|
||||
private static int[] m_reliableOrderedCorrect;
|
||||
private static int[] m_reliableOrderedErrors;
|
||||
|
||||
private static uint[] m_expectedSequenced;
|
||||
private static int[] m_sequencedCorrect;
|
||||
private static int[] m_sequencedErrors;
|
||||
|
||||
static void AppLoop(object sender, EventArgs e)
|
||||
{
|
||||
while (NativeMethods.AppStillIdle)
|
||||
{
|
||||
NetIncomingMessage msg;
|
||||
while ((msg = Server.ReadMessage()) != null)
|
||||
{
|
||||
switch (msg.MessageType)
|
||||
{
|
||||
case NetIncomingMessageType.VerboseDebugMessage:
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
case NetIncomingMessageType.WarningMessage:
|
||||
case NetIncomingMessageType.ErrorMessage:
|
||||
Display(msg.ReadString());
|
||||
break;
|
||||
case NetIncomingMessageType.ConnectionApproval:
|
||||
string ok = msg.ReadString();
|
||||
if (ok == "durableschmurable")
|
||||
msg.SenderConnection.Approve();
|
||||
else
|
||||
msg.SenderConnection.Deny("You didn't say the secret word!");
|
||||
break;
|
||||
case NetIncomingMessageType.StatusChanged:
|
||||
NetConnectionStatus status = (NetConnectionStatus)msg.ReadByte();
|
||||
string reason = msg.ReadString();
|
||||
Display("New status: " + status + " (" + reason + ")");
|
||||
break;
|
||||
case NetIncomingMessageType.Data:
|
||||
uint nr = msg.ReadUInt32();
|
||||
int chan = msg.SequenceChannel;
|
||||
switch (msg.DeliveryMethod)
|
||||
{
|
||||
case NetDeliveryMethod.ReliableOrdered:
|
||||
if (nr != m_expectedReliableOrdered[chan])
|
||||
{
|
||||
//Display("Expected " + m_expectedReliableOrdered[chan] + ", got " + nr);
|
||||
m_reliableOrderedErrors[chan]++;
|
||||
m_expectedReliableOrdered[chan] = nr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_reliableOrderedCorrect[chan]++;
|
||||
m_expectedReliableOrdered[chan]++;
|
||||
}
|
||||
break;
|
||||
case NetDeliveryMethod.UnreliableSequenced:
|
||||
if (nr < m_expectedSequenced[chan])
|
||||
m_sequencedErrors[chan]++;
|
||||
else
|
||||
m_sequencedCorrect[chan]++;
|
||||
m_expectedSequenced[chan] = nr + 1;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Bad NetDeliveryMethod: " + msg.DeliveryMethod);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Server.Recycle(msg);
|
||||
}
|
||||
Thread.Sleep(0);
|
||||
|
||||
double now = NetTime.Now;
|
||||
if (now > m_lastLabelUpdate + kLabelUpdateFrequency)
|
||||
{
|
||||
UpdateLabel();
|
||||
m_lastLabelUpdate = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateLabel()
|
||||
{
|
||||
if (Server.ConnectionsCount < 1)
|
||||
{
|
||||
// don't update! Keep old...
|
||||
const string oldData = "(Note: OLD DATA - NO CONNECTIONS NOW)";
|
||||
if (!MainForm.label1.Text.EndsWith(oldData))
|
||||
MainForm.label1.Text += oldData;
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.Append(Server.Statistics.ToString());
|
||||
bdr.Append(Server.Connections[0].Statistics.ToString());
|
||||
bdr.AppendLine("RECEIVED Reliable ordered: " +
|
||||
m_reliableOrderedCorrect[0] + ", " +
|
||||
m_reliableOrderedCorrect[1] + ", " +
|
||||
m_reliableOrderedCorrect[2] +
|
||||
" received; " +
|
||||
m_reliableOrderedErrors[0] + ", " +
|
||||
m_reliableOrderedErrors[1] + ", " +
|
||||
m_reliableOrderedErrors[2] +
|
||||
" errors");
|
||||
bdr.AppendLine("RECEIVED Sequenced: " +
|
||||
m_sequencedCorrect[0] + ", " +
|
||||
m_sequencedCorrect[1] + ", " +
|
||||
m_sequencedCorrect[2] +
|
||||
" received; " +
|
||||
m_sequencedErrors[0] + ", " +
|
||||
m_sequencedErrors[1] + ", " +
|
||||
m_sequencedErrors[2] +
|
||||
" errors");
|
||||
MainForm.label1.Text = bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user