You've already forked lidgren-network-gen3
mirror of
https://github.com/lidgren/lidgren-network-gen3.git
synced 2026-05-16 07:06:30 +09:00
transferred from trunk/Generation3 of lidgren-network
This commit is contained in:
10
Lidgren.Network/Documentation/ChangedFromV2.txt
Normal file
10
Lidgren.Network/Documentation/ChangedFromV2.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
* The NetBuffer object is gone; instead there are NetOutgoingMessage and NetIncomingMessage objects
|
||||
|
||||
* No need to allocate a read buffer before calling ReadMessage
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
113
Lidgren.Network/Documentation/Discovery.html
Normal file
113
Lidgren.Network/Documentation/Discovery.html
Normal file
@@ -0,0 +1,113 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
body
|
||||
{
|
||||
font-family: Verdana, Geneva, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin-left: 20px;
|
||||
background-color: #dddddd;
|
||||
}
|
||||
td
|
||||
{
|
||||
font-family: Verdana, Geneva, Arial, sans-serif;
|
||||
font-size: small;
|
||||
}
|
||||
.page
|
||||
{
|
||||
width: 700px;
|
||||
}
|
||||
.cf
|
||||
{
|
||||
font-family: Courier New;
|
||||
font-size: 10pt;
|
||||
color: black;
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
.cl
|
||||
{
|
||||
margin: 0px;
|
||||
}
|
||||
.cb1
|
||||
{
|
||||
color: green;
|
||||
}
|
||||
.cb2
|
||||
{
|
||||
color: #2b91af;
|
||||
}
|
||||
.cb3
|
||||
{
|
||||
color: blue;
|
||||
}
|
||||
.cb4
|
||||
{
|
||||
color: #a31515;
|
||||
}
|
||||
</style>
|
||||
<title>Peer/server discovery</title>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="page">
|
||||
<h1>Peer/server discovery</h1>
|
||||
<p>
|
||||
Peer discovery is the process of clients detecting what servers are available. Discovery requests can be made in two ways;
|
||||
locally as a broadcast, which will send a signal to all peers on your subnet. Secondly you can contact an ip address directly
|
||||
and query it if a server is running.
|
||||
</p>
|
||||
<p>Responding to discovery requests are done in the same way regardless of how the request is made.</p>
|
||||
|
||||
<p>Here's how to do on the client side; ie. the side which makes a request:</p>
|
||||
<div class="cf">
|
||||
<pre class="cl"><span class="cb1">// Enable DiscoveryResponse messages</span></pre>
|
||||
<pre class="cl">config.EnableMessageType(<span class="cb2">NetIncomingMessageType</span>.DiscoveryResponse);</pre>
|
||||
<pre class="cl"> </pre>
|
||||
<pre class="cl"><span class="cb1">// Emit a discovery signal</span></pre>
|
||||
<pre class="cl">Client.DiscoverLocalPeers(14242);</pre>
|
||||
</div>
|
||||
|
||||
<p>This will send a discovery signal to your subnet; Here's how to receive the signal on the server side, and send a response back to the client:</p>
|
||||
|
||||
<div class="cf">
|
||||
<pre class="cl"><span class="cb1">// Enable DiscoveryRequest messages</span></pre>
|
||||
<pre class="cl">config.EnableMessageType(<span class="cb2">NetIncomingMessageType</span>.DiscoveryRequest);</pre>
|
||||
<pre class="cl"> </pre>
|
||||
<pre class="cl"><span class="cb1">// Standard message reading loop</span></pre>
|
||||
<pre class="cl"><span class="cb3">while</span> ((inc = Server.ReadMessage()) != <span class="cb1">null</span>)</pre>
|
||||
<pre class="cl">{</pre>
|
||||
<pre class="cl"> <span class="cb3">switch</span> (inc.MessageType)</pre>
|
||||
<pre class="cl"> {</pre>
|
||||
<pre class="cl"> <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DiscoveryRequest:</pre>
|
||||
<pre class="cl"> </pre>
|
||||
<pre class="cl"> <span class="cb1">// Create a response and write some example data to it</span></pre>
|
||||
<pre class="cl"> <span class="cb2">NetOutgoingMessage</span> response = Server.CreateMessage();</pre>
|
||||
<pre class="cl"> response.Write(<span class="cb4">"My server name"</span>);</pre>
|
||||
<pre class="cl"> </pre>
|
||||
<pre class="cl"> <span class="cb1">// Send the response to the sender of the request</span></pre>
|
||||
<pre class="cl"> Server.SendDiscoveryResponse(response, inc.SenderEndpoint);</pre>
|
||||
<pre class="cl"> <span class="cb3">break</span>;</pre>
|
||||
</div>
|
||||
|
||||
<p>When the response then reaches the client, you can read the data you wrote on the server:</p>
|
||||
|
||||
<div class="cf">
|
||||
<pre class="cl"><span class="cb1">// Standard message reading loop</span></pre>
|
||||
<pre class="cl"><span class="cb3">while</span> ((inc = Client.ReadMessage()) != <span class="cb1">null</span>)</pre>
|
||||
<pre class="cl">{</pre>
|
||||
<pre class="cl"> <span class="cb3">switch</span> (inc.MessageType)</pre>
|
||||
<pre class="cl"> {</pre>
|
||||
<pre class="cl"> <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DiscoveryResponse:</pre>
|
||||
<pre class="cl"> </pre>
|
||||
<pre class="cl"> <span class="cb2">Console</span>.WriteLine(<span class="cb4">"Found server at "</span> + inc.SenderEndpoint + <span class="cb4">" name: "</span> + inc.ReadString());</pre>
|
||||
<pre class="cl"> <span class="cb3">break</span>;</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
22
Lidgren.Network/Documentation/Improvements.txt
Normal file
22
Lidgren.Network/Documentation/Improvements.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
Improvements over last version of library:
|
||||
|
||||
* New delivery type: Reliable sequenced (Lost packets are resent but late arrivals are dropped)
|
||||
* Disconnects and shutdown requests are now queued properly, so calling shutdown will still send any queued messages before shutting down
|
||||
* All messages are pooled/recycled for zero garbage
|
||||
* Reduced CPU usage and lower latencies (in the <1 ms range, but still) due to better socket polling
|
||||
* All public members of NetPeer/NetConnection are completely thread safe
|
||||
* Larger number of delivery channels
|
||||
* More exact roundtrip measurement
|
||||
* Method serialize entire objects via reflection
|
||||
* Unique identifier now exists for all peers/connections
|
||||
* More flexible peer discovery; filters possible and arbitrary data can be sent with response
|
||||
* Much better protection against malformed messages crashing the app
|
||||
|
||||
API enhancements:
|
||||
* NetPeerConfiguration immutable properties now locked once NetPeer is initialized
|
||||
* Messages cannot be send twice by accident
|
||||
* Impossible to confuse sending and receiving buffers since they're different classes
|
||||
* No more confusion if user should create a buffer or preallocate and reuse
|
||||
|
||||
|
||||
17
Lidgren.Network/Documentation/PacketLayout.txt
Normal file
17
Lidgren.Network/Documentation/PacketLayout.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
PER MESSAGE:
|
||||
7 bits - NetMessageType
|
||||
1 bit - Is a message fragment?
|
||||
|
||||
[8 bits NetMessageLibraryType, if NetMessageType == Library]
|
||||
|
||||
[16 bits sequence number, if NetMessageType >= UserSequenced]
|
||||
|
||||
8/16 bits - Payload length in bits (variable size ushort)
|
||||
|
||||
[16 bits fragments group id, if fragmented]
|
||||
[16 bits fragments total count, if fragmented]
|
||||
[16 bits fragment number, if fragmented]
|
||||
|
||||
[x - Payload] if length > 0
|
||||
|
||||
115
Lidgren.Network/Documentation/SimulatingBadNetwork.html
Normal file
115
Lidgren.Network/Documentation/SimulatingBadNetwork.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
body
|
||||
{
|
||||
font-family: Verdana, Geneva, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin-left: 20px;
|
||||
background-color: #dddddd;
|
||||
}
|
||||
td
|
||||
{
|
||||
font-family: Verdana, Geneva, Arial, sans-serif;
|
||||
font-size: small;
|
||||
}
|
||||
.cf
|
||||
{
|
||||
font-family: Courier New;
|
||||
font-size: 10pt;
|
||||
color: black;
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
.cl
|
||||
{
|
||||
margin: 0px;
|
||||
}
|
||||
.cb1
|
||||
{
|
||||
color: green;
|
||||
}
|
||||
.cb2
|
||||
{
|
||||
color: #2b91af;
|
||||
}
|
||||
.cb3
|
||||
{
|
||||
color: blue;
|
||||
}
|
||||
.cb4
|
||||
{
|
||||
color: #a31515;
|
||||
}
|
||||
</style>
|
||||
<title>Lidgren tutorial</title>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td width="700">
|
||||
<h1>Simulating bad network conditions</h1>
|
||||
<p>
|
||||
On the internet, your packets are likely to run in to all kinds of trouble. They will be delayed and lost and they might even arrive multiple times at the destination. Lidgren has a few option to simulate how your application or game will react when this happens.<br />
|
||||
They are all configured using the NetPeerConfiguration class - these properties exists:</p>
|
||||
<p>
|
||||
<div class="cf">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<b>SimulatedLoss</b>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td valign="top">
|
||||
This is a float which simulates lost packets. A value of 0 will disable this feature, a value of 0.5f will make half of your sent packets disappear, chosen randomly. Note that packets may contain several messages - this is the amount of packets lost.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<b>SimulatedDuplicatesChance</b>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td valign="top">
|
||||
This is a float which determines the chance that a packet will be duplicated at the destination. 0 means no packets will be duplicated, 0.5f means that on average, every other packet will be duplicated.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<b>SimulatedMinimumLatency</b><br />
|
||||
<b>SimulatedRandomLatency</b>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td valign="top">
|
||||
These two properties control simulating delay of packets in seconds (not milliseconds, use 0.05 for 50 ms of lag). They work on top of the actual network delay and the total delay will be:<br />
|
||||
Actual one way latency + SimulatedMinimumLatency + [Randomly per packet 0 to SimulatedRandomLatency seconds]
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
<p>It's recommended to assume symmetric condtions and configure server and client with the same simulation settings.</p>
|
||||
<p>Simulating bad network conditions only works in DEBUG builds.</p>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
17
Lidgren.Network/Documentation/TODO.txt
Normal file
17
Lidgren.Network/Documentation/TODO.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
Completed features:
|
||||
* Message coalescing
|
||||
* Peer, connection statistics
|
||||
* Lag, loss and duplication simulation for testing
|
||||
* Connection approval
|
||||
* Throttling
|
||||
* Clock synchronization to detect jitter per packet (NetTime.RemoteNow)
|
||||
* Peer discovery
|
||||
* Message fragmentation
|
||||
|
||||
Missing features:
|
||||
* Receipts 25% done, need design
|
||||
* More realistic lag/loss (lumpy)
|
||||
* Detect estimated packet loss
|
||||
* More advanced ack packet
|
||||
|
||||
206
Lidgren.Network/Documentation/Tutorial.html
Normal file
206
Lidgren.Network/Documentation/Tutorial.html
Normal file
@@ -0,0 +1,206 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css">
|
||||
body
|
||||
{
|
||||
font-family: Verdana, Geneva, Arial, sans-serif;
|
||||
font-size: small;
|
||||
margin-left: 20px;
|
||||
background-color: #dddddd;
|
||||
}
|
||||
td
|
||||
{
|
||||
font-family: Verdana, Geneva, Arial, sans-serif;
|
||||
font-size: small;
|
||||
}
|
||||
.cf
|
||||
{
|
||||
font-family: Courier New;
|
||||
font-size: 10pt;
|
||||
color: black;
|
||||
background: white;
|
||||
padding: 16px;
|
||||
border: 1px solid black;
|
||||
}
|
||||
.cl
|
||||
{
|
||||
margin: 0px;
|
||||
}
|
||||
.cb1
|
||||
{
|
||||
color: green;
|
||||
}
|
||||
.cb2
|
||||
{
|
||||
color: #2b91af;
|
||||
}
|
||||
.cb3
|
||||
{
|
||||
color: blue;
|
||||
}
|
||||
.cb4
|
||||
{
|
||||
color: #a31515;
|
||||
}
|
||||
</style>
|
||||
<title>Lidgren basics tutorial</title>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td width="700">
|
||||
<h1>
|
||||
Lidgren basics</h1>
|
||||
<p>
|
||||
Lidgren network library is all about messages. There are two types of messages:</p>
|
||||
<li>Library messages telling you things like a peer has connected or error messages when unexpected things happen.</li>
|
||||
<li>Data messages which is data sent from a remote (connected or unconnected) peer.</li>
|
||||
<p>
|
||||
The base class for establishing connections, receiving and sending message are the NetPeer class. Using it you can make a peer-to-peer network, but if you are creating a server/client topology there are special classes called NetServer and NetClient. They inherit NetPeer but sets some defaults and includes some helper methods/properties.</p>
|
||||
<p>
|
||||
Here's how to set up a NetServer:</p>
|
||||
<div class="cf">
|
||||
<pre class="cl"><span class="cb2">NetPeerConfiguration</span> config = <span class="cb3">new</span> <span class="cb2">NetPeerConfiguration</span>(<span class="cb4">"MyExampleName"</span>);</pre>
|
||||
<pre class="cl">config.Port = 14242;</pre>
|
||||
<pre class="cl"> </pre>
|
||||
<pre class="cl"><span class="cb2">NetServer</span> server = <span class="cb3">new</span> <span class="cb2">NetServer</span>(config);</pre>
|
||||
<pre class="cl">server.Start();</pre>
|
||||
</div>
|
||||
<p>
|
||||
The code above first creates a configuration. It has lots of properties you can change, but the default values should be pretty good for most applications. The string you provide in the constructor (MyExampleName) is an identifier to distinquish it from other applications using the lidgren library. Just make sure you use the same string in both server and client - or you will be unable to communicate between them.</p>
|
||||
<p>
|
||||
Secondly we've set the local port the server should listen to. This is the port number we tell the client(s) what port number to connect to. The local port can be set for a client too, but it's not needed and not recommended.</p>
|
||||
<p>
|
||||
Thirdly we create our server object and fourth we Start() it. Starting the server will create a new network thread and bind to a socket and start listening for connections.</p>
|
||||
<p>
|
||||
Early on we spoke about messages; now is the time to start receiving and sending some. Here's a code snippet for receiving messages:</p>
|
||||
<div class="cf">
|
||||
<pre class="cl"><span class="cb2">NetIncomingMessage</span> msg;</pre>
|
||||
<pre class="cl"><span class="cb3">while</span> ((msg = server.ReadMessage()) != <span class="cb3">null</span>)</pre>
|
||||
<pre class="cl">{</pre>
|
||||
<pre class="cl"> <span class="cb3">switch</span> (msg.MessageType)</pre>
|
||||
<pre class="cl"> {</pre>
|
||||
<pre class="cl"> <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.VerboseDebugMessage:</pre>
|
||||
<pre class="cl"> <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.DebugMessage:</pre>
|
||||
<pre class="cl"> <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.WarningMessage:</pre>
|
||||
<pre class="cl"> <span class="cb3">case</span> <span class="cb2">NetIncomingMessageType</span>.ErrorMessage:</pre>
|
||||
<pre class="cl"> <span class="cb2">Console</span>.WriteLine(msg.ReadString());</pre>
|
||||
<pre class="cl"> <span class="cb3">break</span>;</pre>
|
||||
<pre class="cl"> <span class="cb3">default</span>:</pre>
|
||||
<pre class="cl"> <span class="cb2">Console</span>.WriteLine(<span class="cb4">"Unhandled type: "</span> + msg.MessageType);</pre>
|
||||
<pre class="cl"> <span class="cb3">break</span>;</pre>
|
||||
<pre class="cl"> }</pre>
|
||||
<pre class="cl"> server.Recycle(msg);</pre>
|
||||
<pre class="cl">}</pre>
|
||||
</div>
|
||||
<p>
|
||||
So, lets dissect the above code. First we declare a NetIncomingMessage, which is the type of incoming messages. Then we read a message and handles it, looping back as long as there are messages to fetch. For each message we find, we switch on sometime called MessageType - it's a description what the message contains. In this code example we only catch messages of type VerboseDebugMessage, DebugMessage, WarningMessage and ErrorMessage. All those four types are emitted by the library to inform about various events. They all contains a single string, so we use the method ReadString() to extract a copy of that string and print it in the console.</p>
|
||||
<p>
|
||||
Reading data will increment the internal message pointer so you can read subsequent data using the Read*() methods.</p>
|
||||
<p>
|
||||
For all other message type we just print that it's currently unhandled.</p>
|
||||
<p>
|
||||
Finally, we recycle the message after we're done with it - this will enable the library to reuse the object and create less garbage.</p>
|
||||
<p>
|
||||
Sending messages are even easier:</p>
|
||||
<div class="cf">
|
||||
<pre class="cl"><span class="cb2">NetOutgoingMessage</span> sendMsg = server.CreateMessage();</pre>
|
||||
<pre class="cl">sendMsg.Write(<span class="cb4">"Hello"</span>);</pre>
|
||||
<pre class="cl">sendMsg.Write(42);</pre>
|
||||
<pre class="cl"> </pre>
|
||||
<pre class="cl">server.SendMessage(sendMsg, recipient, <span class="cb2">NetDeliveryMethod</span>.ReliableOrdered);</pre>
|
||||
</div>
|
||||
<p>
|
||||
The above code first creates a new message, or uses a recycled message, which is why it's not possible to just create a message using new(). It then writes a string ("Hello") and an integer (System.Int32, 4 bytes in size) to the message.</p>
|
||||
<p>
|
||||
Then the message is sent using the SendMessage() method. The first argument is the message to send, the second argument is the recipient connection - which we'll not go into detail about just yet - and the third argument are HOW to deliver the message, or rather how to behave if network conditions are bad and a packet gets lost, duplicated or reordered.</p>
|
||||
<p>
|
||||
There are five delivery methods available:</p>
|
||||
<div class="cf">
|
||||
<table>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<b>Unreliable</b>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td valign="top">
|
||||
This is just UDP. Messages can be lost, received more than once and messages sent after other messages may be received before them.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<b>UnreliableSequenced</b>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td valign="top">
|
||||
Using this delivery method messages can still be lost; but you're protected against duplicated messages and if a message arrives late; that is, if a message sent after this one has already been received - it will be dropped. This means you will never receive "older" data than what you already have received.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<b>ReliableUnordered</b>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td valign="top">
|
||||
This delivery method ensures that every message sent will be received eventually. It does not however guarantee what order they will be received; late messages may be delivered before older ones.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top">
|
||||
<b>ReliableSequenced</b>
|
||||
</td>
|
||||
<td>
|
||||
|
||||
</td>
|
||||
<td valign="top">
|
||||
This delivery method is similar to UnreliableSequenced; except that is guarantees that SOME messages will be received - if you only send one message - it will be received. If you sent two messages quickly, and they get reordered in transit, only the newest message will be received - but at least ONE of them will be received guaranteed.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td valign="top"><b>ReliableOrdered</b></td>
|
||||
<td> </td>
|
||||
<td valign="top">
|
||||
This delivery method guarantees that messages will always be received in the exact order they were sent.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<p>
|
||||
Here's how to read and decode the message above:</p>
|
||||
<div class="cf">
|
||||
<pre class="cl"><span class="cb2">NetIncomingMessage</span> incMsg = server.ReadMessage();</pre>
|
||||
<pre class="cl"><span class="cb3">string</span> str = incMsg.ReadString();</pre>
|
||||
<pre class="cl"><span class="cb3">int</span> a = incMsg.ReadInt32();</pre>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
105
Lidgren.Network/Lidgren.Network.csproj
Normal file
105
Lidgren.Network/Lidgren.Network.csproj
Normal file
@@ -0,0 +1,105 @@
|
||||
<?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>{FA245447-5F23-4AA1-BD5F-8D2DDF33CFBD}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Lidgren.Network</RootNamespace>
|
||||
<AssemblyName>Lidgren.Network</AssemblyName>
|
||||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</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.Xml.Linq">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data.DataSetExtensions">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Documentation\Improvements.txt" />
|
||||
<Compile Include="NetBigInteger.cs" />
|
||||
<Compile Include="NetBitVector.cs" />
|
||||
<Compile Include="NetConnectionStatistics.cs" />
|
||||
<Compile Include="NetBitWriter.cs" />
|
||||
<Compile Include="NetClient.cs" />
|
||||
<Compile Include="NetConnection.cs" />
|
||||
<Compile Include="NetConnection.Handshake.cs" />
|
||||
<Compile Include="NetConnection.Latency.cs" />
|
||||
<Compile Include="NetConnection.Reliability.cs" />
|
||||
<Compile Include="NetConnectionStatus.cs" />
|
||||
<Compile Include="NetConstants.cs" />
|
||||
<Compile Include="NetException.cs" />
|
||||
<Compile Include="NetFragmentationInfo.cs" />
|
||||
<Compile Include="NetIncomingMessage.cs" />
|
||||
<Compile Include="NetIncomingMessage.Peek.cs" />
|
||||
<Compile Include="NetIncomingMessage.Read.cs" />
|
||||
<Compile Include="NetIncomingMessage.Write.cs" />
|
||||
<Compile Include="NetIncomingMessageType.cs" />
|
||||
<Compile Include="NetMessageType.cs" />
|
||||
<Compile Include="NetOutgoingMessage.cs" />
|
||||
<Compile Include="NetOutgoingMessage.Write.cs" />
|
||||
<Compile Include="NetPeer.ConnectionApproval.cs" />
|
||||
<Compile Include="NetPeer.cs" />
|
||||
<Compile Include="NetPeer.Discovery.cs" />
|
||||
<Compile Include="NetPeer.Internal.cs" />
|
||||
<Compile Include="NetPeer.LatencySimulation.cs" />
|
||||
<Compile Include="NetPeer.Logging.cs" />
|
||||
<Compile Include="NetPeer.Recycling.cs" />
|
||||
<Compile Include="NetPeerConfiguration.cs" />
|
||||
<Compile Include="NetPeerStatistics.cs" />
|
||||
<Compile Include="NetPeerStatus.cs" />
|
||||
<Compile Include="NetQueue.cs" />
|
||||
<Compile Include="NetRandom.cs" />
|
||||
<Compile Include="NetServer.cs" />
|
||||
<Compile Include="NetTime.cs" />
|
||||
<Compile Include="NetUtility.cs" />
|
||||
<Compile Include="NetEncryption.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Documentation\ChangedFromV2.txt" />
|
||||
<Content Include="Documentation\Discovery.html" />
|
||||
<Content Include="Documentation\PacketLayout.txt" />
|
||||
<Content Include="Documentation\SimulatingBadNetwork.html" />
|
||||
<Content Include="Documentation\TODO.txt" />
|
||||
<Content Include="Documentation\Tutorial.html" />
|
||||
</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>
|
||||
2106
Lidgren.Network/NetBigInteger.cs
Normal file
2106
Lidgren.Network/NetBigInteger.cs
Normal file
File diff suppressed because it is too large
Load Diff
73
Lidgren.Network/NetBitVector.cs
Normal file
73
Lidgren.Network/NetBitVector.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public sealed class NetBitVector
|
||||
{
|
||||
private int m_capacity;
|
||||
private uint[] m_data;
|
||||
|
||||
public int Capacity { get { return m_capacity; } }
|
||||
|
||||
public NetBitVector(int bitsCapacity)
|
||||
{
|
||||
m_capacity = bitsCapacity;
|
||||
m_data = new uint[(bitsCapacity + 31) / 32];
|
||||
}
|
||||
|
||||
public bool IsEmpty()
|
||||
{
|
||||
foreach (uint v in m_data)
|
||||
if (v != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int GetFirstSetIndex()
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
uint data = m_data[0];
|
||||
while (data == 0)
|
||||
{
|
||||
idx++;
|
||||
data = m_data[idx];
|
||||
}
|
||||
|
||||
int a = 0;
|
||||
while (((data >> a) & 1) == 0)
|
||||
a++;
|
||||
|
||||
return (idx * 32) + a;
|
||||
}
|
||||
|
||||
public bool Get(int bitIndex)
|
||||
{
|
||||
int idx = bitIndex / 32;
|
||||
uint data = m_data[idx];
|
||||
int bitNr = bitIndex - (idx * 32);
|
||||
return (data & (1 << bitNr)) != 0;
|
||||
}
|
||||
|
||||
public void Set(int bitIndex, bool value)
|
||||
{
|
||||
int idx = bitIndex / 32;
|
||||
int bitNr = bitIndex - (idx * 32);
|
||||
if (value)
|
||||
m_data[idx] |= (uint)(1 << bitNr);
|
||||
else
|
||||
m_data[idx] &= (uint)(~(1 << bitNr));
|
||||
}
|
||||
|
||||
public bool this [int index]
|
||||
{
|
||||
get { return Get(index); }
|
||||
set { Set(index, value); }
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(m_data, 0, m_data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
432
Lidgren.Network/NetBitWriter.cs
Normal file
432
Lidgren.Network/NetBitWriter.cs
Normal file
@@ -0,0 +1,432 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper class for NetBuffer to write/read bits
|
||||
/// </summary>
|
||||
public static class NetBitWriter
|
||||
{
|
||||
/// <summary>
|
||||
/// Read 1-8 bits from a buffer into a byte
|
||||
/// </summary>
|
||||
public static byte ReadByte(byte[] fromBuffer, int numberOfBits, int readBitOffset)
|
||||
{
|
||||
NetException.Assert(((numberOfBits > 0) && (numberOfBits < 9)), "Read() can only read between 1 and 8 bits");
|
||||
|
||||
int bytePtr = readBitOffset >> 3;
|
||||
int startReadAtIndex = readBitOffset - (bytePtr * 8); // (readBitOffset % 8);
|
||||
|
||||
if (startReadAtIndex == 0 && numberOfBits == 8)
|
||||
return fromBuffer[bytePtr];
|
||||
|
||||
// mask away unused bits lower than (right of) relevant bits in first byte
|
||||
byte returnValue = (byte)(fromBuffer[bytePtr] >> startReadAtIndex);
|
||||
|
||||
int numberOfBitsInSecondByte = numberOfBits - (8 - startReadAtIndex);
|
||||
|
||||
if (numberOfBitsInSecondByte < 1)
|
||||
{
|
||||
// we don't need to read from the second byte, but we DO need
|
||||
// to mask away unused bits higher than (left of) relevant bits
|
||||
return (byte)(returnValue & (255 >> (8 - numberOfBits)));
|
||||
}
|
||||
|
||||
byte second = fromBuffer[bytePtr + 1];
|
||||
|
||||
// mask away unused bits higher than (left of) relevant bits in second byte
|
||||
second &= (byte)(255 >> (8 - numberOfBitsInSecondByte));
|
||||
|
||||
return (byte)(returnValue | (byte)(second << (numberOfBits - numberOfBitsInSecondByte)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read several bytes from a buffer
|
||||
/// </summary>
|
||||
public static void ReadBytes(byte[] fromBuffer, int numberOfBytes, int readBitOffset, byte[] destination, int destinationByteOffset)
|
||||
{
|
||||
int readPtr = readBitOffset >> 3;
|
||||
int startReadAtIndex = readBitOffset - (readPtr * 8); // (readBitOffset % 8);
|
||||
|
||||
if (startReadAtIndex == 0)
|
||||
{
|
||||
for (int i = 0; i < numberOfBytes; i++)
|
||||
destination[destinationByteOffset++] = fromBuffer[readPtr++];
|
||||
return;
|
||||
}
|
||||
|
||||
int secondPartLen = 8 - startReadAtIndex;
|
||||
int secondMask = 255 >> secondPartLen;
|
||||
|
||||
for (int i = 0; i < numberOfBytes; i++)
|
||||
{
|
||||
// mask away unused bits lower than (right of) relevant bits in byte
|
||||
int b = fromBuffer[readPtr] >> startReadAtIndex;
|
||||
|
||||
readPtr++;
|
||||
|
||||
// mask away unused bits higher than (left of) relevant bits in second byte
|
||||
int second = fromBuffer[readPtr] & secondMask;
|
||||
|
||||
destination[destinationByteOffset++] = (byte)(b | (second << secondPartLen));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte consisting of 1-8 bits to a buffer; assumes buffer is previously allocated
|
||||
/// </summary>
|
||||
public static void WriteByte(byte source, int numberOfBits, byte[] destination, int destBitOffset)
|
||||
{
|
||||
NetException.Assert(((numberOfBits >= 1) && (numberOfBits <= 8)), "Must write between 1 and 8 bits!");
|
||||
|
||||
// mask out unwanted bits in the source
|
||||
byte isrc = (byte)((uint)source & ((~(uint)0) >> (8 - numberOfBits)));
|
||||
|
||||
int bytePtr = destBitOffset >> 3;
|
||||
|
||||
int localBitLen = (destBitOffset % 8);
|
||||
if (localBitLen == 0)
|
||||
{
|
||||
destination[bytePtr] = (byte)isrc;
|
||||
return;
|
||||
}
|
||||
|
||||
//destination[bytePtr] &= (byte)(255 >> (8 - localBitLen)); // clear before writing
|
||||
//destination[bytePtr] |= (byte)(isrc << localBitLen); // write first half
|
||||
destination[bytePtr] = (byte)(
|
||||
(uint)(destination[bytePtr] & (255 >> (8 - localBitLen))) |
|
||||
(uint)(isrc << localBitLen)
|
||||
);
|
||||
|
||||
// need write into next byte?
|
||||
if (localBitLen + numberOfBits > 8)
|
||||
{
|
||||
//destination[bytePtr + 1] &= (byte)(255 << localBitLen); // clear before writing
|
||||
//destination[bytePtr + 1] |= (byte)(isrc >> (8 - localBitLen)); // write second half
|
||||
destination[bytePtr + 1] = (byte)(
|
||||
(uint)(destination[bytePtr + 1] & (255 << localBitLen)) |
|
||||
(uint)(isrc >> (8 - localBitLen))
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write several whole bytes
|
||||
/// </summary>
|
||||
public static void WriteBytes(byte[] source, int sourceByteOffset, int numberOfBytes, byte[] destination, int destBitOffset)
|
||||
{
|
||||
int dstBytePtr = destBitOffset >> 3;
|
||||
int firstPartLen = (destBitOffset % 8);
|
||||
|
||||
if (firstPartLen == 0)
|
||||
{
|
||||
// optimized; TODO: write 32 bit chunks if possible
|
||||
for (int i = 0; i < numberOfBytes; i++)
|
||||
destination[dstBytePtr++] = source[sourceByteOffset + i];
|
||||
return;
|
||||
}
|
||||
|
||||
int lastPartLen = 8 - firstPartLen;
|
||||
|
||||
for (int i = 0; i < numberOfBytes; i++)
|
||||
{
|
||||
byte src = source[sourceByteOffset + i];
|
||||
|
||||
// write last part of this byte
|
||||
destination[dstBytePtr] &= (byte)(255 >> lastPartLen); // clear before writing
|
||||
destination[dstBytePtr] |= (byte)(src << firstPartLen); // write first half
|
||||
|
||||
dstBytePtr++;
|
||||
|
||||
// write first part of next byte
|
||||
destination[dstBytePtr] &= (byte)(255 << firstPartLen); // clear before writing
|
||||
destination[dstBytePtr] |= (byte)(src >> lastPartLen); // write second half
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
#if UNSAFE
|
||||
public static unsafe uint ReadUInt32(byte[] fromBuffer, int numberOfBits, int readBitOffset)
|
||||
{
|
||||
NetException.Assert(((numberOfBits > 0) && (numberOfBits <= 32)), "ReadUInt32() can only read between 1 and 32 bits");
|
||||
|
||||
if (numberOfBits == 32 && ((readBitOffset % 8) == 0))
|
||||
{
|
||||
fixed (byte* ptr = &(fromBuffer[readBitOffset / 8]))
|
||||
{
|
||||
return *(((uint*)ptr));
|
||||
}
|
||||
}
|
||||
#else
|
||||
public static uint ReadUInt32(byte[] fromBuffer, int numberOfBits, int readBitOffset)
|
||||
{
|
||||
NetException.Assert(((numberOfBits > 0) && (numberOfBits <= 32)), "ReadUInt32() can only read between 1 and 32 bits");
|
||||
#endif
|
||||
uint returnValue;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
returnValue = ReadByte(fromBuffer, numberOfBits, readBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
returnValue = ReadByte(fromBuffer, 8, readBitOffset);
|
||||
numberOfBits -= 8;
|
||||
readBitOffset += 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
returnValue |= (uint)(ReadByte(fromBuffer, numberOfBits, readBitOffset) << 8);
|
||||
return returnValue;
|
||||
}
|
||||
returnValue |= (uint)(ReadByte(fromBuffer, 8, readBitOffset) << 8);
|
||||
numberOfBits -= 8;
|
||||
readBitOffset += 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
uint r = ReadByte(fromBuffer, numberOfBits, readBitOffset);
|
||||
r <<= 16;
|
||||
returnValue |= r;
|
||||
return returnValue;
|
||||
}
|
||||
returnValue |= (uint)(ReadByte(fromBuffer, 8, readBitOffset) << 16);
|
||||
numberOfBits -= 8;
|
||||
readBitOffset += 8;
|
||||
|
||||
returnValue |= (uint)(ReadByte(fromBuffer, numberOfBits, readBitOffset) << 24);
|
||||
|
||||
#if BIGENDIAN
|
||||
// reorder bytes
|
||||
return
|
||||
((a & 0xff000000) >> 24) |
|
||||
((a & 0x00ff0000) >> 8) |
|
||||
((a & 0x0000ff00) << 8) |
|
||||
((a & 0x000000ff) << 24);
|
||||
#endif
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static ulong ReadUInt64(byte[] fromBuffer, int numberOfBits, int readBitOffset)
|
||||
{
|
||||
throw new NotImplementedException("ReadUInt64 not implemented yet");
|
||||
|
||||
#if BIGENDIAN
|
||||
// reorder bytes
|
||||
return ((a & 0xff00000000000000L) >> 56) |
|
||||
((a & 0x00ff000000000000L) >> 40) |
|
||||
((a & 0x0000ff0000000000L) >> 24) |
|
||||
((a & 0x000000ff00000000L) >> 8) |
|
||||
((a & 0x00000000ff000000L) << 8) |
|
||||
((a & 0x0000000000ff0000L) << 24) |
|
||||
((a & 0x000000000000ff00L) << 40) |
|
||||
((a & 0x00000000000000ffL) << 56);
|
||||
#endif
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static int WriteUInt32(uint source, int numberOfBits, byte[] destination, int destinationBitOffset)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
// reorder bytes
|
||||
source = ((source & 0xff000000) >> 24) |
|
||||
((source & 0x00ff0000) >> 8) |
|
||||
((source & 0x0000ff00) << 8) |
|
||||
((source & 0x000000ff) << 24);
|
||||
#endif
|
||||
|
||||
int returnValue = destinationBitOffset + numberOfBits;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)source, numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)source, 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 8), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 16), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 16), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
NetBitWriter.WriteByte((byte)(source >> 24), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static int WriteUInt64(ulong source, int numberOfBits, byte[] destination, int destinationBitOffset)
|
||||
{
|
||||
#if BIGENDIAN
|
||||
source = ((source & 0xff00000000000000L) >> 56) |
|
||||
((source & 0x00ff000000000000L) >> 40) |
|
||||
((source & 0x0000ff0000000000L) >> 24) |
|
||||
((source & 0x000000ff00000000L) >> 8) |
|
||||
((source & 0x00000000ff000000L) << 8) |
|
||||
((source & 0x0000000000ff0000L) << 24) |
|
||||
((source & 0x000000000000ff00L) << 40) |
|
||||
((source & 0x00000000000000ffL) << 56);
|
||||
#endif
|
||||
|
||||
int returnValue = destinationBitOffset + numberOfBits;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)source, numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)source, 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 8), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 8), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 16), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 16), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 24), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 24), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 32), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 32), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 40), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 40), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 48), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 48), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
NetBitWriter.WriteByte((byte)(source >> 56), numberOfBits, destination, destinationBitOffset);
|
||||
return returnValue;
|
||||
}
|
||||
NetBitWriter.WriteByte((byte)(source >> 56), 8, destination, destinationBitOffset);
|
||||
destinationBitOffset += 8;
|
||||
numberOfBits -= 8;
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
//
|
||||
// Variable size
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Write Base128 encoded variable sized unsigned integer
|
||||
/// </summary>
|
||||
/// <returns>number of bytes written</returns>
|
||||
[CLSCompliant(false)]
|
||||
public static int WriteVariableUInt32(byte[] intoBuffer, int offset, uint value)
|
||||
{
|
||||
int retval = 0;
|
||||
uint num1 = (uint)value;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
intoBuffer[offset + retval] = (byte)(num1 | 0x80);
|
||||
num1 = num1 >> 7;
|
||||
retval++;
|
||||
}
|
||||
intoBuffer[offset + retval] = (byte)num1;
|
||||
return retval + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt32 written using WriteUnsignedVarInt(); will increment offset!
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public static uint ReadVariableUInt32(byte[] buffer, ref int offset)
|
||||
{
|
||||
int num1 = 0;
|
||||
int num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
if (num2 == 0x23)
|
||||
throw new FormatException("Bad 7-bit encoded integer");
|
||||
|
||||
byte num3 = buffer[offset++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
return (uint)num1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Lidgren.Network/NetClient.cs
Normal file
104
Lidgren.Network/NetClient.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public class NetClient : NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the connection to the server, if any
|
||||
/// </summary>
|
||||
public NetConnection ServerConnection
|
||||
{
|
||||
get
|
||||
{
|
||||
NetConnection retval = null;
|
||||
if (m_connections.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
retval = m_connections[0];
|
||||
}
|
||||
catch
|
||||
{
|
||||
// preempted!
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
public NetClient(NetPeerConfiguration config)
|
||||
: base(config)
|
||||
{
|
||||
config.AcceptIncomingConnections = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from server
|
||||
/// </summary>
|
||||
/// <param name="byeMessage">reason for disconnect</param>
|
||||
public void Disconnect(string byeMessage)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
LogWarning("Disconnect requested when not connected!");
|
||||
return;
|
||||
}
|
||||
serverConnection.Disconnect(byeMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
//LogError("Cannot send message, no server connection!");
|
||||
return;
|
||||
}
|
||||
serverConnection.SendMessage(msg, method);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
//LogError("Cannot send message, no server connection!");
|
||||
return;
|
||||
}
|
||||
serverConnection.SendMessage(msg, method, sequenceChannel);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetClient " + ServerConnection + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
246
Lidgren.Network/NetConnection.Handshake.cs
Normal file
246
Lidgren.Network/NetConnection.Handshake.cs
Normal file
@@ -0,0 +1,246 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
internal bool m_connectRequested;
|
||||
internal string m_disconnectByeMessage;
|
||||
internal bool m_connectionInitiator;
|
||||
internal double m_connectInitationTime; // regardless of initiator
|
||||
internal NetOutgoingMessage m_approvalMessage;
|
||||
|
||||
internal void SetStatus(NetConnectionStatus status, string reason)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
if (status == m_status)
|
||||
return;
|
||||
m_status = status;
|
||||
if (reason == null)
|
||||
reason = string.Empty;
|
||||
|
||||
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.StatusChanged))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private void SendConnect()
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.Connected:
|
||||
// reconnect
|
||||
m_disconnectByeMessage = "Reconnecting";
|
||||
ExecuteDisconnect(true);
|
||||
FinishDisconnect();
|
||||
break;
|
||||
case NetConnectionStatus.Connecting:
|
||||
case NetConnectionStatus.None:
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
throw new NetException("This connection is Disconnected; spent. A new one should have been created");
|
||||
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
// let disconnect finish first
|
||||
return;
|
||||
}
|
||||
|
||||
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_type = NetMessageType.Library;
|
||||
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.SendImmediately(this, om);
|
||||
|
||||
m_connectInitationTime = NetTime.Now;
|
||||
SetStatus(NetConnectionStatus.Connecting, "Connecting");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
internal void SendConnectResponse()
|
||||
{
|
||||
NetOutgoingMessage reply = m_owner.CreateMessage(4);
|
||||
reply.m_type = NetMessageType.Library;
|
||||
reply.m_libType = NetMessageLibraryType.ConnectResponse;
|
||||
reply.Write((float)NetTime.Now);
|
||||
|
||||
m_owner.LogVerbose("Sending LibraryConnectResponse");
|
||||
m_owner.SendImmediately(this, reply);
|
||||
}
|
||||
|
||||
internal void SendConnectionEstablished()
|
||||
{
|
||||
NetOutgoingMessage ce = m_owner.CreateMessage(4);
|
||||
ce.m_type = NetMessageType.Library;
|
||||
ce.m_libType = NetMessageLibraryType.ConnectionEstablished;
|
||||
ce.Write((float)NetTime.Now);
|
||||
|
||||
m_owner.LogVerbose("Sending LibraryConnectionEstablished");
|
||||
m_owner.SendImmediately(this, ce);
|
||||
}
|
||||
|
||||
internal void ExecuteDisconnect(bool sendByeMessage)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
if (m_status == NetConnectionStatus.Disconnected || m_status == NetConnectionStatus.None)
|
||||
return;
|
||||
|
||||
if (sendByeMessage)
|
||||
{
|
||||
NetOutgoingMessage om = m_owner.CreateLibraryMessage(NetMessageLibraryType.Disconnect, m_disconnectByeMessage);
|
||||
EnqueueOutgoingMessage(om);
|
||||
}
|
||||
|
||||
m_owner.LogVerbose("Executing Disconnect(" + m_disconnectByeMessage + ")");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void FinishDisconnect()
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
if (m_status == NetConnectionStatus.Disconnected || m_status == NetConnectionStatus.None)
|
||||
return;
|
||||
|
||||
m_owner.LogVerbose("Finishing Disconnect(" + m_disconnectByeMessage + ")");
|
||||
|
||||
// release some held memory
|
||||
if (m_storedMessages != null)
|
||||
{
|
||||
foreach (List<NetOutgoingMessage> oml in m_storedMessages)
|
||||
if (oml != null)
|
||||
oml.Clear();
|
||||
}
|
||||
m_acknowledgesToSend.Clear();
|
||||
|
||||
SetStatus(NetConnectionStatus.Disconnected, m_disconnectByeMessage);
|
||||
m_disconnectByeMessage = null;
|
||||
m_connectionInitiator = false;
|
||||
}
|
||||
|
||||
private void HandleIncomingHandshake(NetMessageLibraryType ltp, int ptr, int payloadBitsLength)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
switch (ltp)
|
||||
{
|
||||
case NetMessageLibraryType.Connect:
|
||||
if (m_status == NetConnectionStatus.Connecting)
|
||||
{
|
||||
// our connectresponse must have been lost, send another one
|
||||
SendConnectResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
m_owner.LogError("NetConnection.HandleIncomingHandshake() passed LibraryConnect but status is " + m_status + "!?");
|
||||
break;
|
||||
case NetMessageLibraryType.ConnectResponse:
|
||||
if (!m_connectionInitiator)
|
||||
{
|
||||
m_owner.LogError("NetConnection.HandleIncomingHandshake() passed LibraryConnectResponse, but we're not initiator!");
|
||||
// weird, just drop it
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
SendConnectionEstablished();
|
||||
return;
|
||||
}
|
||||
|
||||
m_owner.LogWarning("NetConnection.HandleIncomingHandshake() passed " + ltp + ", but status is " + m_status);
|
||||
break;
|
||||
case NetMessageLibraryType.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");
|
||||
return;
|
||||
}
|
||||
|
||||
m_owner.LogWarning("NetConnection.HandleIncomingHandshake() passed " + ltp + ", 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();
|
||||
break;
|
||||
default:
|
||||
// huh?
|
||||
throw new NotImplementedException("Unhandled library type: " + ltp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
156
Lidgren.Network/NetConnection.Latency.cs
Normal file
156
Lidgren.Network/NetConnection.Latency.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
private double m_lastSendRespondedTo;
|
||||
|
||||
// remote + diff = local
|
||||
// diff = local - remote
|
||||
// local - diff = remote
|
||||
internal double m_remoteToLocalNetTime = double.MinValue;
|
||||
|
||||
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } }
|
||||
|
||||
internal void InitializeLatency(float rtt, float remoteNetTime)
|
||||
{
|
||||
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;
|
||||
|
||||
double currentRemoteNetTime = remoteNetTime - (rtt * 0.5);
|
||||
m_remoteToLocalNetTime = (float)(NetTime.Now - currentRemoteNetTime);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
internal void HandleIncomingPing(byte pingNumber)
|
||||
{
|
||||
// send pong
|
||||
NetOutgoingMessage pong = m_owner.CreateMessage(1);
|
||||
pong.m_type = NetMessageType.Library;
|
||||
pong.m_libType = NetMessageLibraryType.Pong;
|
||||
pong.Write((byte)pingNumber);
|
||||
pong.Write(NetTime.Now);
|
||||
|
||||
// send immediately
|
||||
m_owner.SendImmediately(this, pong);
|
||||
}
|
||||
|
||||
internal void HandleIncomingPong(double receiveNow, byte pingNumber, double remoteNetTime)
|
||||
{
|
||||
// verify it´s the correct ping number
|
||||
if (pingNumber != m_lastSentPingNumber)
|
||||
{
|
||||
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
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
double now = NetTime.Now; // need exact number
|
||||
|
||||
m_lastHeardFrom = now;
|
||||
m_lastSendRespondedTo = m_lastPingSendTime;
|
||||
|
||||
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;
|
||||
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));
|
||||
}
|
||||
|
||||
internal void KeepAliveHeartbeat(double now)
|
||||
{
|
||||
// do keepalive and latency pings
|
||||
if (m_status == NetConnectionStatus.Disconnected || m_status == NetConnectionStatus.None)
|
||||
return;
|
||||
|
||||
// in case of inactivity; send forced keepalive/ping
|
||||
if (now > m_lastSendRespondedTo + m_peerConfiguration.m_keepAliveDelay)
|
||||
m_nextPing = now;
|
||||
|
||||
// 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_type = NetMessageType.Library;
|
||||
pig.m_libType = NetMessageLibraryType.KeepAlive;
|
||||
EnqueueOutgoingMessage(pig);
|
||||
m_nextForceAckTime = now + m_peerConfiguration.m_maxAckDelayTime;
|
||||
}
|
||||
|
||||
// timeout
|
||||
if (now > m_lastSendRespondedTo + m_peerConfiguration.m_connectionTimeout)
|
||||
{
|
||||
Disconnect("Timed out after " + (now - m_lastSendRespondedTo) + " seconds");
|
||||
return;
|
||||
}
|
||||
|
||||
// ping time?
|
||||
if (now >= m_nextPing)
|
||||
{
|
||||
//
|
||||
// send ping
|
||||
//
|
||||
m_lastSentPingNumber++;
|
||||
|
||||
NetOutgoingMessage ping = m_owner.CreateMessage(1);
|
||||
ping.m_type = NetMessageType.Library;
|
||||
ping.m_libType = NetMessageLibraryType.Ping;
|
||||
ping.Write((byte)m_lastSentPingNumber);
|
||||
|
||||
m_owner.SendImmediately(this, ping);
|
||||
|
||||
m_lastPingSendTime = NetTime.Now; // need exact number
|
||||
m_nextPing = now + m_owner.Configuration.m_pingFrequency;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
266
Lidgren.Network/NetConnection.Reliability.cs
Normal file
266
Lidgren.Network/NetConnection.Reliability.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
/* 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.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
private ushort[] m_nextSendSequenceNumber;
|
||||
private ushort[] m_lastReceivedSequenced;
|
||||
|
||||
internal List<NetOutgoingMessage>[] m_storedMessages; // naïve! replace by something better?
|
||||
internal NetBitVector m_storedMessagesNotEmpty;
|
||||
|
||||
private ushort[] m_nextExpectedReliableSequence;
|
||||
private List<NetIncomingMessage>[] m_withheldMessages;
|
||||
internal Queue<int> m_acknowledgesToSend;
|
||||
internal double m_nextForceAckTime;
|
||||
|
||||
private NetBitVector[] m_reliableReceived;
|
||||
|
||||
public int GetStoredMessagesCount()
|
||||
{
|
||||
int retval = 0;
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
{
|
||||
List<NetOutgoingMessage> list = m_storedMessages[i];
|
||||
if (list != null)
|
||||
retval += list.Count;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
private void InitializeReliability()
|
||||
{
|
||||
int num = ((int)NetMessageType.UserReliableOrdered + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced;
|
||||
m_nextSendSequenceNumber = new ushort[num];
|
||||
m_lastReceivedSequenced = new ushort[num];
|
||||
m_nextForceAckTime = double.MaxValue;
|
||||
|
||||
m_storedMessages = new List<NetOutgoingMessage>[NetConstants.NumReliableChannels];
|
||||
m_storedMessagesNotEmpty = new NetBitVector(NetConstants.NumReliableChannels);
|
||||
|
||||
m_reliableReceived = new NetBitVector[NetConstants.NumSequenceNumbers];
|
||||
m_nextExpectedReliableSequence = new ushort[NetConstants.NumReliableChannels];
|
||||
m_withheldMessages = new List<NetIncomingMessage>[NetConstants.NetChannelsPerDeliveryMethod]; // only for ReliableOrdered
|
||||
m_acknowledgesToSend = new Queue<int>();
|
||||
}
|
||||
|
||||
internal ushort GetSendSequenceNumber(NetMessageType mtp)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
int slot = (int)mtp - (int)NetMessageType.UserSequenced;
|
||||
return m_nextSendSequenceNumber[slot]++;
|
||||
}
|
||||
|
||||
internal static int Relate(int seqNr, int lastReceived)
|
||||
{
|
||||
return (seqNr < lastReceived ? (seqNr + NetConstants.NumSequenceNumbers) - lastReceived : seqNr - lastReceived);
|
||||
}
|
||||
|
||||
// returns true if message should be rejected
|
||||
internal bool ReceivedSequencedMessage(NetMessageType mtp, ushort seqNr)
|
||||
{
|
||||
int slot = (int)mtp - (int)NetMessageType.UserSequenced;
|
||||
|
||||
int diff = Relate(seqNr, m_lastReceivedSequenced[slot]);
|
||||
|
||||
if (diff == 0)
|
||||
return true; // reject; already received
|
||||
if (diff > (ushort.MaxValue / 2))
|
||||
return true; // reject; out of window
|
||||
|
||||
m_lastReceivedSequenced[slot] = seqNr;
|
||||
return false;
|
||||
}
|
||||
|
||||
// called the FIRST time a reliable message is sent
|
||||
private void StoreReliableMessage(double now, NetOutgoingMessage msg)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
int reliableSlot = (int)msg.m_type - (int)NetMessageType.UserReliableUnordered;
|
||||
|
||||
List<NetOutgoingMessage> list = m_storedMessages[reliableSlot];
|
||||
if (list == null)
|
||||
{
|
||||
list = new List<NetOutgoingMessage>();
|
||||
m_storedMessages[reliableSlot] = list;
|
||||
}
|
||||
Interlocked.Increment(ref msg.m_inQueueCount);
|
||||
list.Add(msg);
|
||||
|
||||
if (list.Count == 1)
|
||||
m_storedMessagesNotEmpty.Set(reliableSlot, true);
|
||||
|
||||
// schedule next resend
|
||||
int numSends = msg.m_numSends;
|
||||
float[] baseTimes = m_peerConfiguration.m_resendBaseTime;
|
||||
float[] multiplers = m_peerConfiguration.m_resendRTTMultiplier;
|
||||
msg.m_nextResendTime = now + baseTimes[numSends] + (m_averageRoundtripTime * multiplers[numSends]);
|
||||
}
|
||||
|
||||
private void Resend(double now, NetOutgoingMessage msg)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
int numSends = msg.m_numSends;
|
||||
float[] baseTimes = m_peerConfiguration.m_resendBaseTime;
|
||||
if (numSends >= baseTimes.Length)
|
||||
{
|
||||
// no more resends! We failed!
|
||||
int reliableSlot = (int)msg.m_type - (int)NetMessageType.UserReliableUnordered;
|
||||
List<NetOutgoingMessage> list = m_storedMessages[reliableSlot];
|
||||
list.Remove(msg);
|
||||
m_owner.LogWarning("Failed to deliver reliable message " + msg);
|
||||
|
||||
Disconnect("Failed to deliver reliable message!");
|
||||
|
||||
return; // bye
|
||||
}
|
||||
|
||||
m_owner.LogVerbose("Resending " + msg);
|
||||
|
||||
Interlocked.Increment(ref msg.m_inQueueCount);
|
||||
m_unsentMessages.EnqueueFirst(msg);
|
||||
|
||||
msg.m_lastSentTime = now;
|
||||
|
||||
// schedule next resend
|
||||
float[] multiplers = m_peerConfiguration.m_resendRTTMultiplier;
|
||||
msg.m_nextResendTime = now + baseTimes[numSends] + (m_averageRoundtripTime * multiplers[numSends]);
|
||||
}
|
||||
|
||||
private void HandleIncomingAcks(int ptr, int payloadByteLength)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
int numAcks = payloadByteLength / 3;
|
||||
if (numAcks * 3 != payloadByteLength)
|
||||
m_owner.LogWarning("Malformed ack message; payload length is " + payloadByteLength);
|
||||
|
||||
byte[] buffer = m_owner.m_receiveBuffer;
|
||||
for (int i = 0; i < numAcks; i++)
|
||||
{
|
||||
ushort seqNr = (ushort)(buffer[ptr++] | (buffer[ptr++] << 8));
|
||||
NetMessageType tp = (NetMessageType)buffer[ptr++];
|
||||
// m_owner.LogDebug("Got ack for " + tp + " " + seqNr);
|
||||
|
||||
// remove stored message
|
||||
int reliableSlot = (int)tp - (int)NetMessageType.UserReliableUnordered;
|
||||
|
||||
List<NetOutgoingMessage> list = m_storedMessages[reliableSlot];
|
||||
if (list == null)
|
||||
continue;
|
||||
|
||||
// find message
|
||||
for (int a = 0; a < list.Count; a++)
|
||||
{
|
||||
NetOutgoingMessage om = list[a];
|
||||
if (om.m_sequenceNumber == seqNr)
|
||||
{
|
||||
// found!
|
||||
list.RemoveAt(a);
|
||||
Interlocked.Decrement(ref om.m_inQueueCount);
|
||||
|
||||
NetException.Assert(om.m_lastSentTime != 0);
|
||||
|
||||
if (om.m_lastSentTime > m_lastSendRespondedTo)
|
||||
m_lastSendRespondedTo = om.m_lastSentTime;
|
||||
|
||||
if (om.m_inQueueCount < 1)
|
||||
m_owner.Recycle(om);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: receipt handling
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpectedReliableSequenceArrived(int reliableSlot)
|
||||
{
|
||||
NetBitVector received = m_reliableReceived[reliableSlot];
|
||||
|
||||
int nextExpected = m_nextExpectedReliableSequence[reliableSlot];
|
||||
|
||||
if (received == null)
|
||||
{
|
||||
nextExpected = (nextExpected + 1) % NetConstants.NumSequenceNumbers;
|
||||
m_nextExpectedReliableSequence[reliableSlot] = (ushort)nextExpected;
|
||||
return;
|
||||
}
|
||||
|
||||
received[(nextExpected + (NetConstants.NumSequenceNumbers / 2)) % NetConstants.NumSequenceNumbers] = false; // reset for next pass
|
||||
nextExpected = (nextExpected + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
while (received[nextExpected] == true)
|
||||
{
|
||||
// it seems we've already received the next expected reliable sequence number
|
||||
|
||||
// ordered?
|
||||
const int orderedSlotsStart = ((int)NetMessageType.UserReliableOrdered - (int)NetMessageType.UserReliableUnordered);
|
||||
if (reliableSlot >= orderedSlotsStart)
|
||||
{
|
||||
// ... then we should have a withheld message waiting
|
||||
|
||||
// this should be a withheld message
|
||||
int orderedSlot = reliableSlot - orderedSlotsStart;
|
||||
bool foundWithheld = false;
|
||||
|
||||
List<NetIncomingMessage> withheldList = m_withheldMessages[orderedSlot];
|
||||
if (withheldList != null)
|
||||
{
|
||||
foreach (NetIncomingMessage wm in withheldList)
|
||||
{
|
||||
int wmSeqChan = wm.SequenceChannel;
|
||||
|
||||
if (orderedSlot == wmSeqChan && wm.m_sequenceNumber == nextExpected)
|
||||
{
|
||||
// Found withheld message due for delivery
|
||||
m_owner.LogVerbose("Releasing withheld message " + wm);
|
||||
|
||||
// AcceptMessage
|
||||
m_owner.ReleaseMessage(wm);
|
||||
|
||||
foundWithheld = true;
|
||||
withheldList.Remove(wm);
|
||||
|
||||
// advance next expected
|
||||
received[(nextExpected + (NetConstants.NumSequenceNumbers / 2)) % NetConstants.NumSequenceNumbers] = false; // reset for next pass
|
||||
nextExpected = (nextExpected + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundWithheld)
|
||||
throw new NetException("Failed to find withheld message!");
|
||||
}
|
||||
}
|
||||
|
||||
m_nextExpectedReliableSequence[reliableSlot] = (ushort)nextExpected;
|
||||
}
|
||||
}
|
||||
}
|
||||
652
Lidgren.Network/NetConnection.cs
Normal file
652
Lidgren.Network/NetConnection.cs
Normal file
@@ -0,0 +1,652 @@
|
||||
/* Copyright (c) 2010 Michael Lidgren
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
|
||||
the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
[DebuggerDisplay("RemoteEndpoint={m_remoteEndpoint} Status={m_status}")]
|
||||
public partial class NetConnection
|
||||
{
|
||||
private NetPeer m_owner;
|
||||
internal IPEndPoint m_remoteEndpoint;
|
||||
internal double m_lastHeardFrom;
|
||||
internal NetQueue<NetOutgoingMessage> m_unsentMessages;
|
||||
internal NetConnectionStatus m_status;
|
||||
private double m_lastSentUnsentMessages;
|
||||
private float m_throttleDebt;
|
||||
private NetPeerConfiguration m_peerConfiguration;
|
||||
internal NetConnectionStatistics m_statistics;
|
||||
private int m_lesserHeartbeats;
|
||||
private int m_nextFragmentGroupId;
|
||||
internal long m_remoteUniqueIdentifier;
|
||||
private Dictionary<int, NetIncomingMessage> m_fragmentGroups;
|
||||
private int m_handshakeAttempts;
|
||||
|
||||
internal PendingConnectionStatus m_pendingStatus = PendingConnectionStatus.NotPending;
|
||||
internal string m_pendingDenialReason;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the object containing data about the connection
|
||||
/// </summary>
|
||||
public object Tag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Statistics for the connection
|
||||
/// </summary>
|
||||
public NetConnectionStatistics Statistics { get { return m_statistics; } }
|
||||
|
||||
/// <summary>
|
||||
/// The unique identifier of the remote NetPeer for this connection
|
||||
/// </summary>
|
||||
public long RemoteUniqueIdentifier { get { return m_remoteUniqueIdentifier; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remote endpoint for the connection
|
||||
/// </summary>
|
||||
public IPEndPoint RemoteEndpoint { get { return m_remoteEndpoint; } }
|
||||
|
||||
internal NetConnection(NetPeer owner, IPEndPoint remoteEndpoint)
|
||||
{
|
||||
m_owner = owner;
|
||||
m_peerConfiguration = m_owner.m_configuration;
|
||||
m_remoteEndpoint = remoteEndpoint;
|
||||
m_unsentMessages = new NetQueue<NetOutgoingMessage>(16);
|
||||
m_fragmentGroups = new Dictionary<int, NetIncomingMessage>();
|
||||
m_status = NetConnectionStatus.None;
|
||||
|
||||
double now = NetTime.Now;
|
||||
m_nextPing = now + 5.0f;
|
||||
m_nextForceAckTime = double.MaxValue;
|
||||
m_lastSentUnsentMessages = now;
|
||||
m_lastSendRespondedTo = now;
|
||||
m_statistics = new NetConnectionStatistics(this);
|
||||
|
||||
InitializeReliability();
|
||||
}
|
||||
|
||||
// run on network thread
|
||||
internal void Heartbeat(double now)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
m_lesserHeartbeats++;
|
||||
|
||||
if (m_lesserHeartbeats >= 2)
|
||||
{
|
||||
//
|
||||
// Do greater heartbeat every third heartbeat
|
||||
//
|
||||
m_lesserHeartbeats = 0;
|
||||
|
||||
// keepalive, timeout and ping stuff
|
||||
KeepAliveHeartbeat(now);
|
||||
|
||||
if (m_connectRequested)
|
||||
SendConnect();
|
||||
|
||||
if (m_status == NetConnectionStatus.Connecting && now - m_connectInitationTime > m_owner.m_configuration.m_handshakeAttemptDelay)
|
||||
{
|
||||
if (m_connectionInitiator)
|
||||
SendConnect();
|
||||
else
|
||||
SendConnectResponse();
|
||||
|
||||
m_connectInitationTime = now;
|
||||
|
||||
if (++m_handshakeAttempts >= m_owner.m_configuration.m_handshakeMaxAttempts)
|
||||
{
|
||||
Disconnect("Failed to complete handshake");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// queue resends
|
||||
if (!m_storedMessagesNotEmpty.IsEmpty())
|
||||
{
|
||||
int first = m_storedMessagesNotEmpty.GetFirstSetIndex();
|
||||
for (int i = first; i < m_storedMessages.Length; i++)
|
||||
{
|
||||
if (m_storedMessagesNotEmpty.Get(i))
|
||||
{
|
||||
foreach (NetOutgoingMessage om in m_storedMessages[i])
|
||||
{
|
||||
if (now >= om.m_nextResendTime)
|
||||
{
|
||||
Resend(now, om);
|
||||
break; // need to break out here; collection may have been modified
|
||||
}
|
||||
}
|
||||
}
|
||||
#if DEBUG
|
||||
else
|
||||
{
|
||||
NetException.Assert(m_storedMessages[i] == null || m_storedMessages[i].Count < 1, "m_storedMessagesNotEmpty fail!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send unsent messages; high priority first
|
||||
byte[] buffer = m_owner.m_sendBuffer;
|
||||
int ptr = 0;
|
||||
|
||||
float throttle = m_peerConfiguration.m_throttleBytesPerSecond;
|
||||
if (throttle > 0)
|
||||
{
|
||||
double frameLength = now - m_lastSentUnsentMessages;
|
||||
if (m_throttleDebt > 0)
|
||||
m_throttleDebt -= (float)(frameLength * throttle);
|
||||
m_lastSentUnsentMessages = now;
|
||||
}
|
||||
|
||||
int mtu = m_peerConfiguration.MaximumTransmissionUnit;
|
||||
|
||||
float throttleThreshold = m_peerConfiguration.m_throttlePeakBytes;
|
||||
if (m_throttleDebt < throttleThreshold)
|
||||
{
|
||||
//
|
||||
// Send new unsent messages
|
||||
//
|
||||
int numIncludedMessages = 0;
|
||||
while (m_unsentMessages.Count > 0)
|
||||
{
|
||||
if (m_throttleDebt >= throttleThreshold)
|
||||
break;
|
||||
|
||||
NetOutgoingMessage msg = m_unsentMessages.TryDequeue();
|
||||
if (msg == null)
|
||||
continue;
|
||||
Interlocked.Decrement(ref msg.m_inQueueCount);
|
||||
|
||||
int msgPayloadLength = msg.LengthBytes;
|
||||
msg.m_lastSentTime = now;
|
||||
|
||||
if (ptr > 0 && (ptr + NetPeer.kMaxPacketHeaderSize + msgPayloadLength) > mtu)
|
||||
{
|
||||
// send packet and start new packet
|
||||
m_owner.SendPacket(ptr, m_remoteEndpoint, numIncludedMessages);
|
||||
m_statistics.PacketSent(ptr, numIncludedMessages);
|
||||
numIncludedMessages = 0;
|
||||
m_throttleDebt += ptr;
|
||||
ptr = 0;
|
||||
}
|
||||
|
||||
//
|
||||
// encode message
|
||||
//
|
||||
|
||||
ptr = msg.Encode(buffer, ptr, this);
|
||||
numIncludedMessages++;
|
||||
|
||||
if (msg.m_type >= NetMessageType.UserReliableUnordered && msg.m_numSends == 1)
|
||||
{
|
||||
// message is sent for the first time, and is reliable, store for resend
|
||||
StoreReliableMessage(now, msg);
|
||||
}
|
||||
|
||||
// room to piggyback some acks?
|
||||
if (m_acknowledgesToSend.Count > 0)
|
||||
{
|
||||
int payloadLeft = (mtu - ptr) - NetPeer.kMaxPacketHeaderSize;
|
||||
if (payloadLeft > 9)
|
||||
{
|
||||
// yes, add them as a regular message
|
||||
ptr = NetOutgoingMessage.EncodeAcksMessage(m_owner.m_sendBuffer, ptr, this, (payloadLeft - 3));
|
||||
|
||||
if (m_acknowledgesToSend.Count < 1)
|
||||
m_nextForceAckTime = double.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (msg.m_type == NetMessageType.Library && msg.m_libType == NetMessageLibraryType.Disconnect)
|
||||
{
|
||||
FinishDisconnect();
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg.m_inQueueCount < 1)
|
||||
m_owner.Recycle(msg);
|
||||
}
|
||||
|
||||
if (ptr > 0)
|
||||
{
|
||||
m_owner.SendPacket(ptr, m_remoteEndpoint, numIncludedMessages);
|
||||
m_statistics.PacketSent(ptr, numIncludedMessages);
|
||||
numIncludedMessages = 0;
|
||||
m_throttleDebt += ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void HandleUserMessage(double now, NetMessageType mtp, bool isFragment, ushort channelSequenceNumber, int ptr, int payloadLengthBits)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
try
|
||||
{
|
||||
NetDeliveryMethod ndm = NetPeer.GetDeliveryMethod(mtp);
|
||||
|
||||
//
|
||||
// Unreliable
|
||||
//
|
||||
if (ndm == NetDeliveryMethod.Unreliable)
|
||||
{
|
||||
AcceptMessage(mtp, isFragment, channelSequenceNumber, ptr, payloadLengthBits);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// UnreliableSequenced
|
||||
//
|
||||
if (ndm == NetDeliveryMethod.UnreliableSequenced)
|
||||
{
|
||||
bool reject = ReceivedSequencedMessage(mtp, channelSequenceNumber);
|
||||
if (!reject)
|
||||
AcceptMessage(mtp, isFragment, channelSequenceNumber, ptr, payloadLengthBits);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Reliable delivery methods below
|
||||
//
|
||||
|
||||
// queue ack
|
||||
m_acknowledgesToSend.Enqueue((int)channelSequenceNumber | ((int)mtp << 16));
|
||||
if (m_nextForceAckTime == double.MaxValue)
|
||||
m_nextForceAckTime = now + m_peerConfiguration.m_maxAckDelayTime;
|
||||
|
||||
if (ndm == NetDeliveryMethod.ReliableSequenced)
|
||||
{
|
||||
bool reject = ReceivedSequencedMessage(mtp, channelSequenceNumber);
|
||||
if (!reject)
|
||||
AcceptMessage(mtp, isFragment, channelSequenceNumber, ptr, payloadLengthBits);
|
||||
return;
|
||||
}
|
||||
|
||||
// relate to all received up to
|
||||
int reliableSlot = (int)mtp - (int)NetMessageType.UserReliableUnordered;
|
||||
int diff = Relate(channelSequenceNumber, m_nextExpectedReliableSequence[reliableSlot]);
|
||||
|
||||
if (diff > (ushort.MaxValue / 2))
|
||||
{
|
||||
// Reject out-of-window
|
||||
//m_statistics.CountDuplicateMessage(msg);
|
||||
m_owner.LogVerbose("Rejecting duplicate reliable " + mtp + " " + channelSequenceNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if (diff == 0)
|
||||
{
|
||||
// Expected sequence number
|
||||
AcceptMessage(mtp, isFragment, channelSequenceNumber, ptr, payloadLengthBits);
|
||||
|
||||
ExpectedReliableSequenceArrived(reliableSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Early reliable message - we must check if it's already been received
|
||||
//
|
||||
// DeliveryMethod is ReliableUnordered or ReliableOrdered here
|
||||
//
|
||||
|
||||
// get bools list we must check
|
||||
NetBitVector recList = m_reliableReceived[reliableSlot];
|
||||
if (recList == null)
|
||||
{
|
||||
recList = new NetBitVector(NetConstants.NumSequenceNumbers);
|
||||
m_reliableReceived[reliableSlot] = recList;
|
||||
}
|
||||
|
||||
if (recList[channelSequenceNumber])
|
||||
{
|
||||
// Reject duplicate
|
||||
//m_statistics.CountDuplicateMessage(msg);
|
||||
m_owner.LogVerbose("Rejecting duplicate reliable " + ndm.ToString() + channelSequenceNumber.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
// It's an early reliable message
|
||||
recList[channelSequenceNumber] = true;
|
||||
|
||||
m_owner.LogVerbose("Received early reliable message: " + channelSequenceNumber);
|
||||
|
||||
//
|
||||
// It's not a duplicate; mark as received. Release if it's unordered, else withhold
|
||||
//
|
||||
|
||||
if (ndm == NetDeliveryMethod.ReliableUnordered)
|
||||
{
|
||||
AcceptMessage(mtp, isFragment, channelSequenceNumber, ptr, payloadLengthBits);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Only ReliableOrdered left here; withhold it
|
||||
//
|
||||
|
||||
// Early ordered message; withhold
|
||||
const int orderedSlotsStart = ((int)NetMessageType.UserReliableOrdered - (int)NetMessageType.UserReliableUnordered);
|
||||
int orderedSlot = reliableSlot - orderedSlotsStart;
|
||||
|
||||
List<NetIncomingMessage> wmList = m_withheldMessages[orderedSlot];
|
||||
if (wmList == null)
|
||||
{
|
||||
wmList = new List<NetIncomingMessage>();
|
||||
m_withheldMessages[orderedSlot] = wmList;
|
||||
}
|
||||
|
||||
// create message
|
||||
NetIncomingMessage im = m_owner.CreateIncomingMessage(NetIncomingMessageType.Data, m_owner.m_receiveBuffer, ptr, NetUtility.BytesToHoldBits(payloadLengthBits));
|
||||
im.m_bitLength = payloadLengthBits;
|
||||
im.m_messageType = mtp;
|
||||
im.m_sequenceNumber = channelSequenceNumber;
|
||||
im.m_senderConnection = this;
|
||||
im.m_senderEndpoint = m_remoteEndpoint;
|
||||
|
||||
m_owner.LogVerbose("Withholding " + im + " (waiting for " + m_nextExpectedReliableSequence[reliableSlot] + ")");
|
||||
|
||||
wmList.Add(im);
|
||||
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
#if DEBUG
|
||||
throw new NetException("Message generated exception: " + ex, ex);
|
||||
#else
|
||||
m_owner.LogError("Message generated exception: " + ex);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private void AcceptMessage(NetMessageType mtp, bool isFragment, ushort seqNr, int ptr, int payloadLengthBits)
|
||||
{
|
||||
byte[] buffer = m_owner.m_receiveBuffer;
|
||||
NetIncomingMessage im;
|
||||
int bytesLen = NetUtility.BytesToHoldBits(payloadLengthBits);
|
||||
|
||||
if (isFragment)
|
||||
{
|
||||
int fragmentGroup = buffer[ptr++] | (buffer[ptr++] << 8);
|
||||
int fragmentTotalCount = buffer[ptr++] | (buffer[ptr++] << 8);
|
||||
int fragmentNr = buffer[ptr++] | (buffer[ptr++] << 8);
|
||||
|
||||
// do we already have fragments of this group?
|
||||
if (!m_fragmentGroups.TryGetValue(fragmentGroup, out im))
|
||||
{
|
||||
// new fragmented message
|
||||
int estLength = fragmentTotalCount * bytesLen;
|
||||
|
||||
im = m_owner.CreateIncomingMessage(NetIncomingMessageType.Data, estLength);
|
||||
im.m_messageType = mtp;
|
||||
im.m_sequenceNumber = seqNr;
|
||||
im.m_senderConnection = this;
|
||||
im.m_senderEndpoint = m_remoteEndpoint;
|
||||
NetFragmentationInfo info = new NetFragmentationInfo();
|
||||
info.TotalFragmentCount = fragmentTotalCount;
|
||||
info.Received = new bool[fragmentTotalCount];
|
||||
info.FragmentSize = bytesLen;
|
||||
im.m_fragmentationInfo = info;
|
||||
m_fragmentGroups[fragmentGroup] = im;
|
||||
}
|
||||
|
||||
// insert this fragment at correct position
|
||||
bool done = InsertFragment(im, fragmentNr, ptr, bytesLen);
|
||||
if (!done)
|
||||
return;
|
||||
|
||||
// all received!
|
||||
m_fragmentGroups.Remove(fragmentGroup);
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-fragmented - release to application
|
||||
im = m_owner.CreateIncomingMessage(NetIncomingMessageType.Data, buffer, ptr, bytesLen);
|
||||
im.m_bitLength = payloadLengthBits;
|
||||
im.m_messageType = mtp;
|
||||
im.m_sequenceNumber = seqNr;
|
||||
im.m_senderConnection = this;
|
||||
im.m_senderEndpoint = m_remoteEndpoint;
|
||||
}
|
||||
|
||||
// m_owner.LogVerbose("Releasing " + im);
|
||||
m_owner.ReleaseMessage(im);
|
||||
}
|
||||
|
||||
private bool InsertFragment(NetIncomingMessage im, int nr, int ptr, int payloadLength)
|
||||
{
|
||||
NetFragmentationInfo info = im.m_fragmentationInfo;
|
||||
|
||||
if (nr >= info.TotalFragmentCount)
|
||||
{
|
||||
m_owner.LogError("Received fragment larger than total fragments! (total " + info.TotalFragmentCount + ", nr " + nr + ")");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.Received[nr] == true)
|
||||
{
|
||||
// duplicate fragment
|
||||
return false;
|
||||
}
|
||||
|
||||
// insert data
|
||||
int offset = nr * info.FragmentSize;
|
||||
|
||||
if (im.m_data.Length < offset + payloadLength)
|
||||
Array.Resize<byte>(ref im.m_data, offset + payloadLength);
|
||||
|
||||
Buffer.BlockCopy(m_owner.m_receiveBuffer, ptr, im.m_data, offset, payloadLength);
|
||||
|
||||
im.m_bitLength = (8 * (offset + payloadLength));
|
||||
|
||||
info.Received[nr] = true;
|
||||
info.TotalReceived++;
|
||||
|
||||
return info.TotalReceived >= info.TotalFragmentCount;
|
||||
}
|
||||
|
||||
internal void HandleLibraryMessage(double now, NetMessageLibraryType libType, int ptr, int payloadLengthBits)
|
||||
{
|
||||
m_owner.VerifyNetworkThread();
|
||||
|
||||
switch (libType)
|
||||
{
|
||||
case NetMessageLibraryType.Error:
|
||||
m_owner.LogWarning("Received NetMessageLibraryType.Error message!");
|
||||
break;
|
||||
case NetMessageLibraryType.Connect:
|
||||
case NetMessageLibraryType.ConnectResponse:
|
||||
case NetMessageLibraryType.ConnectionEstablished:
|
||||
case NetMessageLibraryType.Disconnect:
|
||||
HandleIncomingHandshake(libType, ptr, payloadLengthBits);
|
||||
break;
|
||||
case NetMessageLibraryType.KeepAlive:
|
||||
// no operation, we just want the acks
|
||||
break;
|
||||
case NetMessageLibraryType.Ping:
|
||||
if (NetUtility.BytesToHoldBits(payloadLengthBits) > 0)
|
||||
HandleIncomingPing(m_owner.m_receiveBuffer[ptr]);
|
||||
else
|
||||
m_owner.LogWarning("Received malformed ping");
|
||||
break;
|
||||
case NetMessageLibraryType.Pong:
|
||||
if (payloadLengthBits == (9 * 8))
|
||||
{
|
||||
byte pingNr = m_owner.m_receiveBuffer[ptr++];
|
||||
double remoteNetTime = BitConverter.ToDouble(m_owner.m_receiveBuffer, ptr);
|
||||
HandleIncomingPong(now, pingNr, remoteNetTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_owner.LogWarning("Received malformed pong");
|
||||
}
|
||||
break;
|
||||
case NetMessageLibraryType.Acknowledge:
|
||||
HandleIncomingAcks(ptr, NetUtility.BytesToHoldBits(payloadLengthBits));
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException("Unhandled library type: " + libType);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_type = (NetMessageType)method;
|
||||
EnqueueOutgoingMessage(msg);
|
||||
}
|
||||
|
||||
public void SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_type = (NetMessageType)((int)method + sequenceChannel);
|
||||
EnqueueOutgoingMessage(msg);
|
||||
}
|
||||
|
||||
// called by user and network thread
|
||||
internal void EnqueueOutgoingMessage(NetOutgoingMessage msg)
|
||||
{
|
||||
if (m_owner == null)
|
||||
return; // we've been disposed
|
||||
|
||||
int msgLen = msg.LengthBytes;
|
||||
int mtu = m_owner.m_configuration.m_maximumTransmissionUnit;
|
||||
|
||||
if (msgLen <= mtu)
|
||||
{
|
||||
Interlocked.Increment(ref msg.m_inQueueCount);
|
||||
m_unsentMessages.Enqueue(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
if ((int)msg.m_type < (int)NetMessageType.UserReliableUnordered)
|
||||
{
|
||||
// unreliable
|
||||
m_owner.LogWarning("Sending more than MTU (currently " + mtu + ") bytes unreliably is not recommended!");
|
||||
}
|
||||
#endif
|
||||
mtu -= NetConstants.FragmentHeaderSize; // size of fragmentation info
|
||||
|
||||
// message must be fragmented
|
||||
int fgi = Interlocked.Increment(ref m_nextFragmentGroupId);
|
||||
|
||||
int numFragments = (msgLen + mtu - 1) / mtu;
|
||||
|
||||
for(int i=0;i<numFragments;i++)
|
||||
{
|
||||
int flen = (i == numFragments - 1 ? (msgLen - (mtu * (numFragments - 1))) : mtu);
|
||||
|
||||
NetOutgoingMessage fm = m_owner.CreateMessage(flen);
|
||||
fm.m_fragmentGroupId = fgi;
|
||||
fm.m_fragmentNumber = i;
|
||||
fm.m_fragmentTotalCount = numFragments;
|
||||
|
||||
fm.Write(msg.m_data, mtu * i, flen);
|
||||
fm.m_type = msg.m_type;
|
||||
Interlocked.Increment(ref fm.m_inQueueCount);
|
||||
m_unsentMessages.Enqueue(fm);
|
||||
}
|
||||
}
|
||||
|
||||
public void Disconnect(string byeMessage)
|
||||
{
|
||||
// called on user thread (possibly)
|
||||
if (m_status == NetConnectionStatus.None || m_status == NetConnectionStatus.Disconnected)
|
||||
return;
|
||||
|
||||
m_owner.LogVerbose("Disconnect requested for " + this);
|
||||
m_disconnectByeMessage = byeMessage;
|
||||
|
||||
// loosen up throttling
|
||||
m_throttleDebt = -m_owner.m_configuration.m_throttlePeakBytes;
|
||||
|
||||
// shorten resend times
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
{
|
||||
List<NetOutgoingMessage> list = m_storedMessages[i];
|
||||
if (list != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (NetOutgoingMessage om in list)
|
||||
om.m_nextResendTime = (om.m_nextResendTime * 0.8) - 0.05;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// ok, collection was modified, never mind then - it was worth a shot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NetOutgoingMessage bye = m_owner.CreateLibraryMessage(NetMessageLibraryType.Disconnect, byeMessage);
|
||||
EnqueueOutgoingMessage(bye);
|
||||
}
|
||||
|
||||
public void Approve()
|
||||
{
|
||||
if (!m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
|
||||
m_owner.LogError("Approve() called but ConnectionApproval is not enabled in NetPeerConfiguration!");
|
||||
|
||||
if (m_pendingStatus != PendingConnectionStatus.Pending)
|
||||
{
|
||||
m_owner.LogWarning("Approve() called on non-pending connection!");
|
||||
return;
|
||||
}
|
||||
m_pendingStatus = PendingConnectionStatus.Approved;
|
||||
}
|
||||
|
||||
public void Deny(string reason)
|
||||
{
|
||||
if (!m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
|
||||
m_owner.LogError("Deny() called but ConnectionApproval is not enabled in NetPeerConfiguration!");
|
||||
|
||||
if (m_pendingStatus != PendingConnectionStatus.Pending)
|
||||
{
|
||||
m_owner.LogWarning("Deny() called on non-pending connection!");
|
||||
return;
|
||||
}
|
||||
m_pendingStatus = PendingConnectionStatus.Denied;
|
||||
m_pendingDenialReason = reason;
|
||||
}
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
m_owner = null;
|
||||
m_unsentMessages = null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetConnection to " + m_remoteEndpoint + ": " + m_status + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
104
Lidgren.Network/NetConnectionStatistics.cs
Normal file
104
Lidgren.Network/NetConnectionStatistics.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public sealed class NetConnectionStatistics
|
||||
{
|
||||
private NetConnection m_connection;
|
||||
|
||||
internal int m_sentPackets;
|
||||
internal int m_receivedPackets;
|
||||
|
||||
internal int m_sentMessages;
|
||||
internal int m_receivedMessages;
|
||||
|
||||
internal int m_sentBytes;
|
||||
internal int m_receivedBytes;
|
||||
|
||||
internal NetConnectionStatistics(NetConnection conn)
|
||||
{
|
||||
m_connection = conn;
|
||||
Reset();
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_sentPackets = 0;
|
||||
m_receivedPackets = 0;
|
||||
m_sentBytes = 0;
|
||||
m_receivedBytes = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of sent packets for this connection
|
||||
/// </summary>
|
||||
public int SentPackets { get { return m_sentPackets; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of received packets for this connection
|
||||
/// </summary>
|
||||
public int ReceivedPackets { get { return m_receivedPackets; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of sent bytes for this connection
|
||||
/// </summary>
|
||||
public int SentBytes { get { return m_sentBytes; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of received bytes for this connection
|
||||
/// </summary>
|
||||
public int ReceivedBytes { get { return m_receivedBytes; } }
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
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");
|
||||
int numUnsent = m_connection.m_unsentMessages.Count;
|
||||
if (numUnsent > 0)
|
||||
bdr.AppendLine("Unsent messages: " + numUnsent);
|
||||
int numStored = m_connection.GetStoredMessagesCount();
|
||||
if (numStored > 0)
|
||||
bdr.AppendLine("Stored messages: " + numStored);
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
34
Lidgren.Network/NetConnectionStatus.cs
Normal file
34
Lidgren.Network/NetConnectionStatus.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Status of a connection
|
||||
/// </summary>
|
||||
public enum NetConnectionStatus
|
||||
{
|
||||
None,
|
||||
Connecting,
|
||||
Connected,
|
||||
Disconnecting,
|
||||
Disconnected
|
||||
}
|
||||
}
|
||||
44
Lidgren.Network/NetConstants.cs
Normal file
44
Lidgren.Network/NetConstants.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal static class NetConstants
|
||||
{
|
||||
internal const int NetChannelsPerDeliveryMethod = 32;
|
||||
|
||||
internal const int NumSequenceNumbers = ushort.MaxValue + 1; // 0 is a valid sequence number
|
||||
|
||||
/// <summary>
|
||||
/// Number of channels which needs a sequence number to work
|
||||
/// </summary>
|
||||
internal const int NumSequencedChannels = ((int)NetMessageType.UserReliableOrdered + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced;
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
144
Lidgren.Network/NetEncryption.cs
Normal file
144
Lidgren.Network/NetEncryption.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public sealed class NetXTEA
|
||||
{
|
||||
private const int m_blockSize = 8;
|
||||
private const int m_keySize = 16;
|
||||
private const int m_delta = unchecked((int)0x9E3779B9);
|
||||
private const int m_dSum = unchecked((int)0xC6EF3720); // sum on decrypt
|
||||
|
||||
private byte[] m_keyBytes;
|
||||
private int[] m_key;
|
||||
private int m_rounds;
|
||||
|
||||
public byte[] Key { get { return m_keyBytes; } }
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte key
|
||||
/// </summary>
|
||||
public NetXTEA(byte[] key, int rounds)
|
||||
{
|
||||
m_keyBytes = key;
|
||||
m_key = new int[4];
|
||||
m_key[0] = BitConverter.ToInt32(key, 0);
|
||||
m_key[1] = BitConverter.ToInt32(key, 4);
|
||||
m_key[2] = BitConverter.ToInt32(key, 8);
|
||||
m_key[3] = BitConverter.ToInt32(key, 12);
|
||||
m_rounds = rounds;
|
||||
}
|
||||
|
||||
public void EncryptBlock(
|
||||
byte[] inBytes,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
// Pack bytes into integers
|
||||
int v0 = BytesToInt(inBytes, inOff);
|
||||
int v1 = BytesToInt(inBytes, inOff + 4);
|
||||
|
||||
int sum = 0;
|
||||
|
||||
for (int i = 0; i != m_rounds; i++)
|
||||
{
|
||||
v0 += ((v1 << 4 ^ (int)((uint)v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
|
||||
sum += m_delta;
|
||||
v1 += ((v0 << 4 ^ (int)((uint)v0 >> 5)) + v0) ^ (sum + m_key[(int)((uint)sum >> 11) & 3]);
|
||||
}
|
||||
|
||||
UnpackInt(v0, outBytes, outOff);
|
||||
UnpackInt(v1, outBytes, outOff + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public void DecryptBlock(
|
||||
byte[] inBytes,
|
||||
int inOff,
|
||||
byte[] outBytes,
|
||||
int outOff)
|
||||
{
|
||||
// Pack bytes into integers
|
||||
int v0 = BytesToInt(inBytes, inOff);
|
||||
int v1 = BytesToInt(inBytes, inOff + 4);
|
||||
|
||||
int sum = m_dSum;
|
||||
|
||||
for (int i = 0; i != m_rounds; i++)
|
||||
{
|
||||
v1 -= ((v0 << 4 ^ (int)((uint)v0 >> 5)) + v0) ^ (sum + m_key[(int)((uint)sum >> 11) & 3]);
|
||||
sum -= m_delta;
|
||||
v0 -= ((v1 << 4 ^ (int)((uint)v1 >> 5)) + v1) ^ (sum + m_key[sum & 3]);
|
||||
}
|
||||
|
||||
UnpackInt(v0, outBytes, outOff);
|
||||
UnpackInt(v1, outBytes, outOff + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private static int BytesToInt(byte[] b, int inOff)
|
||||
{
|
||||
//return BitConverter.ToInt32(b, inOff);
|
||||
return ((b[inOff++]) << 24) |
|
||||
((b[inOff++] & 255) << 16) |
|
||||
((b[inOff++] & 255) << 8) |
|
||||
((b[inOff] & 255));
|
||||
}
|
||||
|
||||
private static void UnpackInt(
|
||||
int v,
|
||||
byte[] b,
|
||||
int outOff)
|
||||
{
|
||||
uint uv = (uint)v;
|
||||
b[outOff++] = (byte)(uv >> 24);
|
||||
b[outOff++] = (byte)(uv >> 16);
|
||||
b[outOff++] = (byte)(uv >> 8);
|
||||
b[outOff] = (byte)uv;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NetSHA
|
||||
{
|
||||
// TODO: switch to SHA256
|
||||
private static SHA1 m_sha;
|
||||
|
||||
public static byte[] Hash(byte[] data)
|
||||
{
|
||||
if (m_sha == null)
|
||||
m_sha = SHA1Managed.Create();
|
||||
return m_sha.ComputeHash(data);
|
||||
}
|
||||
}
|
||||
|
||||
public static class NetSRP
|
||||
{
|
||||
private static readonly BigInteger N = new BigInteger(NetUtility.ToByteArray("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3"));
|
||||
private static readonly BigInteger g = new BigInteger((uint)2);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a verifier that the server can use to authenticate users later on
|
||||
/// </summary>
|
||||
public static byte[] ComputePasswordVerifier(string username, string password, byte[] salt)
|
||||
{
|
||||
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);
|
||||
|
||||
byte[] x = NetSHA.Hash(total);
|
||||
|
||||
// Verifier (v) = g^x (mod N)
|
||||
BigInteger xx = new BigInteger(x);
|
||||
return g.ModPow(xx, N).GetBytes();
|
||||
}
|
||||
}
|
||||
}
|
||||
69
Lidgren.Network/NetException.cs
Normal file
69
Lidgren.Network/NetException.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
/* 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.Runtime.Serialization;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class NetException : Exception
|
||||
{
|
||||
public NetException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
public NetException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public NetException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
}
|
||||
|
||||
private NetException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception, in DEBUG only, if first parameter is false
|
||||
/// </summary>
|
||||
[Conditional("DEBUG")]
|
||||
public static void Assert(bool isOk, string message)
|
||||
{
|
||||
if (!isOk)
|
||||
throw new NetException(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an exception, in DEBUG only, if first parameter is false
|
||||
/// </summary>
|
||||
[Conditional("DEBUG")]
|
||||
public static void Assert(bool isOk)
|
||||
{
|
||||
if (!isOk)
|
||||
throw new NetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Lidgren.Network/NetFragmentationInfo.cs
Normal file
12
Lidgren.Network/NetFragmentationInfo.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public class NetFragmentationInfo
|
||||
{
|
||||
public int TotalFragmentCount;
|
||||
public bool[] Received;
|
||||
public int TotalReceived;
|
||||
public int FragmentSize;
|
||||
}
|
||||
}
|
||||
262
Lidgren.Network/NetIncomingMessage.Peek.cs
Normal file
262
Lidgren.Network/NetIncomingMessage.Peek.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
/* 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.Diagnostics;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetIncomingMessage
|
||||
{
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
public bool PeekBoolean()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition);
|
||||
return (retval > 0 ? true : false);
|
||||
}
|
||||
|
||||
//
|
||||
// 8 bit
|
||||
//
|
||||
public byte PeekByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public sbyte PeekSByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
return (sbyte)retval;
|
||||
}
|
||||
|
||||
public byte PeekByte(int numberOfBits)
|
||||
{
|
||||
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
public byte[] PeekBytes(int numberOfBytes)
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
|
||||
|
||||
byte[] retval = new byte[numberOfBytes];
|
||||
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
public void PeekBytes(byte[] into, int offset, int numberOfBytes)
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
|
||||
NetException.Assert(offset + numberOfBytes <= into.Length);
|
||||
|
||||
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// 16 bit
|
||||
//
|
||||
public Int16 PeekInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
|
||||
return (short)retval;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt16 PeekUInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
|
||||
return (ushort)retval;
|
||||
}
|
||||
|
||||
//
|
||||
// 32 bit
|
||||
//
|
||||
public Int32 PeekInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
return (Int32)retval;
|
||||
}
|
||||
|
||||
public Int32 PeekInt32(int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadInt() can only read between 1 and 32 bits");
|
||||
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
|
||||
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
|
||||
|
||||
if (numberOfBits == 32)
|
||||
return (int)retval;
|
||||
|
||||
int signBit = 1 << (numberOfBits - 1);
|
||||
if ((retval & signBit) == 0)
|
||||
return (int)retval; // positive
|
||||
|
||||
// negative
|
||||
unchecked
|
||||
{
|
||||
uint mask = ((uint)-1) >> (33 - numberOfBits);
|
||||
uint tmp = (retval & mask) + 1;
|
||||
return -((int)tmp);
|
||||
}
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt32 PeekUInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt32 PeekUInt32(int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadUInt() can only read between 1 and 32 bits");
|
||||
//NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size");
|
||||
|
||||
UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
//
|
||||
// 64 bit
|
||||
//
|
||||
[CLSCompliant(false)]
|
||||
public UInt64 PeekUInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
|
||||
ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition + 32);
|
||||
|
||||
ulong retval = low + (high << 32);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Int64 PeekInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
unchecked
|
||||
{
|
||||
ulong retval = PeekUInt64();
|
||||
long longRetval = (long)retval;
|
||||
return longRetval;
|
||||
}
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt64 PeekUInt64(int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits");
|
||||
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
|
||||
|
||||
ulong retval;
|
||||
if (numberOfBits <= 32)
|
||||
{
|
||||
retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Int64 PeekInt64(int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits");
|
||||
return (long)PeekUInt64(numberOfBits);
|
||||
}
|
||||
|
||||
//
|
||||
// Floating point
|
||||
//
|
||||
public float PeekFloat()
|
||||
{
|
||||
return PeekSingle();
|
||||
}
|
||||
|
||||
public float PeekSingle()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
// endianness is handled inside BitConverter.ToSingle
|
||||
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = PeekBytes(4);
|
||||
return BitConverter.ToSingle(bytes, 0); // endianness is handled inside BitConverter.ToSingle
|
||||
}
|
||||
|
||||
public double PeekDouble()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
// read directly
|
||||
double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = PeekBytes(8);
|
||||
return BitConverter.ToDouble(bytes, 0); // endianness is handled inside BitConverter.ToSingle
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a string
|
||||
/// </summary>
|
||||
public string PeekString()
|
||||
{
|
||||
int byteLen = (int)ReadVariableUInt32();
|
||||
|
||||
if (byteLen == 0)
|
||||
return String.Empty;
|
||||
|
||||
NetException.Assert(m_bitLength - m_readPosition >= (byteLen * 8), c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0)
|
||||
{
|
||||
// read directly
|
||||
string retval = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, byteLen);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = PeekBytes(byteLen);
|
||||
return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
548
Lidgren.Network/NetIncomingMessage.Read.cs
Normal file
548
Lidgren.Network/NetIncomingMessage.Read.cs
Normal file
@@ -0,0 +1,548 @@
|
||||
/* 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.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetIncomingMessage
|
||||
{
|
||||
private const string c_readOverflowError = "Trying to read past the buffer size - likely caused by mismatching Write/Reads, different size or order.";
|
||||
|
||||
private static Dictionary<Type, MethodInfo> s_readMethods;
|
||||
|
||||
private int m_readPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the read position in the buffer, in bits (not bytes)
|
||||
/// </summary>
|
||||
public int Position
|
||||
{
|
||||
get { return m_readPosition; }
|
||||
set { m_readPosition = value; }
|
||||
}
|
||||
|
||||
static NetIncomingMessage()
|
||||
{
|
||||
Type[] integralTypes = typeof(Byte).Assembly.GetTypes();
|
||||
|
||||
s_readMethods = new Dictionary<Type, MethodInfo>();
|
||||
MethodInfo[] methods = typeof(NetIncomingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
|
||||
foreach (MethodInfo mi in methods)
|
||||
{
|
||||
if (mi.GetParameters().Length == 0 && mi.Name.StartsWith("Read", StringComparison.InvariantCulture))
|
||||
{
|
||||
string n = mi.Name.Substring(4);
|
||||
foreach (Type it in integralTypes)
|
||||
{
|
||||
if (it.Name == n)
|
||||
s_readMethods[it] = mi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
public bool ReadBoolean()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 1, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 1, m_readPosition);
|
||||
m_readPosition += 1;
|
||||
return (retval > 0 ? true : false);
|
||||
}
|
||||
|
||||
//
|
||||
// 8 bit
|
||||
//
|
||||
public byte ReadByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
m_readPosition += 8;
|
||||
return retval;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public sbyte ReadSByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
m_readPosition += 8;
|
||||
return (sbyte)retval;
|
||||
}
|
||||
|
||||
public byte ReadByte(int numberOfBits)
|
||||
{
|
||||
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
|
||||
m_readPosition += numberOfBits;
|
||||
return retval;
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(int numberOfBytes)
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
|
||||
|
||||
byte[] retval = new byte[numberOfBytes];
|
||||
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, retval, 0);
|
||||
m_readPosition += (8 * numberOfBytes);
|
||||
return retval;
|
||||
}
|
||||
|
||||
public void ReadBytes(byte[] into, int offset, int numberOfBytes)
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= (numberOfBytes * 8), c_readOverflowError);
|
||||
NetException.Assert(offset + numberOfBytes <= into.Length);
|
||||
|
||||
NetBitWriter.ReadBytes(m_data, numberOfBytes, m_readPosition, into, offset);
|
||||
m_readPosition += (8 * numberOfBytes);
|
||||
return;
|
||||
}
|
||||
|
||||
public void ReadBits(byte[] into, int offset, int numberOfBits)
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
|
||||
NetException.Assert(offset + NetUtility.BytesToHoldBits(numberOfBits) <= into.Length);
|
||||
|
||||
int numberOfWholeBytes = numberOfBits / 8;
|
||||
int extraBits = numberOfBits - (numberOfWholeBytes * 8);
|
||||
|
||||
NetBitWriter.ReadBytes(m_data, numberOfWholeBytes, m_readPosition, into, offset);
|
||||
m_readPosition += (8 * numberOfWholeBytes);
|
||||
|
||||
if (extraBits > 0)
|
||||
into[offset + numberOfWholeBytes] = ReadByte(extraBits);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// 16 bit
|
||||
//
|
||||
public Int16 ReadInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
|
||||
m_readPosition += 16;
|
||||
return (short)retval;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt16 ReadUInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
|
||||
m_readPosition += 16;
|
||||
return (ushort)retval;
|
||||
}
|
||||
|
||||
//
|
||||
// 32 bit
|
||||
//
|
||||
public Int32 ReadInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
m_readPosition += 32;
|
||||
return (Int32)retval;
|
||||
}
|
||||
|
||||
public Int32 ReadInt32(int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadInt() can only read between 1 and 32 bits");
|
||||
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
|
||||
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
|
||||
m_readPosition += numberOfBits;
|
||||
|
||||
if (numberOfBits == 32)
|
||||
return (int)retval;
|
||||
|
||||
int signBit = 1 << (numberOfBits - 1);
|
||||
if ((retval & signBit) == 0)
|
||||
return (int)retval; // positive
|
||||
|
||||
// negative
|
||||
unchecked
|
||||
{
|
||||
uint mask = ((uint)-1) >> (33 - numberOfBits);
|
||||
uint tmp = (retval & mask) + 1;
|
||||
return -((int)tmp);
|
||||
}
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt32 ReadUInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
m_readPosition += 32;
|
||||
return retval;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt32 ReadUInt32(int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "ReadUInt() can only read between 1 and 32 bits");
|
||||
//NetException.Assert(m_bitLength - m_readBitPtr >= numberOfBits, "tried to read past buffer size");
|
||||
|
||||
UInt32 retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
|
||||
m_readPosition += numberOfBits;
|
||||
return retval;
|
||||
}
|
||||
|
||||
//
|
||||
// 64 bit
|
||||
//
|
||||
[CLSCompliant(false)]
|
||||
public UInt64 ReadUInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
|
||||
ulong low = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
m_readPosition += 32;
|
||||
ulong high = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
|
||||
ulong retval = low + (high << 32);
|
||||
|
||||
m_readPosition += 32;
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Int64 ReadInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
unchecked
|
||||
{
|
||||
ulong retval = ReadUInt64();
|
||||
long longRetval = (long)retval;
|
||||
return longRetval;
|
||||
}
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public UInt64 ReadUInt64(int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits");
|
||||
NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError);
|
||||
|
||||
ulong retval;
|
||||
if (numberOfBits <= 32)
|
||||
{
|
||||
retval = (ulong)NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
retval |= NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition) << 32;
|
||||
}
|
||||
m_readPosition += numberOfBits;
|
||||
return retval;
|
||||
}
|
||||
|
||||
public Int64 ReadInt64(int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((numberOfBits > 0) && (numberOfBits < 65)), "ReadInt64(bits) can only read between 1 and 64 bits");
|
||||
return (long)ReadUInt64(numberOfBits);
|
||||
}
|
||||
|
||||
//
|
||||
// Floating point
|
||||
//
|
||||
public float ReadFloat()
|
||||
{
|
||||
return ReadSingle();
|
||||
}
|
||||
|
||||
public float ReadSingle()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
// endianness is handled inside BitConverter.ToSingle
|
||||
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
|
||||
m_readPosition += 32;
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = ReadBytes(4);
|
||||
return BitConverter.ToSingle(bytes, 0); // endianness is handled inside BitConverter.ToSingle
|
||||
}
|
||||
|
||||
public double ReadDouble()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
// read directly
|
||||
double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3);
|
||||
m_readPosition += 64;
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = ReadBytes(8);
|
||||
return BitConverter.ToDouble(bytes, 0); // endianness is handled inside BitConverter.ToSingle
|
||||
}
|
||||
|
||||
//
|
||||
// Variable bit count
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt32 written using WriteVariableUInt32()
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public uint ReadVariableUInt32()
|
||||
{
|
||||
int num1 = 0;
|
||||
int num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
if (num2 == 0x23)
|
||||
throw new FormatException("Bad 7-bit encoded integer");
|
||||
|
||||
byte num3 = this.ReadByte();
|
||||
num1 |= (num3 & 0x7f) << num2;
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
return (uint)num1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a Int32 written using WriteVariableInt32()
|
||||
/// </summary>
|
||||
public int ReadVariableInt32()
|
||||
{
|
||||
int num1 = 0;
|
||||
int num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
if (num2 == 0x23)
|
||||
throw new FormatException("Bad 7-bit encoded integer");
|
||||
|
||||
byte num3 = this.ReadByte();
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
int sign = (num1 << 31) >> 31;
|
||||
return sign ^ (num1 >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt32 written using WriteVariableInt64()
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public UInt64 ReadVariableUInt64()
|
||||
{
|
||||
UInt64 num1 = 0;
|
||||
int num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
if (num2 == 0x23)
|
||||
throw new FormatException("Bad 7-bit encoded integer");
|
||||
|
||||
byte num3 = this.ReadByte();
|
||||
num1 |= ((UInt64)num3 & 0x7f) << num2;
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
return num1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a float written using WriteSignedSingle()
|
||||
/// </summary>
|
||||
public float ReadSignedSingle(int numberOfBits)
|
||||
{
|
||||
uint encodedVal = ReadUInt32(numberOfBits);
|
||||
int maxVal = (1 << numberOfBits) - 1;
|
||||
return ((float)(encodedVal + 1) / (float)(maxVal + 1) - 0.5f) * 2.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a float written using WriteUnitSingle()
|
||||
/// </summary>
|
||||
public float ReadUnitSingle(int numberOfBits)
|
||||
{
|
||||
uint encodedVal = ReadUInt32(numberOfBits);
|
||||
int maxVal = (1 << numberOfBits) - 1;
|
||||
return (float)(encodedVal + 1) / (float)(maxVal + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a float written using WriteRangedSingle() using the same MIN and MAX values
|
||||
/// </summary>
|
||||
public float ReadRangedSingle(float min, float max, int numberOfBits)
|
||||
{
|
||||
float range = max - min;
|
||||
int maxVal = (1 << numberOfBits) - 1;
|
||||
float encodedVal = (float)ReadUInt32(numberOfBits);
|
||||
float unit = encodedVal / (float)maxVal;
|
||||
return min + (unit * range);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an integer written using WriteRangedInteger() using the same min/max values
|
||||
/// </summary>
|
||||
public int ReadRangedInteger(int min, int max)
|
||||
{
|
||||
uint range = (uint)(max - min);
|
||||
int numBits = NetUtility.BitsToHoldUInt(range);
|
||||
|
||||
uint rvalue = ReadUInt32(numBits);
|
||||
return (int)(min + rvalue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a string
|
||||
/// </summary>
|
||||
public string ReadString()
|
||||
{
|
||||
int byteLen = (int)ReadVariableUInt32();
|
||||
|
||||
if (byteLen == 0)
|
||||
return String.Empty;
|
||||
|
||||
NetException.Assert(m_bitLength - m_readPosition >= (byteLen * 8), c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0)
|
||||
{
|
||||
// read directly
|
||||
string retval = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, byteLen);
|
||||
m_readPosition += (8 * byteLen);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = ReadBytes(byteLen);
|
||||
return System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a stored IPv4 endpoint description
|
||||
/// </summary>
|
||||
public IPEndPoint ReadIPEndpoint()
|
||||
{
|
||||
byte len = ReadByte();
|
||||
byte[] addressBytes = ReadBytes(len);
|
||||
int port = (int)ReadUInt16();
|
||||
|
||||
IPAddress address = new IPAddress(addressBytes);
|
||||
return new IPEndPoint(address, port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes.
|
||||
/// </summary>
|
||||
public void SkipPadBits()
|
||||
{
|
||||
m_readPosition = ((m_readPosition + 7) >> 3) * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads data with the specified number of bits.
|
||||
/// </summary>
|
||||
public void SkipPadBits(int numberOfBits)
|
||||
{
|
||||
m_readPosition += numberOfBits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all public and private declared instance fields of the object in declaration order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllFields(object target)
|
||||
{
|
||||
ReadAllFields(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all fields with the specified binding of the object in declaration order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllFields(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
foreach (FieldInfo fi in fields)
|
||||
{
|
||||
object value;
|
||||
|
||||
// find read method
|
||||
MethodInfo readMethod;
|
||||
if (s_readMethods.TryGetValue(fi.FieldType, out readMethod))
|
||||
{
|
||||
// read value
|
||||
value = readMethod.Invoke(this, null);
|
||||
|
||||
// set the value
|
||||
fi.SetValue(target, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all public and private declared instance fields of the object in declaration order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllProperties(object target)
|
||||
{
|
||||
ReadAllProperties(target, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all fields with the specified binding of the object in declaration order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllProperties(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
foreach (PropertyInfo fi in fields)
|
||||
{
|
||||
object value;
|
||||
|
||||
// find read method
|
||||
MethodInfo readMethod;
|
||||
if (s_readMethods.TryGetValue(fi.PropertyType, out readMethod))
|
||||
{
|
||||
// read value
|
||||
value = readMethod.Invoke(this, null);
|
||||
|
||||
// set the value
|
||||
MethodInfo setMethod = fi.GetSetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
|
||||
setMethod.Invoke(target, new object[] { value });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
476
Lidgren.Network/NetIncomingMessage.Write.cs
Normal file
476
Lidgren.Network/NetIncomingMessage.Write.cs
Normal file
@@ -0,0 +1,476 @@
|
||||
/* 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.Net;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetIncomingMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures the buffer can hold this number of bits
|
||||
/// </summary>
|
||||
private void InternalEnsureBufferSize(int numberOfBits)
|
||||
{
|
||||
int byteLen = ((numberOfBits + 7) >> 3);
|
||||
if (m_data == null)
|
||||
{
|
||||
m_data = new byte[byteLen];
|
||||
return;
|
||||
}
|
||||
if (m_data.Length < byteLen)
|
||||
Array.Resize<byte>(ref m_data, byteLen);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
internal void Write(bool value)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 1);
|
||||
NetBitWriter.WriteByte((value ? (byte)1 : (byte)0), 1, m_data, m_bitLength);
|
||||
m_bitLength += 1;
|
||||
}
|
||||
|
||||
//
|
||||
// 8 bit
|
||||
//
|
||||
internal void Write(byte source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 8);
|
||||
NetBitWriter.WriteByte(source, 8, m_data, m_bitLength);
|
||||
m_bitLength += 8;
|
||||
}
|
||||
|
||||
internal void Write(sbyte source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 8);
|
||||
NetBitWriter.WriteByte((byte)source, 8, m_data, m_bitLength);
|
||||
m_bitLength += 8;
|
||||
}
|
||||
|
||||
internal void Write(byte source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 8), "Write(byte, numberOfBits) can only write between 1 and 8 bits");
|
||||
InternalEnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteByte(source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
internal void Write(byte[] source)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
int bits = source.Length * 8;
|
||||
InternalEnsureBufferSize(m_bitLength + bits);
|
||||
NetBitWriter.WriteBytes(source, 0, source.Length, m_data, m_bitLength);
|
||||
m_bitLength += bits;
|
||||
}
|
||||
|
||||
internal void Write(byte[] source, int offsetInBytes, int numberOfBytes)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
int bits = numberOfBytes * 8;
|
||||
InternalEnsureBufferSize(m_bitLength + bits);
|
||||
NetBitWriter.WriteBytes(source, offsetInBytes, numberOfBytes, m_data, m_bitLength);
|
||||
m_bitLength += bits;
|
||||
}
|
||||
|
||||
//
|
||||
// 16 bit
|
||||
//
|
||||
internal void Write(UInt16 source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 16);
|
||||
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
|
||||
m_bitLength += 16;
|
||||
}
|
||||
|
||||
internal void Write(UInt16 source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 16), "Write(ushort, numberOfBits) can only write between 1 and 16 bits");
|
||||
InternalEnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
internal void Write(Int16 source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 16);
|
||||
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
|
||||
m_bitLength += 16;
|
||||
}
|
||||
|
||||
//
|
||||
// 32 bit
|
||||
//
|
||||
#if UNSAFE
|
||||
internal unsafe void Write(Int32 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 32);
|
||||
|
||||
// can write fast?
|
||||
if (m_bitLength % 8 == 0)
|
||||
{
|
||||
fixed (byte* numRef = &Data[m_bitLength / 8])
|
||||
{
|
||||
*((int*)numRef) = source;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NetBitWriter.WriteUInt32((UInt32)source, 32, Data, m_bitLength);
|
||||
}
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#else
|
||||
internal void Write(Int32 source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 32);
|
||||
NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, m_bitLength);
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNSAFE
|
||||
internal unsafe void Write(UInt32 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 32);
|
||||
|
||||
// can write fast?
|
||||
if (m_bitLength % 8 == 0)
|
||||
{
|
||||
fixed (byte* numRef = &Data[m_bitLength / 8])
|
||||
{
|
||||
*((uint*)numRef) = source;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NetBitWriter.WriteUInt32(source, 32, Data, m_bitLength);
|
||||
}
|
||||
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#else
|
||||
internal void Write(UInt32 source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 32);
|
||||
NetBitWriter.WriteUInt32(source, 32, m_data, m_bitLength);
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#endif
|
||||
|
||||
internal void Write(UInt32 source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(uint, numberOfBits) can only write between 1 and 32 bits");
|
||||
InternalEnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteUInt32(source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
internal void Write(Int32 source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(int, numberOfBits) can only write between 1 and 32 bits");
|
||||
InternalEnsureBufferSize(m_bitLength + numberOfBits);
|
||||
|
||||
if (numberOfBits != 32)
|
||||
{
|
||||
// make first bit sign
|
||||
int signBit = 1 << (numberOfBits - 1);
|
||||
if (source < 0)
|
||||
source = (-source - 1) | signBit;
|
||||
else
|
||||
source &= (~signBit);
|
||||
}
|
||||
|
||||
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
|
||||
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
//
|
||||
// 64 bit
|
||||
//
|
||||
internal void Write(UInt64 source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 64);
|
||||
NetBitWriter.WriteUInt64(source, 64, m_data, m_bitLength);
|
||||
m_bitLength += 64;
|
||||
}
|
||||
|
||||
internal void Write(UInt64 source, int numberOfBits)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteUInt64(source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
internal void Write(Int64 source)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 64);
|
||||
ulong usource = (ulong)source;
|
||||
NetBitWriter.WriteUInt64(usource, 64, m_data, m_bitLength);
|
||||
m_bitLength += 64;
|
||||
}
|
||||
|
||||
internal void Write(Int64 source, int numberOfBits)
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + numberOfBits);
|
||||
ulong usource = (ulong)source;
|
||||
NetBitWriter.WriteUInt64(usource, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
//
|
||||
// Floating point
|
||||
//
|
||||
#if UNSAFE
|
||||
internal unsafe void Write(float source)
|
||||
{
|
||||
uint val = *((uint*)&source);
|
||||
#if BIGENDIAN
|
||||
val = NetUtility.SwapByteOrder(val);
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#else
|
||||
internal void Write(float source)
|
||||
{
|
||||
byte[] val = BitConverter.GetBytes(source);
|
||||
#if BIGENDIAN
|
||||
// swap byte order
|
||||
byte tmp = val[3];
|
||||
val[3] = val[0];
|
||||
val[0] = tmp;
|
||||
tmp = val[2];
|
||||
val[2] = val[1];
|
||||
val[1] = tmp;
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNSAFE
|
||||
internal unsafe void Write(double source)
|
||||
{
|
||||
ulong val = *((ulong*)&source);
|
||||
#if BIGENDIAN
|
||||
val = NetUtility.SwapByteOrder(val);
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#else
|
||||
internal void Write(double source)
|
||||
{
|
||||
byte[] val = BitConverter.GetBytes(source);
|
||||
#if BIGENDIAN
|
||||
// 0 1 2 3 4 5 6 7
|
||||
|
||||
// swap byte order
|
||||
byte tmp = val[7];
|
||||
val[7] = val[0];
|
||||
val[0] = tmp;
|
||||
|
||||
tmp = val[6];
|
||||
val[6] = val[1];
|
||||
val[1] = tmp;
|
||||
|
||||
tmp = val[5];
|
||||
val[5] = val[2];
|
||||
val[2] = tmp;
|
||||
|
||||
tmp = val[4];
|
||||
val[4] = val[3];
|
||||
val[3] = tmp;
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Variable bits
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Write Base128 encoded variable sized unsigned integer
|
||||
/// </summary>
|
||||
/// <returns>number of bytes written</returns>
|
||||
internal int WriteVariableUInt32(uint value)
|
||||
{
|
||||
int retval = 1;
|
||||
uint num1 = (uint)value;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
this.Write((byte)(num1 | 0x80));
|
||||
num1 = num1 >> 7;
|
||||
retval++;
|
||||
}
|
||||
this.Write((byte)num1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write Base128 encoded variable sized signed integer
|
||||
/// </summary>
|
||||
/// <returns>number of bytes written</returns>
|
||||
internal int WriteVariableInt32(int value)
|
||||
{
|
||||
int retval = 1;
|
||||
uint num1 = (uint)((value << 1) ^ (value >> 31));
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
this.Write((byte)(num1 | 0x80));
|
||||
num1 = num1 >> 7;
|
||||
retval++;
|
||||
}
|
||||
this.Write((byte)num1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write Base128 encoded variable sized unsigned integer
|
||||
/// </summary>
|
||||
/// <returns>number of bytes written</returns>
|
||||
internal int WriteVariableUInt64(UInt64 value)
|
||||
{
|
||||
int retval = 1;
|
||||
UInt64 num1 = (UInt64)value;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
this.Write((byte)(num1 | 0x80));
|
||||
num1 = num1 >> 7;
|
||||
retval++;
|
||||
}
|
||||
this.Write((byte)num1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compress (lossy) a float in the range -1..1 using numberOfBits bits
|
||||
/// </summary>
|
||||
internal void WriteSignedSingle(float value, int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((value >= -1.0) && (value <= 1.0)), " WriteSignedSingle() must be passed a float in the range -1 to 1; val is " + value);
|
||||
|
||||
float unit = (value + 1.0f) * 0.5f;
|
||||
int maxVal = (1 << numberOfBits) - 1;
|
||||
uint writeVal = (uint)(unit * (float)maxVal);
|
||||
|
||||
Write(writeVal, numberOfBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compress (lossy) a float in the range 0..1 using numberOfBits bits
|
||||
/// </summary>
|
||||
internal void WriteUnitSingle(float value, int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((value >= 0.0) && (value <= 1.0)), " WriteUnitSingle() must be passed a float in the range 0 to 1; val is " + value);
|
||||
|
||||
int maxValue = (1 << numberOfBits) - 1;
|
||||
uint writeVal = (uint)(value * (float)maxValue);
|
||||
|
||||
Write(writeVal, numberOfBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compress a float within a specified range using a certain number of bits
|
||||
/// </summary>
|
||||
internal void WriteRangedSingle(float value, float min, float max, int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((value >= min) && (value <= max)), " WriteRangedSingle() must be passed a float in the range MIN to MAX; val is " + value);
|
||||
|
||||
float range = max - min;
|
||||
float unit = ((value - min) / range);
|
||||
int maxVal = (1 << numberOfBits) - 1;
|
||||
Write((UInt32)((float)maxVal * unit), numberOfBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an integer with the least amount of bits need for the specified range
|
||||
/// Returns number of bits written
|
||||
/// </summary>
|
||||
internal int WriteRangedInteger(int min, int max, int value)
|
||||
{
|
||||
NetException.Assert(value >= min && value <= max, "Value not within min/max range!");
|
||||
|
||||
uint range = (uint)(max - min);
|
||||
int numBits = NetUtility.BitsToHoldUInt(range);
|
||||
|
||||
uint rvalue = (uint)(value - min);
|
||||
Write(rvalue, numBits);
|
||||
|
||||
return numBits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a string
|
||||
/// </summary>
|
||||
internal void Write(string source)
|
||||
{
|
||||
if (string.IsNullOrEmpty(source))
|
||||
{
|
||||
InternalEnsureBufferSize(m_bitLength + 8);
|
||||
WriteVariableUInt32(0);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(source);
|
||||
InternalEnsureBufferSize(m_bitLength + (bytes.Length > 127 ? 2 * 8 : 1 * 8) + (bytes.Length * 8));
|
||||
WriteVariableUInt32((uint)bytes.Length);
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an endpoint description
|
||||
/// </summary>
|
||||
/// <param name="endPoint"></param>
|
||||
internal void Write(IPEndPoint endPoint)
|
||||
{
|
||||
byte[] bytes = endPoint.Address.GetAddressBytes();
|
||||
Write((byte)bytes.Length);
|
||||
Write(bytes);
|
||||
Write((ushort)endPoint.Port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes.
|
||||
/// </summary>
|
||||
internal void WritePadBits()
|
||||
{
|
||||
m_bitLength = ((m_bitLength + 7) / 8) * 8;
|
||||
InternalEnsureBufferSize(m_bitLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads data with the specified number of bits.
|
||||
/// </summary>
|
||||
internal void WritePadBits(int numberOfBits)
|
||||
{
|
||||
m_bitLength += numberOfBits;
|
||||
InternalEnsureBufferSize(m_bitLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Lidgren.Network/NetIncomingMessage.cs
Normal file
123
Lidgren.Network/NetIncomingMessage.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
/* 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.Net;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal enum NetIncomingMessageReleaseStatus
|
||||
{
|
||||
NotReleased = 0,
|
||||
ReleasedToApplication,
|
||||
RecycledByApplication
|
||||
}
|
||||
|
||||
[DebuggerDisplay("{m_readPosition} of {m_bitLength} bits ({LengthBytes} bytes) read")]
|
||||
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 IPEndPoint m_senderEndpoint;
|
||||
internal NetConnection m_senderConnection;
|
||||
|
||||
internal NetFragmentationInfo m_fragmentationInfo;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data in number of bytes
|
||||
/// </summary>
|
||||
public int LengthBytes
|
||||
{
|
||||
get { return ((m_bitLength + 7) >> 3); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data in number of bits
|
||||
/// </summary>
|
||||
public int LengthBits
|
||||
{
|
||||
get { return m_bitLength; }
|
||||
}
|
||||
|
||||
/// <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; } }
|
||||
|
||||
/// <summary>
|
||||
/// IPEndPoint of sender, if any
|
||||
/// </summary>
|
||||
public IPEndPoint SenderEndpoint { get { return m_senderEndpoint; } }
|
||||
|
||||
/// <summary>
|
||||
/// NetConnection of sender, if any
|
||||
/// </summary>
|
||||
public NetConnection SenderConnection { get { return m_senderConnection; } }
|
||||
|
||||
internal NetIncomingMessage()
|
||||
{
|
||||
}
|
||||
|
||||
internal NetIncomingMessage(byte[] data, int dataLength)
|
||||
{
|
||||
m_data = data;
|
||||
m_bitLength = dataLength * 8;
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_bitLength = 0;
|
||||
m_readPosition = 0;
|
||||
m_fragmentationInfo = null;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[NetIncomingMessage {0}, {1}|{2}, {3} bits]",
|
||||
m_incomingType,
|
||||
m_messageType,
|
||||
m_sequenceNumber,
|
||||
m_bitLength
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Lidgren.Network/NetIncomingMessageType.cs
Normal file
45
Lidgren.Network/NetIncomingMessageType.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
/* 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.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of incoming message
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
|
||||
public enum NetIncomingMessageType
|
||||
{
|
||||
// library note: values are power-of-two, but they are not flags - it's a convenience for NetPeerConfiguration.DisabledMessageTypes
|
||||
Error = 0,
|
||||
StatusChanged = 1 << 0, // Data (string)
|
||||
UnconnectedData = 1 << 1, // Data Based on data received
|
||||
ConnectionApproval = 1 << 2, // Data
|
||||
Data = 1 << 3, // Data Based on data received
|
||||
Receipt = 1 << 4, // Data
|
||||
DiscoveryRequest = 1 << 5, // (no data)
|
||||
DiscoveryResponse = 1 << 6, // Data
|
||||
VerboseDebugMessage = 1 << 7, // Data (string)
|
||||
DebugMessage = 1 << 8, // Data (string)
|
||||
WarningMessage = 1 << 9, // Data (string)
|
||||
ErrorMessage = 1 << 10, // Data (string)
|
||||
}
|
||||
}
|
||||
166
Lidgren.Network/NetMessageType.cs
Normal file
166
Lidgren.Network/NetMessageType.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
/* 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;
|
||||
|
||||
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,
|
||||
NatIntroduction = 11,
|
||||
}
|
||||
|
||||
internal enum NetMessageType : byte
|
||||
{
|
||||
Error = 0,
|
||||
|
||||
Library = 1, // NetMessageLibraryType byte follows
|
||||
|
||||
UserUnreliable = 2,
|
||||
|
||||
// 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 = 35,
|
||||
|
||||
// 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,
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
618
Lidgren.Network/NetOutgoingMessage.Write.cs
Normal file
618
Lidgren.Network/NetOutgoingMessage.Write.cs
Normal file
@@ -0,0 +1,618 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public sealed partial class NetOutgoingMessage
|
||||
{
|
||||
private const int c_overAllocateAmount = 4;
|
||||
|
||||
private static Dictionary<Type, MethodInfo> s_writeMethods;
|
||||
|
||||
internal byte[] m_data;
|
||||
internal int m_bitLength;
|
||||
|
||||
static NetOutgoingMessage()
|
||||
{
|
||||
s_writeMethods = new Dictionary<Type, MethodInfo>();
|
||||
MethodInfo[] methods = typeof(NetOutgoingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
|
||||
foreach (MethodInfo mi in methods)
|
||||
{
|
||||
if (mi.Name == "Write")
|
||||
{
|
||||
ParameterInfo[] pis = mi.GetParameters();
|
||||
if (pis.Length == 1)
|
||||
s_writeMethods[pis[0].ParameterType] = mi;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the internal data buffer, don't modify
|
||||
/// </summary>
|
||||
public byte[] PeekDataBuffer()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the buffer in bytes
|
||||
/// </summary>
|
||||
public int LengthBytes
|
||||
{
|
||||
get { return ((m_bitLength + 7) >> 3); }
|
||||
set
|
||||
{
|
||||
m_bitLength = value * 8;
|
||||
InternalEnsureBufferSize(m_bitLength);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the buffer in bits
|
||||
/// </summary>
|
||||
public int LengthBits
|
||||
{
|
||||
get { return m_bitLength; }
|
||||
set
|
||||
{
|
||||
m_bitLength = value;
|
||||
InternalEnsureBufferSize(m_bitLength);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the buffer can hold this number of bits
|
||||
/// </summary>
|
||||
public void EnsureBufferSize(int numberOfBits)
|
||||
{
|
||||
int byteLen = ((numberOfBits + 7) >> 3);
|
||||
if (m_data == null)
|
||||
{
|
||||
m_data = new byte[byteLen + c_overAllocateAmount];
|
||||
return;
|
||||
}
|
||||
if (m_data.Length < byteLen)
|
||||
Array.Resize<byte>(ref m_data, byteLen + c_overAllocateAmount);
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the buffer can hold this number of bits
|
||||
/// </summary>
|
||||
public void InternalEnsureBufferSize(int numberOfBits)
|
||||
{
|
||||
int byteLen = ((numberOfBits + 7) >> 3);
|
||||
if (m_data == null)
|
||||
{
|
||||
m_data = new byte[byteLen];
|
||||
return;
|
||||
}
|
||||
if (m_data.Length < byteLen)
|
||||
Array.Resize<byte>(ref m_data, byteLen);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
public void Write(bool value)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 1);
|
||||
NetBitWriter.WriteByte((value ? (byte)1 : (byte)0), 1, m_data, m_bitLength);
|
||||
m_bitLength += 1;
|
||||
}
|
||||
|
||||
//
|
||||
// 8 bit
|
||||
//
|
||||
public void Write(byte source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 8);
|
||||
NetBitWriter.WriteByte(source, 8, m_data, m_bitLength);
|
||||
m_bitLength += 8;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public void Write(sbyte source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 8);
|
||||
NetBitWriter.WriteByte((byte)source, 8, m_data, m_bitLength);
|
||||
m_bitLength += 8;
|
||||
}
|
||||
|
||||
public void Write(byte source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 8), "Write(byte, numberOfBits) can only write between 1 and 8 bits");
|
||||
EnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteByte(source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
public void Write(byte[] source)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
int bits = source.Length * 8;
|
||||
EnsureBufferSize(m_bitLength + bits);
|
||||
NetBitWriter.WriteBytes(source, 0, source.Length, m_data, m_bitLength);
|
||||
m_bitLength += bits;
|
||||
}
|
||||
|
||||
public void Write(byte[] source, int offsetInBytes, int numberOfBytes)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException("source");
|
||||
int bits = numberOfBytes * 8;
|
||||
EnsureBufferSize(m_bitLength + bits);
|
||||
NetBitWriter.WriteBytes(source, offsetInBytes, numberOfBytes, m_data, m_bitLength);
|
||||
m_bitLength += bits;
|
||||
}
|
||||
|
||||
//
|
||||
// 16 bit
|
||||
//
|
||||
[CLSCompliant(false)]
|
||||
public void Write(UInt16 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 16);
|
||||
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
|
||||
m_bitLength += 16;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public void Write(UInt16 source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 16), "Write(ushort, numberOfBits) can only write between 1 and 16 bits");
|
||||
EnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
public void Write(Int16 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 16);
|
||||
NetBitWriter.WriteUInt32((uint)source, 16, m_data, m_bitLength);
|
||||
m_bitLength += 16;
|
||||
}
|
||||
|
||||
//
|
||||
// 32 bit
|
||||
//
|
||||
#if UNSAFE
|
||||
public unsafe void Write(Int32 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 32);
|
||||
|
||||
// can write fast?
|
||||
if (m_bitLength % 8 == 0)
|
||||
{
|
||||
fixed (byte* numRef = &Data[m_bitLength / 8])
|
||||
{
|
||||
*((int*)numRef) = source;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NetBitWriter.WriteUInt32((UInt32)source, 32, Data, m_bitLength);
|
||||
}
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#else
|
||||
public void Write(Int32 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 32);
|
||||
NetBitWriter.WriteUInt32((UInt32)source, 32, m_data, m_bitLength);
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNSAFE
|
||||
public unsafe void Write(UInt32 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 32);
|
||||
|
||||
// can write fast?
|
||||
if (m_bitLength % 8 == 0)
|
||||
{
|
||||
fixed (byte* numRef = &Data[m_bitLength / 8])
|
||||
{
|
||||
*((uint*)numRef) = source;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NetBitWriter.WriteUInt32(source, 32, Data, m_bitLength);
|
||||
}
|
||||
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#else
|
||||
[CLSCompliant(false)]
|
||||
public void Write(UInt32 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 32);
|
||||
NetBitWriter.WriteUInt32(source, 32, m_data, m_bitLength);
|
||||
m_bitLength += 32;
|
||||
}
|
||||
#endif
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public void Write(UInt32 source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(uint, numberOfBits) can only write between 1 and 32 bits");
|
||||
EnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteUInt32(source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
public void Write(Int32 source, int numberOfBits)
|
||||
{
|
||||
NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(int, numberOfBits) can only write between 1 and 32 bits");
|
||||
EnsureBufferSize(m_bitLength + numberOfBits);
|
||||
|
||||
if (numberOfBits != 32)
|
||||
{
|
||||
// make first bit sign
|
||||
int signBit = 1 << (numberOfBits - 1);
|
||||
if (source < 0)
|
||||
source = (-source - 1) | signBit;
|
||||
else
|
||||
source &= (~signBit);
|
||||
}
|
||||
|
||||
NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength);
|
||||
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
//
|
||||
// 64 bit
|
||||
//
|
||||
[CLSCompliant(false)]
|
||||
public void Write(UInt64 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 64);
|
||||
NetBitWriter.WriteUInt64(source, 64, m_data, m_bitLength);
|
||||
m_bitLength += 64;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public void Write(UInt64 source, int numberOfBits)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + numberOfBits);
|
||||
NetBitWriter.WriteUInt64(source, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
public void Write(Int64 source)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 64);
|
||||
ulong usource = (ulong)source;
|
||||
NetBitWriter.WriteUInt64(usource, 64, m_data, m_bitLength);
|
||||
m_bitLength += 64;
|
||||
}
|
||||
|
||||
public void Write(Int64 source, int numberOfBits)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + numberOfBits);
|
||||
ulong usource = (ulong)source;
|
||||
NetBitWriter.WriteUInt64(usource, numberOfBits, m_data, m_bitLength);
|
||||
m_bitLength += numberOfBits;
|
||||
}
|
||||
|
||||
//
|
||||
// Floating point
|
||||
//
|
||||
#if UNSAFE
|
||||
public unsafe void Write(float source)
|
||||
{
|
||||
uint val = *((uint*)&source);
|
||||
#if BIGENDIAN
|
||||
val = NetUtility.SwapByteOrder(val);
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#else
|
||||
public void Write(float source)
|
||||
{
|
||||
byte[] val = BitConverter.GetBytes(source);
|
||||
#if BIGENDIAN
|
||||
// swap byte order
|
||||
byte tmp = val[3];
|
||||
val[3] = val[0];
|
||||
val[0] = tmp;
|
||||
tmp = val[2];
|
||||
val[2] = val[1];
|
||||
val[1] = tmp;
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if UNSAFE
|
||||
public unsafe void Write(double source)
|
||||
{
|
||||
ulong val = *((ulong*)&source);
|
||||
#if BIGENDIAN
|
||||
val = NetUtility.SwapByteOrder(val);
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#else
|
||||
public void Write(double source)
|
||||
{
|
||||
byte[] val = BitConverter.GetBytes(source);
|
||||
#if BIGENDIAN
|
||||
// 0 1 2 3 4 5 6 7
|
||||
|
||||
// swap byte order
|
||||
byte tmp = val[7];
|
||||
val[7] = val[0];
|
||||
val[0] = tmp;
|
||||
|
||||
tmp = val[6];
|
||||
val[6] = val[1];
|
||||
val[1] = tmp;
|
||||
|
||||
tmp = val[5];
|
||||
val[5] = val[2];
|
||||
val[2] = tmp;
|
||||
|
||||
tmp = val[4];
|
||||
val[4] = val[3];
|
||||
val[3] = tmp;
|
||||
#endif
|
||||
Write(val);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Variable bits
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Write Base128 encoded variable sized unsigned integer
|
||||
/// </summary>
|
||||
/// <returns>number of bytes written</returns>
|
||||
[CLSCompliant(false)]
|
||||
public int WriteVariableUInt32(uint value)
|
||||
{
|
||||
int retval = 1;
|
||||
uint num1 = (uint)value;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
this.Write((byte)(num1 | 0x80));
|
||||
num1 = num1 >> 7;
|
||||
retval++;
|
||||
}
|
||||
this.Write((byte)num1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write Base128 encoded variable sized signed integer
|
||||
/// </summary>
|
||||
/// <returns>number of bytes written</returns>
|
||||
public int WriteVariableInt32(int value)
|
||||
{
|
||||
int retval = 1;
|
||||
uint num1 = (uint)((value << 1) ^ (value >> 31));
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
this.Write((byte)(num1 | 0x80));
|
||||
num1 = num1 >> 7;
|
||||
retval++;
|
||||
}
|
||||
this.Write((byte)num1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write Base128 encoded variable sized unsigned integer
|
||||
/// </summary>
|
||||
/// <returns>number of bytes written</returns>
|
||||
[CLSCompliant(false)]
|
||||
public int WriteVariableUInt64(UInt64 value)
|
||||
{
|
||||
int retval = 1;
|
||||
UInt64 num1 = (UInt64)value;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
this.Write((byte)(num1 | 0x80));
|
||||
num1 = num1 >> 7;
|
||||
retval++;
|
||||
}
|
||||
this.Write((byte)num1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compress (lossy) a float in the range -1..1 using numberOfBits bits
|
||||
/// </summary>
|
||||
public void WriteSignedSingle(float value, int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((value >= -1.0) && (value <= 1.0)), " WriteSignedSingle() must be passed a float in the range -1 to 1; val is " + value);
|
||||
|
||||
float unit = (value + 1.0f) * 0.5f;
|
||||
int maxVal = (1 << numberOfBits) - 1;
|
||||
uint writeVal = (uint)(unit * (float)maxVal);
|
||||
|
||||
Write(writeVal, numberOfBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compress (lossy) a float in the range 0..1 using numberOfBits bits
|
||||
/// </summary>
|
||||
public void WriteUnitSingle(float value, int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((value >= 0.0) && (value <= 1.0)), " WriteUnitSingle() must be passed a float in the range 0 to 1; val is " + value);
|
||||
|
||||
int maxValue = (1 << numberOfBits) - 1;
|
||||
uint writeVal = (uint)(value * (float)maxValue);
|
||||
|
||||
Write(writeVal, numberOfBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compress a float within a specified range using a certain number of bits
|
||||
/// </summary>
|
||||
public void WriteRangedSingle(float value, float min, float max, int numberOfBits)
|
||||
{
|
||||
NetException.Assert(((value >= min) && (value <= max)), " WriteRangedSingle() must be passed a float in the range MIN to MAX; val is " + value);
|
||||
|
||||
float range = max - min;
|
||||
float unit = ((value - min) / range);
|
||||
int maxVal = (1 << numberOfBits) - 1;
|
||||
Write((UInt32)((float)maxVal * unit), numberOfBits);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an integer with the least amount of bits need for the specified range
|
||||
/// Returns number of bits written
|
||||
/// </summary>
|
||||
public int WriteRangedInteger(int min, int max, int value)
|
||||
{
|
||||
NetException.Assert(value >= min && value <= max, "Value not within min/max range!");
|
||||
|
||||
uint range = (uint)(max - min);
|
||||
int numBits = NetUtility.BitsToHoldUInt(range);
|
||||
|
||||
uint rvalue = (uint)(value - min);
|
||||
Write(rvalue, numBits);
|
||||
|
||||
return numBits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a string
|
||||
/// </summary>
|
||||
public void Write(string source)
|
||||
{
|
||||
if (string.IsNullOrEmpty(source))
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + 8);
|
||||
WriteVariableUInt32(0);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(source);
|
||||
EnsureBufferSize(m_bitLength + 1 + bytes.Length);
|
||||
WriteVariableUInt32((uint)bytes.Length);
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an endpoint description
|
||||
/// </summary>
|
||||
/// <param name="endPoint"></param>
|
||||
internal void Write(IPEndPoint endPoint)
|
||||
{
|
||||
byte[] bytes = endPoint.Address.GetAddressBytes();
|
||||
Write((byte)bytes.Length);
|
||||
Write(bytes);
|
||||
Write((ushort)endPoint.Port);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads data with enough bits to reach a full byte. Decreases cpu usage for subsequent byte writes.
|
||||
/// </summary>
|
||||
public void WritePadBits()
|
||||
{
|
||||
m_bitLength = ((m_bitLength + 7) >> 3) * 8;
|
||||
EnsureBufferSize(m_bitLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads data with the specified number of bits.
|
||||
/// </summary>
|
||||
public void WritePadBits(int numberOfBits)
|
||||
{
|
||||
m_bitLength += numberOfBits;
|
||||
EnsureBufferSize(m_bitLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes all public and private declared instance fields of the object in declaration order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllFields(object ob)
|
||||
{
|
||||
WriteAllFields(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes all fields with specified binding in declaration order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllFields(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
foreach (FieldInfo fi in fields)
|
||||
{
|
||||
object value = fi.GetValue(ob);
|
||||
|
||||
// find the appropriate Write method
|
||||
MethodInfo writeMethod;
|
||||
if (s_writeMethods.TryGetValue(fi.FieldType, out writeMethod))
|
||||
writeMethod.Invoke(this, new object[] { value });
|
||||
else
|
||||
throw new NetException("Failed to find write method for type " + fi.FieldType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes all public and private declared instance properties of the object in declaration order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllProperties(object ob)
|
||||
{
|
||||
WriteAllProperties(ob, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes all properties with specified binding in declaration order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllProperties(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
foreach (PropertyInfo fi in fields)
|
||||
{
|
||||
MethodInfo getMethod = fi.GetGetMethod((flags & BindingFlags.NonPublic) == BindingFlags.NonPublic);
|
||||
object value = getMethod.Invoke(ob, null);
|
||||
|
||||
// find the appropriate Write method
|
||||
MethodInfo writeMethod;
|
||||
if (s_writeMethods.TryGetValue(fi.PropertyType, out writeMethod))
|
||||
writeMethod.Invoke(this, new object[] { value });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append all the bits of message to this message
|
||||
/// </summary>
|
||||
public void Write(NetOutgoingMessage message)
|
||||
{
|
||||
EnsureBufferSize(m_bitLength + (message.LengthBytes * 8));
|
||||
|
||||
Write(message.m_data, 0, message.LengthBytes);
|
||||
|
||||
// did we write excessive bits?
|
||||
int bitsInLastByte = (message.m_bitLength % 8);
|
||||
if (bitsInLastByte != 0)
|
||||
{
|
||||
int excessBits = 8 - bitsInLastByte;
|
||||
m_bitLength -= excessBits;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
182
Lidgren.Network/NetOutgoingMessage.cs
Normal file
182
Lidgren.Network/NetOutgoingMessage.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
/* 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.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
[DebuggerDisplay("LengthBits={LengthBits}")]
|
||||
public sealed partial class NetOutgoingMessage
|
||||
{
|
||||
// reference count before message can be recycled
|
||||
internal int m_inQueueCount;
|
||||
|
||||
internal NetMessageType m_type;
|
||||
internal NetMessageLibraryType m_libType;
|
||||
internal ushort m_sequenceNumber;
|
||||
|
||||
internal IPEndPoint m_unconnectedRecipient;
|
||||
|
||||
internal double m_lastSentTime; // when was this message sent last?
|
||||
internal double m_nextResendTime; // when to resend this message the next time
|
||||
internal int m_numSends; // the number of times this message has been sent/resent
|
||||
|
||||
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_numSends > 0; } }
|
||||
|
||||
internal NetOutgoingMessage()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
NetException.Assert(m_inQueueCount == 0, "Ouch! Resetting NetOutgoingMessage still in some queue!");
|
||||
|
||||
m_bitLength = 0;
|
||||
m_type = NetMessageType.Error;
|
||||
m_inQueueCount = 0;
|
||||
m_numSends = 0;
|
||||
m_fragmentGroupId = -1;
|
||||
}
|
||||
|
||||
internal static int EncodeAcksMessage(byte[] buffer, int ptr, NetConnection conn, int maxBytesPayload)
|
||||
{
|
||||
// TODO: if appropriate; make bit vector of adjacent acks
|
||||
|
||||
buffer[ptr++] = (byte)NetMessageType.Library;
|
||||
buffer[ptr++] = (byte)NetMessageLibraryType.Acknowledge;
|
||||
|
||||
Queue<int> acks = conn.m_acknowledgesToSend;
|
||||
|
||||
int maxAcks = maxBytesPayload / 3;
|
||||
int acksToEncode = (acks.Count < maxAcks ? acks.Count : maxAcks);
|
||||
|
||||
int payloadBitsLength = acksToEncode * 3 * 8;
|
||||
if (payloadBitsLength < 127)
|
||||
{
|
||||
buffer[ptr++] = (byte)payloadBitsLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[ptr++] = (byte)((payloadBitsLength & 127) | 128);
|
||||
buffer[ptr++] = (byte)(payloadBitsLength >> 7);
|
||||
}
|
||||
|
||||
for (int i = 0; i < acksToEncode; i++)
|
||||
{
|
||||
int ack = acks.Dequeue();
|
||||
buffer[ptr++] = (byte)ack; // message type
|
||||
buffer[ptr++] = (byte)(ack >> 8); // seqnr low
|
||||
buffer[ptr++] = (byte)(ack >> 16); // seqnr high
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal int Encode(byte[] buffer, int ptr, NetConnection conn)
|
||||
{
|
||||
// message type
|
||||
buffer[ptr++] = (byte)((int)m_type | (m_fragmentGroupId == -1 ? 0 : 128));
|
||||
|
||||
if (m_type == NetMessageType.Library)
|
||||
buffer[ptr++] =(byte)m_libType;
|
||||
|
||||
// channel sequence number
|
||||
if (m_type >= NetMessageType.UserSequenced)
|
||||
{
|
||||
if (conn == null)
|
||||
throw new NetException("Trying to encode NetMessageType " + m_type + " to unconnected endpoint!");
|
||||
if (m_numSends == 0)
|
||||
m_sequenceNumber = conn.GetSendSequenceNumber(m_type);
|
||||
buffer[ptr++] = (byte)m_sequenceNumber;
|
||||
buffer[ptr++] = (byte)(m_sequenceNumber >> 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");
|
||||
}
|
||||
|
||||
// fragmentation info
|
||||
if (m_fragmentGroupId != -1)
|
||||
{
|
||||
buffer[ptr++] = (byte)m_fragmentGroupId;
|
||||
buffer[ptr++] = (byte)(m_fragmentGroupId >> 8);
|
||||
buffer[ptr++] = (byte)m_fragmentTotalCount;
|
||||
buffer[ptr++] = (byte)(m_fragmentTotalCount >> 8);
|
||||
buffer[ptr++] = (byte)m_fragmentNumber;
|
||||
buffer[ptr++] = (byte)(m_fragmentNumber >> 8);
|
||||
}
|
||||
|
||||
// payload
|
||||
if (payloadBitsLength > 0)
|
||||
{
|
||||
// zero out last byte
|
||||
buffer[ptr + payloadBytesLength] = 0;
|
||||
|
||||
Buffer.BlockCopy(m_data, 0, buffer, ptr, payloadBytesLength);
|
||||
ptr += payloadBytesLength;
|
||||
}
|
||||
|
||||
m_numSends++;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.Append("[NetOutgoingMessage ");
|
||||
bdr.Append(m_type.ToString());
|
||||
if (m_type == NetMessageType.Library)
|
||||
{
|
||||
bdr.Append('|');
|
||||
bdr.Append(m_libType.ToString());
|
||||
}
|
||||
bdr.Append(" #");
|
||||
bdr.Append(m_sequenceNumber);
|
||||
bdr.Append(" sent ");
|
||||
bdr.Append(m_numSends);
|
||||
bdr.Append(" times]");
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Lidgren.Network/NetPeer.ConnectionApproval.cs
Normal file
85
Lidgren.Network/NetPeer.ConnectionApproval.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal enum PendingConnectionStatus
|
||||
{
|
||||
NotPending = 0,
|
||||
Pending,
|
||||
Approved,
|
||||
Denied,
|
||||
}
|
||||
|
||||
public partial class NetPeer
|
||||
{
|
||||
private List<NetConnection> m_pendingConnections;
|
||||
|
||||
private void AddPendingConnection(NetConnection conn, NetIncomingMessage approval)
|
||||
{
|
||||
if (m_pendingConnections == null)
|
||||
m_pendingConnections = new List<NetConnection>();
|
||||
m_pendingConnections.Add(conn);
|
||||
conn.m_pendingStatus = PendingConnectionStatus.Pending;
|
||||
|
||||
if (approval == null)
|
||||
approval = CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, 0);
|
||||
approval.m_messageType = NetMessageType.Library;
|
||||
approval.m_senderConnection = conn;
|
||||
approval.m_senderEndpoint = conn.m_remoteEndpoint;
|
||||
|
||||
ReleaseMessage(approval);
|
||||
}
|
||||
|
||||
private void CheckPendingConnections()
|
||||
{
|
||||
if (m_pendingConnections == null || m_pendingConnections.Count < 1)
|
||||
return;
|
||||
|
||||
foreach (NetConnection conn in m_pendingConnections)
|
||||
{
|
||||
switch (conn.m_pendingStatus)
|
||||
{
|
||||
case PendingConnectionStatus.Pending:
|
||||
if (NetTime.Now > conn.m_connectInitationTime + 10.0)
|
||||
{
|
||||
LogWarning("Pending connection still in pending state after 10 seconds; forgot to Approve/Deny?");
|
||||
m_pendingConnections.Remove(conn);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case PendingConnectionStatus.Approved:
|
||||
// accept connection
|
||||
AcceptConnection(conn);
|
||||
m_pendingConnections.Remove(conn);
|
||||
return;
|
||||
case PendingConnectionStatus.Denied:
|
||||
// send disconnected
|
||||
NetOutgoingMessage bye = CreateLibraryMessage(NetMessageLibraryType.Disconnect, conn.m_pendingDenialReason);
|
||||
EnqueueUnconnectedMessage(bye, conn.m_remoteEndpoint);
|
||||
m_pendingConnections.Remove(conn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Lidgren.Network/NetPeer.Discovery.cs
Normal file
38
Lidgren.Network/NetPeer.Discovery.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Emit a discovery signal to all hosts on your subnet
|
||||
/// </summary>
|
||||
public void DiscoverLocalPeers(int serverPort)
|
||||
{
|
||||
NetOutgoingMessage om = CreateMessage();
|
||||
SendUnconnectedLibraryMessage(om, NetMessageLibraryType.Discovery, new IPEndPoint(IPAddress.Broadcast, serverPort));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a discovery signal to a single known host
|
||||
/// </summary>
|
||||
public bool DiscoverKnownPeer(string host, int serverPort)
|
||||
{
|
||||
IPAddress address = NetUtility.Resolve(host);
|
||||
if (address == null)
|
||||
return false;
|
||||
return DiscoverKnownPeer(new IPEndPoint(address, serverPort));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a discovery signal to a single known host
|
||||
/// </summary>
|
||||
public bool DiscoverKnownPeer(IPEndPoint endpoint)
|
||||
{
|
||||
NetOutgoingMessage om = CreateMessage();
|
||||
SendUnconnectedLibraryMessage(om, NetMessageLibraryType.Discovery, endpoint);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
603
Lidgren.Network/NetPeer.Internal.cs
Normal file
603
Lidgren.Network/NetPeer.Internal.cs
Normal file
@@ -0,0 +1,603 @@
|
||||
/* 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.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
using System.Net.NetworkInformation;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
private EndPoint m_senderRemote;
|
||||
internal byte[] m_receiveBuffer;
|
||||
internal byte[] m_sendBuffer;
|
||||
internal Socket m_socket;
|
||||
internal byte[] m_macAddressBytes;
|
||||
private int m_listenPort;
|
||||
private AutoResetEvent m_messageReceivedEvent;
|
||||
|
||||
private NetQueue<NetIncomingMessage> m_releasedIncomingMessages;
|
||||
private NetQueue<NetOutgoingMessage> m_unsentUnconnectedMessage;
|
||||
|
||||
/// <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; } }
|
||||
|
||||
private void InternalInitialize()
|
||||
{
|
||||
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(16);
|
||||
m_unsentUnconnectedMessage = new NetQueue<NetOutgoingMessage>(4);
|
||||
m_messageReceivedEvent = new AutoResetEvent(false);
|
||||
}
|
||||
|
||||
internal void ReleaseMessage(NetIncomingMessage msg)
|
||||
{
|
||||
NetException.Assert(msg.m_status != NetIncomingMessageReleaseStatus.ReleasedToApplication, "Message released to application twice!");
|
||||
|
||||
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 + ")");
|
||||
}
|
||||
|
||||
//
|
||||
// Network loop
|
||||
//
|
||||
private void Run()
|
||||
{
|
||||
//
|
||||
// Initialize
|
||||
//
|
||||
VerifyNetworkThread();
|
||||
|
||||
InitializeRecycling();
|
||||
|
||||
System.Net.NetworkInformation.PhysicalAddress 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");
|
||||
}
|
||||
|
||||
LogDebug("Network thread started");
|
||||
|
||||
lock (m_initializeLock)
|
||||
{
|
||||
if (m_status == NetPeerStatus.Running)
|
||||
return;
|
||||
|
||||
m_statistics.Reset();
|
||||
|
||||
// bind to socket
|
||||
IPEndPoint iep = null;
|
||||
try
|
||||
{
|
||||
iep = new IPEndPoint(m_configuration.LocalAddress, m_configuration.Port);
|
||||
EndPoint ep = (EndPoint)iep;
|
||||
|
||||
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||
m_socket.ReceiveBufferSize = m_configuration.ReceiveBufferSize;
|
||||
m_socket.SendBufferSize = m_configuration.SendBufferSize;
|
||||
m_socket.Blocking = false;
|
||||
m_socket.Bind(ep);
|
||||
|
||||
IPEndPoint boundEp = m_socket.LocalEndPoint as IPEndPoint;
|
||||
LogDebug("Socket bound to " + boundEp + ": " + m_socket.IsBound);
|
||||
|
||||
m_listenPort = boundEp.Port;
|
||||
|
||||
long first = (pa == null ? (long)0 : (long)pa.GetHashCode());
|
||||
long second = (long)((long)boundEp.GetHashCode() << 32);
|
||||
m_uniqueIdentifier = first ^ second;
|
||||
|
||||
m_receiveBuffer = new byte[m_configuration.ReceiveBufferSize];
|
||||
m_sendBuffer = new byte[m_configuration.SendBufferSize];
|
||||
|
||||
LogVerbose("Initialization done");
|
||||
|
||||
// only set Running if everything succeeds
|
||||
m_status = NetPeerStatus.Running;
|
||||
}
|
||||
catch (SocketException sex)
|
||||
{
|
||||
if (sex.SocketErrorCode == SocketError.AddressAlreadyInUse)
|
||||
throw new NetException("Failed to bind to port " + (iep == null ? "Null" : iep.ToString()) + " - Address already in use!", sex);
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new NetException("Failed to bind to " + (iep == null ? "Null" : iep.ToString()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Network loop
|
||||
//
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
Heartbeat();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogWarning(ex.ToString());
|
||||
}
|
||||
} while (m_status == NetPeerStatus.Running);
|
||||
|
||||
//
|
||||
// perform shutdown
|
||||
//
|
||||
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);
|
||||
}
|
||||
|
||||
// one final heartbeat, will send stuff and do disconnect
|
||||
Heartbeat();
|
||||
|
||||
lock (m_initializeLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (m_socket != null)
|
||||
{
|
||||
m_socket.Shutdown(SocketShutdown.Receive);
|
||||
m_socket.Close(2); // 2 seconds timeout
|
||||
}
|
||||
if (m_messageReceivedEvent != null)
|
||||
m_messageReceivedEvent.Close();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_socket = null;
|
||||
m_messageReceivedEvent = null;
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
LogDebug("Shutdown complete");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void Heartbeat()
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
#if DEBUG
|
||||
// send delayed packets
|
||||
SendDelayedPackets();
|
||||
#endif
|
||||
|
||||
// connection approval
|
||||
CheckPendingConnections();
|
||||
|
||||
double now = NetTime.Now;
|
||||
|
||||
// do connection heartbeats
|
||||
foreach (NetConnection conn in m_connections)
|
||||
{
|
||||
conn.Heartbeat(now);
|
||||
if (conn.m_status == NetConnectionStatus.Disconnected)
|
||||
{
|
||||
RemoveConnection(conn);
|
||||
break; // can't continue iteration here
|
||||
}
|
||||
}
|
||||
|
||||
// send unconnected sends
|
||||
NetOutgoingMessage um;
|
||||
while ((um = m_unsentUnconnectedMessage.TryDequeue()) != null)
|
||||
{
|
||||
IPEndPoint recipient = um.m_unconnectedRecipient;
|
||||
|
||||
//
|
||||
// TODO: use throttling here
|
||||
//
|
||||
|
||||
int ptr = um.Encode(m_sendBuffer, 0, null);
|
||||
|
||||
if (recipient.Address.Equals(IPAddress.Broadcast))
|
||||
{
|
||||
// send using broadcast
|
||||
try
|
||||
{
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
SendPacket(ptr, recipient, 1);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// send normally
|
||||
SendPacket(ptr, recipient, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
return;
|
||||
|
||||
//if (m_socket == null || m_socket.Available < 1)
|
||||
// return;
|
||||
|
||||
int bytesReceived = 0;
|
||||
try
|
||||
{
|
||||
bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote);
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
// no good response to this yet
|
||||
if (sx.ErrorCode == 10054)
|
||||
{
|
||||
// connection reset by peer, aka forcibly closed
|
||||
// we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?!
|
||||
//LogWarning("Connection reset by peer, seemingly from " + m_senderRemote);
|
||||
return;
|
||||
}
|
||||
|
||||
LogWarning(sx.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (bytesReceived < 1)
|
||||
return;
|
||||
|
||||
// renew current time; we might have waited in Poll
|
||||
now = NetTime.Now;
|
||||
|
||||
//LogVerbose("Received " + bytesReceived + " bytes");
|
||||
|
||||
IPEndPoint ipsender = (IPEndPoint)m_senderRemote;
|
||||
|
||||
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)
|
||||
{
|
||||
// get NetMessageType
|
||||
byte top = m_receiveBuffer[ptr++];
|
||||
bool isFragment = (top & 128) == 128;
|
||||
msgType = (NetMessageType)(top & 127);
|
||||
|
||||
// get NetmessageLibraryType?
|
||||
if (msgType == NetMessageType.Library)
|
||||
libType = (NetMessageLibraryType)m_receiveBuffer[ptr++];
|
||||
|
||||
// get sequence number?
|
||||
ushort sequenceNumber;
|
||||
if (msgType >= NetMessageType.UserSequenced)
|
||||
sequenceNumber = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8));
|
||||
else
|
||||
sequenceNumber = 0;
|
||||
|
||||
// get payload length
|
||||
int payloadLengthBits = (int)m_receiveBuffer[ptr++];
|
||||
if ((payloadLengthBits & 128) == 128) // large payload
|
||||
payloadLengthBits = (payloadLengthBits & 127) | (m_receiveBuffer[ptr++] << 7);
|
||||
|
||||
int payloadLengthBytes = NetUtility.BytesToHoldBits(payloadLengthBits);
|
||||
|
||||
if ((ptr + payloadLengthBytes) > bytesReceived)
|
||||
{
|
||||
LogWarning("Malformed message from " + ipsender.ToString() + "; not enough bytes");
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// 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);
|
||||
|
||||
// heartbeat done
|
||||
return;
|
||||
}
|
||||
|
||||
private void HandleUnconnectedLibraryMessage(NetMessageLibraryType libType, int ptr, int payloadLengthBits, IPEndPoint senderEndpoint)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
if (libType != NetMessageLibraryType.Connect && libType != NetMessageLibraryType.Discovery && libType != NetMessageLibraryType.DiscoveryResponse)
|
||||
{
|
||||
LogWarning("Received unconnected library message of type " + libType);
|
||||
return;
|
||||
}
|
||||
|
||||
int payloadLengthBytes = NetUtility.BytesToHoldBits(payloadLengthBits);
|
||||
|
||||
//
|
||||
// Handle Discovery
|
||||
//
|
||||
if (libType == 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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (libType == 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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Handle NetMessageLibraryType.Connect
|
||||
//
|
||||
|
||||
if (!m_configuration.m_acceptIncomingConnections)
|
||||
{
|
||||
LogWarning("Connect received; but we're not accepting incoming connections!");
|
||||
return;
|
||||
}
|
||||
|
||||
string appIdent;
|
||||
long remoteUniqueIdentifier = 0;
|
||||
NetIncomingMessage approval = null;
|
||||
try
|
||||
{
|
||||
NetIncomingMessage reader = new NetIncomingMessage();
|
||||
|
||||
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();
|
||||
|
||||
int approvalBitLength = (int)reader.ReadVariableUInt32();
|
||||
if (approvalBitLength > 0)
|
||||
{
|
||||
int approvalByteLength = NetUtility.BytesToHoldBits(approvalBitLength);
|
||||
if (approvalByteLength < m_configuration.MaximumTransmissionUnit)
|
||||
{
|
||||
approval = CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, approvalByteLength);
|
||||
reader.ReadBits(approval.m_data, 0, approvalBitLength);
|
||||
approval.m_bitLength = approvalBitLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// malformed connect packet
|
||||
LogWarning("Malformed connect packet from " + senderEndpoint + " - " + ex.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (appIdent.Equals(m_configuration.AppIdentifier) == false)
|
||||
{
|
||||
// wrong app ident
|
||||
LogWarning("Connect received with wrong appidentifier (need '" + m_configuration.AppIdentifier + "' found '" + appIdent + "') from " + senderEndpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
// ok, someone wants to connect to us, and we're accepting connections!
|
||||
if (m_connections.Count >= m_configuration.MaximumConnections)
|
||||
{
|
||||
HandleServerFull(senderEndpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
AcceptConnection(conn);
|
||||
return;
|
||||
}
|
||||
|
||||
private void HandleUnconnectedUserMessage(int ptr, int payloadLengthBits, IPEndPoint senderEndpoint)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
NetIncomingMessage ium = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, m_receiveBuffer, ptr, NetUtility.BytesToHoldBits(payloadLengthBits));
|
||||
ium.m_bitLength = payloadLengthBits;
|
||||
ium.m_senderEndpoint = senderEndpoint;
|
||||
ReleaseMessage(ium);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
internal void RemoveConnection(NetConnection conn)
|
||||
{
|
||||
lock (m_connections)
|
||||
{
|
||||
m_connections.Remove(conn);
|
||||
m_connectionLookup.Remove(conn.m_remoteEndpoint);
|
||||
}
|
||||
conn.Dispose();
|
||||
}
|
||||
|
||||
private void HandleServerFull(IPEndPoint connecter)
|
||||
{
|
||||
const string rejectMessage = "Server is full!";
|
||||
NetOutgoingMessage reply = CreateLibraryMessage(NetMessageLibraryType.Disconnect, rejectMessage);
|
||||
EnqueueUnconnectedMessage(reply, connecter);
|
||||
}
|
||||
|
||||
// called by user and network thread
|
||||
private void EnqueueUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
msg.m_unconnectedRecipient = recipient;
|
||||
Interlocked.Increment(ref msg.m_inQueueCount);
|
||||
m_unsentUnconnectedMessage.Enqueue(msg);
|
||||
}
|
||||
|
||||
internal static NetDeliveryMethod GetDeliveryMethod(NetMessageType mtp)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
internal void SendImmediately(NetConnection conn, NetOutgoingMessage msg)
|
||||
{
|
||||
NetException.Assert(msg.m_type == NetMessageType.Library, "SendImmediately can only send library (non-reliable) messages");
|
||||
|
||||
msg.m_inQueueCount = 1;
|
||||
int len = msg.Encode(m_sendBuffer, 0, conn);
|
||||
Interlocked.Decrement(ref msg.m_inQueueCount);
|
||||
|
||||
SendPacket(len, conn.m_remoteEndpoint, 1);
|
||||
|
||||
Recycle(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
139
Lidgren.Network/NetPeer.LatencySimulation.cs
Normal file
139
Lidgren.Network/NetPeer.LatencySimulation.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
/* 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.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
private List<DelayedPacket> m_delayedPackets = new List<DelayedPacket>();
|
||||
|
||||
private class DelayedPacket
|
||||
{
|
||||
public byte[] Data;
|
||||
public double DelayedUntil;
|
||||
public IPEndPoint Target;
|
||||
}
|
||||
|
||||
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages)
|
||||
{
|
||||
// simulate loss
|
||||
float loss = m_configuration.m_loss;
|
||||
if (loss > 0.0f)
|
||||
{
|
||||
if (NetRandom.Instance.Chance(m_configuration.m_loss))
|
||||
{
|
||||
LogVerbose("Sending packet " + numBytes + " bytes - SIMULATED LOST!");
|
||||
return; // packet "lost"
|
||||
}
|
||||
}
|
||||
|
||||
m_statistics.PacketSent(numBytes, numMessages);
|
||||
|
||||
// simulate latency
|
||||
float m = m_configuration.m_minimumOneWayLatency;
|
||||
float r = m_configuration.m_randomOneWayLatency;
|
||||
if (m == 0.0f && r == 0.0f)
|
||||
{
|
||||
// no latency simulation
|
||||
LogVerbose("Sending packet " + numBytes + " bytes");
|
||||
ActuallySendPacket(m_sendBuffer, numBytes, target);
|
||||
return;
|
||||
}
|
||||
|
||||
int num = 1;
|
||||
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.Chance(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);
|
||||
|
||||
// Enqueue delayed packet
|
||||
DelayedPacket p = new DelayedPacket();
|
||||
p.Target = target;
|
||||
p.Data = new byte[numBytes];
|
||||
Buffer.BlockCopy(m_sendBuffer, 0, p.Data, 0, numBytes);
|
||||
p.DelayedUntil = NetTime.Now + delay;
|
||||
|
||||
m_delayedPackets.Add(p);
|
||||
}
|
||||
|
||||
LogVerbose("Sending packet " + numBytes + " bytes - delayed " + NetTime.ToReadable(delay));
|
||||
}
|
||||
|
||||
private void SendDelayedPackets()
|
||||
{
|
||||
if (m_delayedPackets.Count <= 0)
|
||||
return;
|
||||
|
||||
double now = NetTime.Now;
|
||||
|
||||
RestartDelaySending:
|
||||
foreach (DelayedPacket p in m_delayedPackets)
|
||||
{
|
||||
if (now > p.DelayedUntil)
|
||||
{
|
||||
ActuallySendPacket(p.Data, p.Data.Length, p.Target);
|
||||
m_delayedPackets.Remove(p);
|
||||
goto RestartDelaySending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target)
|
||||
{
|
||||
try
|
||||
{
|
||||
int bytesSent = m_socket.SendTo(data, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
}
|
||||
#else
|
||||
//
|
||||
// Release - just send the packet straight away
|
||||
//
|
||||
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages)
|
||||
{
|
||||
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 (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
51
Lidgren.Network/NetPeer.Logging.cs
Normal file
51
Lidgren.Network/NetPeer.Logging.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
/* 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.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
[Conditional("DEBUG")]
|
||||
internal void LogVerbose(string message)
|
||||
{
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.VerboseDebugMessage))
|
||||
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.VerboseDebugMessage, message));
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void LogDebug(string message)
|
||||
{
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.DebugMessage))
|
||||
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.DebugMessage, message));
|
||||
}
|
||||
|
||||
internal void LogWarning(string message)
|
||||
{
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.WarningMessage))
|
||||
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.WarningMessage, message));
|
||||
}
|
||||
|
||||
internal void LogError(string message)
|
||||
{
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ErrorMessage))
|
||||
ReleaseMessage(CreateIncomingMessage(NetIncomingMessageType.ErrorMessage, message));
|
||||
}
|
||||
}
|
||||
}
|
||||
280
Lidgren.Network/NetPeer.Recycling.cs
Normal file
280
Lidgren.Network/NetPeer.Recycling.cs
Normal file
@@ -0,0 +1,280 @@
|
||||
/* 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.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
internal int m_storedBytes;
|
||||
private int m_maxStoredBytes;
|
||||
private List<byte[]> m_storagePool = new List<byte[]>();
|
||||
private NetQueue<NetIncomingMessage> m_incomingMessagesPool = new NetQueue<NetIncomingMessage>(16);
|
||||
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool = new NetQueue<NetOutgoingMessage>(16);
|
||||
|
||||
private void InitializeRecycling()
|
||||
{
|
||||
m_storagePool.Clear();
|
||||
m_storedBytes = 0;
|
||||
m_maxStoredBytes = m_configuration.m_maxRecycledBytesKept;
|
||||
m_incomingMessagesPool.Clear();
|
||||
m_outgoingMessagesPool.Clear();
|
||||
}
|
||||
|
||||
internal byte[] GetStorage(int requiredBytes)
|
||||
{
|
||||
if (m_storagePool.Count < 1)
|
||||
{
|
||||
m_statistics.m_bytesAllocated += requiredBytes;
|
||||
return new byte[requiredBytes];
|
||||
}
|
||||
|
||||
lock (m_storagePool)
|
||||
{
|
||||
// search from end to start
|
||||
for (int i = m_storagePool.Count - 1; i >= 0; i--)
|
||||
{
|
||||
byte[] retval = m_storagePool[i];
|
||||
if (retval.Length >= requiredBytes)
|
||||
{
|
||||
m_storagePool.RemoveAt(i);
|
||||
m_storedBytes -= retval.Length;
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_statistics.m_bytesAllocated += requiredBytes;
|
||||
return new byte[requiredBytes];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
public NetOutgoingMessage CreateMessage()
|
||||
{
|
||||
return CreateMessage(m_configuration.DefaultOutgoingMessageCapacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">initial capacity in bytes</param>
|
||||
public NetOutgoingMessage CreateMessage(int initialCapacity)
|
||||
{
|
||||
NetOutgoingMessage retval = m_outgoingMessagesPool.TryDequeue();
|
||||
if (retval == null)
|
||||
retval = new NetOutgoingMessage();
|
||||
else
|
||||
retval.Reset();
|
||||
|
||||
byte[] storage = GetStorage(initialCapacity);
|
||||
retval.m_data = storage;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetOutgoingMessage CreateLibraryMessage(NetMessageLibraryType tp, string content)
|
||||
{
|
||||
NetOutgoingMessage retval = CreateMessage(1 + (content == null ? 0 : content.Length));
|
||||
retval.m_type = NetMessageType.Library;
|
||||
retval.m_libType = tp;
|
||||
retval.Write((content == null ? "" : content));
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycle the message to the library for reuse
|
||||
/// </summary>
|
||||
public void Recycle(NetIncomingMessage msg)
|
||||
{
|
||||
if (msg.m_status != NetIncomingMessageReleaseStatus.ReleasedToApplication)
|
||||
throw new NetException("Message not under application control; recycled more than once?");
|
||||
|
||||
msg.m_status = NetIncomingMessageReleaseStatus.RecycledByApplication;
|
||||
if (msg.m_data != null)
|
||||
{
|
||||
lock (m_storagePool)
|
||||
{
|
||||
#if DEBUG
|
||||
if (m_storagePool.Contains(msg.m_data))
|
||||
throw new NetException("Storage pool object recycled twice!");
|
||||
#endif
|
||||
m_storedBytes += msg.m_data.Length;
|
||||
m_storagePool.Add(msg.m_data);
|
||||
}
|
||||
msg.m_data = null;
|
||||
}
|
||||
m_incomingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycle the message to the library for reuse
|
||||
/// </summary>
|
||||
internal void Recycle(NetOutgoingMessage msg)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
#if DEBUG
|
||||
lock (m_connections)
|
||||
{
|
||||
foreach (NetConnection conn in m_connections)
|
||||
{
|
||||
if (conn.m_unsentMessages.Contains(msg))
|
||||
throw new NetException("Ouch! Recycling unsent message!");
|
||||
|
||||
for(int i=0;i<conn.m_storedMessages.Length;i++)
|
||||
{
|
||||
List<NetOutgoingMessage> list = conn.m_storedMessages[i];
|
||||
if (list != null && list.Count > 0)
|
||||
{
|
||||
foreach (NetOutgoingMessage om in conn.m_storedMessages[i])
|
||||
{
|
||||
if (om == msg)
|
||||
throw new NetException("Ouch! Recycling stored message!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
NetException.Assert(msg.m_inQueueCount == 0, "Recycling message still in some queue!");
|
||||
|
||||
if (msg.m_data != null)
|
||||
{
|
||||
lock (m_storagePool)
|
||||
{
|
||||
if (!m_storagePool.Contains(msg.m_data))
|
||||
{
|
||||
m_storedBytes += msg.m_data.Length;
|
||||
m_storagePool.Add(msg.m_data);
|
||||
}
|
||||
}
|
||||
msg.m_data = null;
|
||||
}
|
||||
m_outgoingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call to check if storage pool should be reduced
|
||||
/// </summary>
|
||||
private void ReduceStoragePool()
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
if (m_storedBytes < m_configuration.m_maxRecycledBytesKept)
|
||||
return; // never mind threading, no big deal if storage is larger than config setting for a frame
|
||||
|
||||
int wasStoredBytes;
|
||||
int reduceTo;
|
||||
lock (m_storagePool)
|
||||
{
|
||||
// since newly stored message at added to the end; remove from the start
|
||||
wasStoredBytes = m_storedBytes;
|
||||
reduceTo = m_maxStoredBytes / 2;
|
||||
|
||||
while (m_storedBytes > reduceTo && m_storagePool.Count > 0)
|
||||
{
|
||||
byte[] arr = m_storagePool[0];
|
||||
m_storedBytes -= arr.Length;
|
||||
m_storagePool.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
LogDebug("Reduced recycled bytes pool from " + wasStoredBytes + " bytes to " + m_storedBytes + " bytes (target " + reduceTo + ")");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an incoming message with the required capacity for releasing to the application
|
||||
/// </summary>
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, string contents)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (string.IsNullOrEmpty(contents))
|
||||
{
|
||||
retval = CreateIncomingMessage(tp, 1);
|
||||
retval.Write("");
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(contents);
|
||||
retval = CreateIncomingMessage(tp, bytes.Length + (bytes.Length > 127 ? 2 : 1));
|
||||
retval.Write(contents);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an incoming message with the required capacity for releasing to the application
|
||||
/// </summary>
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int requiredCapacity)
|
||||
{
|
||||
NetIncomingMessage retval = m_incomingMessagesPool.TryDequeue();
|
||||
if (retval == null)
|
||||
retval = new NetIncomingMessage();
|
||||
else
|
||||
retval.Reset();
|
||||
|
||||
NetException.Assert(retval.m_status != NetIncomingMessageReleaseStatus.ReleasedToApplication);
|
||||
|
||||
retval.m_incomingType = tp;
|
||||
retval.m_senderConnection = null;
|
||||
retval.m_senderEndpoint = null;
|
||||
retval.m_status = NetIncomingMessageReleaseStatus.NotReleased;
|
||||
|
||||
if (requiredCapacity > 0)
|
||||
{
|
||||
byte[] storage = GetStorage(requiredCapacity);
|
||||
retval.m_data = storage;
|
||||
}
|
||||
else
|
||||
{
|
||||
retval.m_data = null;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] copyFrom, int offset, int copyLength)
|
||||
{
|
||||
NetIncomingMessage retval = m_incomingMessagesPool.TryDequeue();
|
||||
if (retval == null)
|
||||
retval = new NetIncomingMessage();
|
||||
else
|
||||
retval.Reset();
|
||||
|
||||
retval.m_data = GetStorage(copyLength);
|
||||
Buffer.BlockCopy(copyFrom, offset, retval.m_data, 0, copyLength);
|
||||
|
||||
retval.m_bitLength = copyLength * 8;
|
||||
retval.m_incomingType = tp;
|
||||
retval.m_senderConnection = null;
|
||||
retval.m_senderEndpoint = null;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
342
Lidgren.Network/NetPeer.cs
Normal file
342
Lidgren.Network/NetPeer.cs
Normal file
@@ -0,0 +1,342 @@
|
||||
/* Copyright (c) 2010 Michael Lidgren
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
|
||||
the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
//
|
||||
// This partial file holds public netpeer methods accessible to the application
|
||||
//
|
||||
[DebuggerDisplay("Status={m_status}")]
|
||||
public partial class NetPeer
|
||||
{
|
||||
internal const int kMinPacketHeaderSize = 2;
|
||||
internal const int kMaxPacketHeaderSize = 5;
|
||||
|
||||
private NetPeerStatus m_status;
|
||||
private object m_initializeLock = new object();
|
||||
internal long m_uniqueIdentifier;
|
||||
|
||||
internal NetPeerConfiguration m_configuration;
|
||||
internal NetPeerStatistics m_statistics;
|
||||
private Thread m_networkThread;
|
||||
private string m_shutdownReason;
|
||||
|
||||
internal List<NetConnection> m_connections;
|
||||
private Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the NetPeer
|
||||
/// </summary>
|
||||
public NetPeerStatus Status { get { return m_status; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the list of connections
|
||||
/// </summary>
|
||||
public NetConnection[] Connections
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (m_connections)
|
||||
return m_connections.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of active connections
|
||||
/// </summary>
|
||||
public int ConnectionsCount
|
||||
{
|
||||
get { return m_connections.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Statistics on this NetPeer since it was initialized
|
||||
/// </summary>
|
||||
public NetPeerStatistics Statistics
|
||||
{
|
||||
get { return m_statistics; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the configuration of the netpeer
|
||||
/// </summary>
|
||||
public NetPeerConfiguration Configuration { get { return m_configuration; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number this NetPeer is listening and sending on
|
||||
/// </summary>
|
||||
public int Port { get { return m_listenPort; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a semi-unique identifier based on Mac address and ip/port. Note! Not available until Start has been called!
|
||||
/// </summary>
|
||||
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
|
||||
|
||||
public NetPeer(NetPeerConfiguration configuration)
|
||||
{
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
m_configuration = configuration;
|
||||
m_connections = new List<NetConnection>();
|
||||
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
|
||||
m_statistics = new NetPeerStatistics(this);
|
||||
|
||||
InternalInitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds to socket
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
if (m_status != NetPeerStatus.NotRunning)
|
||||
{
|
||||
// already running! Just ignore...
|
||||
LogWarning("Start() called on already running NetPeer - ignoring.");
|
||||
return;
|
||||
}
|
||||
|
||||
m_status = NetPeerStatus.Starting;
|
||||
|
||||
m_releasedIncomingMessages.Clear();
|
||||
m_unsentUnconnectedMessage.Clear();
|
||||
|
||||
m_configuration.VerifyAndLock();
|
||||
|
||||
// start network thread
|
||||
m_networkThread = new Thread(new ThreadStart(Run));
|
||||
m_networkThread.Name = "Lidgren network thread";
|
||||
m_networkThread.IsBackground = true;
|
||||
m_networkThread.Start();
|
||||
|
||||
// allow some time for network thread to start up in case they call Connect() immediately
|
||||
Thread.Sleep(3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a pending message from any connection, if any
|
||||
/// </summary>
|
||||
public NetIncomingMessage ReadMessage()
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning)
|
||||
return null;
|
||||
|
||||
return m_releasedIncomingMessages.TryDequeue();
|
||||
}
|
||||
|
||||
public NetIncomingMessage WaitMessage(int maxMillis)
|
||||
{
|
||||
if (m_messageReceivedEvent != null)
|
||||
m_messageReceivedEvent.WaitOne(maxMillis);
|
||||
return m_releasedIncomingMessages.TryDequeue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection Connect(string host, int port)
|
||||
{
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection Connect(IPEndPoint remoteEndpoint)
|
||||
{
|
||||
return Connect(remoteEndpoint, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection Connect(string host, int port, NetOutgoingMessage approvalMessage)
|
||||
{
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), approvalMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public virtual NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage approvalMessage)
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning)
|
||||
throw new NetException("Must call Start() first");
|
||||
|
||||
if (m_connectionLookup.ContainsKey(remoteEndpoint))
|
||||
throw new NetException("Already connected to that endpoint!");
|
||||
|
||||
NetConnection conn = new NetConnection(this, remoteEndpoint);
|
||||
conn.m_approvalMessage = approvalMessage;
|
||||
|
||||
// handle on network thread
|
||||
conn.m_connectRequested = true;
|
||||
conn.m_connectionInitiator = true;
|
||||
|
||||
lock (m_connections)
|
||||
{
|
||||
m_connections.Add(conn);
|
||||
m_connectionLookup[remoteEndpoint] = conn;
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an existing connection
|
||||
/// </summary>
|
||||
public bool SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod deliveryMethod)
|
||||
{
|
||||
return SendMessage(msg, recipient, deliveryMethod, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an existing connection
|
||||
/// </summary>
|
||||
public bool SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod deliveryMethod, int channel)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
if (channel < 0 || channel > 63)
|
||||
throw new NetException("Channel must be between 0 and 63");
|
||||
if (channel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
|
||||
throw new NetException("Channel must be 0 for Unreliable and ReliableUnordered");
|
||||
|
||||
if (m_status != NetPeerStatus.Running)
|
||||
return false;
|
||||
|
||||
msg.m_type = (NetMessageType)((int)deliveryMethod + channel);
|
||||
|
||||
recipient.EnqueueOutgoingMessage(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a number of existing connections
|
||||
/// </summary>
|
||||
public bool SendMessage(NetOutgoingMessage msg, IEnumerable<NetConnection> recipients, NetDeliveryMethod deliveryMethod, int channel)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
if (channel < 0 || channel > 63)
|
||||
throw new NetException("Channel must be between 0 and 63");
|
||||
if (channel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
|
||||
throw new NetException("Channel must be 0 for Unreliable and ReliableUnordered");
|
||||
|
||||
if (m_status != NetPeerStatus.Running)
|
||||
return false;
|
||||
|
||||
msg.m_type = (NetMessageType)((int)deliveryMethod + channel);
|
||||
|
||||
foreach (NetConnection conn in recipients)
|
||||
conn.EnqueueOutgoingMessage(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
|
||||
IPAddress adr = NetUtility.Resolve(host);
|
||||
if (adr == null)
|
||||
throw new NetException("Failed to resolve " + host);
|
||||
|
||||
msg.m_type = NetMessageType.UserUnreliable; // sortof not applicable
|
||||
EnqueueUnconnectedMessage(msg, new IPEndPoint(adr, port));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_type = NetMessageType.UserUnreliable; // sortof not applicable
|
||||
EnqueueUnconnectedMessage(msg, recipient);
|
||||
}
|
||||
|
||||
internal void SendUnconnectedLibraryMessage(NetOutgoingMessage msg, NetMessageLibraryType libType, IPEndPoint recipient)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_type = NetMessageType.Library;
|
||||
msg.m_libType = libType;
|
||||
EnqueueUnconnectedMessage(msg, recipient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a number of unconnected hosts
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IEnumerable<IPEndPoint> recipients)
|
||||
{
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_type = NetMessageType.UserUnreliable; // sortof not applicable
|
||||
foreach (IPEndPoint ipe in recipients)
|
||||
EnqueueUnconnectedMessage(msg, ipe);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a discovery response message
|
||||
/// </summary>
|
||||
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (msg == null)
|
||||
msg = CreateMessage(0);
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_type = NetMessageType.Library;
|
||||
msg.m_libType = NetMessageLibraryType.DiscoveryResponse;
|
||||
EnqueueUnconnectedMessage(msg, recipient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects all active connections and closes the socket
|
||||
/// </summary>
|
||||
public void Shutdown(string bye)
|
||||
{
|
||||
// called on user thread
|
||||
|
||||
if (m_socket == null)
|
||||
return; // already shut down
|
||||
|
||||
LogDebug("Shutdown requested");
|
||||
m_shutdownReason = bye;
|
||||
m_status = NetPeerStatus.ShutdownRequested;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetPeer bound to " + m_socket.LocalEndPoint + " " + ConnectionsCount + " connections]";
|
||||
}
|
||||
}
|
||||
}
|
||||
442
Lidgren.Network/NetPeerConfiguration.cs
Normal file
442
Lidgren.Network/NetPeerConfiguration.cs
Normal file
@@ -0,0 +1,442 @@
|
||||
/* 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.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Partly immutable after NetPeer has been initialized
|
||||
/// </summary>
|
||||
public sealed class NetPeerConfiguration
|
||||
{
|
||||
private const string c_isLockedMessage = "You may not alter the NetPeerConfiguration after the NetPeer has been initialized!";
|
||||
|
||||
private bool m_isLocked;
|
||||
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 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 float m_connectionTimeout;
|
||||
internal float m_keepAliveDelay;
|
||||
internal float m_pingFrequency;
|
||||
|
||||
// reliability
|
||||
internal float[] m_resendRTTMultiplier;
|
||||
internal float[] m_resendBaseTime;
|
||||
internal float m_maxAckDelayTime;
|
||||
|
||||
// bad network simulation
|
||||
internal float m_loss;
|
||||
internal float m_duplicates;
|
||||
internal float m_minimumOneWayLatency;
|
||||
internal float m_randomOneWayLatency;
|
||||
|
||||
public NetPeerConfiguration(string appIdentifier)
|
||||
{
|
||||
if (string.IsNullOrEmpty(appIdentifier))
|
||||
throw new NetException("App identifier must be at least one character long");
|
||||
m_appIdentifier = appIdentifier;
|
||||
|
||||
// defaults
|
||||
m_isLocked = false;
|
||||
m_acceptIncomingConnections = true;
|
||||
m_localAddress = IPAddress.Any;
|
||||
m_port = 0;
|
||||
m_receiveBufferSize = 131071;
|
||||
m_sendBufferSize = 131071;
|
||||
m_keepAliveDelay = 4.0f;
|
||||
m_connectionTimeout = 25;
|
||||
m_maximumConnections = 8;
|
||||
m_defaultOutgoingMessageCapacity = 8;
|
||||
m_pingFrequency = 6.0f;
|
||||
m_throttleBytesPerSecond = 1024 * 512;
|
||||
m_throttlePeakBytes = 8192;
|
||||
m_maxAckDelayTime = 0.01f;
|
||||
m_handshakeAttemptDelay = 1.0f;
|
||||
m_handshakeMaxAttempts = 7;
|
||||
m_maxRecycledBytesKept = 128 * 1024;
|
||||
|
||||
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;
|
||||
|
||||
// reliability
|
||||
m_resendRTTMultiplier = new float[]
|
||||
{
|
||||
1.1f,
|
||||
2.25f,
|
||||
3.5f,
|
||||
4.0f,
|
||||
4.0f,
|
||||
4.0f,
|
||||
4.0f,
|
||||
4.0f,
|
||||
4.0f,
|
||||
6.0f,
|
||||
6.0f
|
||||
};
|
||||
|
||||
m_resendBaseTime = new float[]
|
||||
{
|
||||
0.025f, // just processing time + ack delay wait time
|
||||
0.05f, // just processing time + ack delay wait time
|
||||
0.2f, // 0.16 delay since last resend
|
||||
0.5f, // 0.3 delay
|
||||
1.5f, // 1.0 delay
|
||||
3.0f, // 1.5 delay
|
||||
5.0f, // 2.0 delay
|
||||
7.5f, // 2.5 delay
|
||||
12.5f, // 5.0 delay
|
||||
17.5f, // 5.0 delay
|
||||
25.0f // 7.5 delay, obi wan you're my only hope
|
||||
};
|
||||
|
||||
// Maximum transmission unit
|
||||
// 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;
|
||||
}
|
||||
|
||||
public NetPeerConfiguration Clone()
|
||||
{
|
||||
NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration;
|
||||
retval.m_isLocked = false;
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal void VerifyAndLock()
|
||||
{
|
||||
if (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
|
||||
/// </summary>
|
||||
public string AppIdentifier
|
||||
{
|
||||
get { return m_appIdentifier; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables receiving of the specified type of message
|
||||
/// </summary>
|
||||
public void EnableMessageType(NetIncomingMessageType tp)
|
||||
{
|
||||
m_disabledTypes &= (~tp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables receiving of the specified type of message
|
||||
/// </summary>
|
||||
public void DisableMessageType(NetIncomingMessageType tp)
|
||||
{
|
||||
m_disabledTypes |= tp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables receiving of the specified type of message
|
||||
/// </summary>
|
||||
public void SetMessageTypeEnabled(NetIncomingMessageType tp, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
m_disabledTypes &= (~tp);
|
||||
else
|
||||
m_disabledTypes |= tp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if receiving of the specified type of message is enabled
|
||||
/// </summary>
|
||||
public bool IsMessageTypeEnabled(NetIncomingMessageType tp)
|
||||
{
|
||||
return !((m_disabledTypes & tp) == tp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers
|
||||
/// </summary>
|
||||
public int MaximumTransmissionUnit
|
||||
{
|
||||
get { return m_maximumTransmissionUnit; }
|
||||
set
|
||||
{
|
||||
if (value < 1 || value >= 4096)
|
||||
throw new NetException("MaximumTransmissionUnit must be between 1 and 4095 bytes");
|
||||
m_maximumTransmissionUnit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum amount of connections this peer can hold. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int MaximumConnections
|
||||
{
|
||||
get { return m_maximumConnections; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_maximumConnections = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the NetPeer should accept incoming connections
|
||||
/// </summary>
|
||||
public bool AcceptIncomingConnections
|
||||
{
|
||||
get { return m_acceptIncomingConnections; }
|
||||
set { m_acceptIncomingConnections = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default capacity in bytes when NetPeer.CreateMessage() is called without argument
|
||||
/// </summary>
|
||||
public int DefaultOutgoingMessageCapacity
|
||||
{
|
||||
get { return m_defaultOutgoingMessageCapacity; }
|
||||
set { m_defaultOutgoingMessageCapacity = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the local ip address to bind to. Defaults to IPAddress.Any. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public IPAddress LocalAddress
|
||||
{
|
||||
get { return m_localAddress; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_localAddress = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the local port to bind to. Defaults to 0. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int Port
|
||||
{
|
||||
get { return m_port; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_port = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size in bytes of the receiving buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int ReceiveBufferSize
|
||||
{
|
||||
get { return m_receiveBufferSize; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_receiveBufferSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size in bytes of the sending buffer. Defaults to 131071 bytes. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int SendBufferSize
|
||||
{
|
||||
get { return m_sendBufferSize; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_sendBufferSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds of inactivity before sending an extra ping packet as keepalive. This should be shorter than ping interval.
|
||||
/// </summary>
|
||||
public float KeepAliveDelay
|
||||
{
|
||||
get { return m_keepAliveDelay; }
|
||||
set
|
||||
{
|
||||
if (value < m_pingFrequency)
|
||||
throw new NetException("Setting KeepAliveDelay to lower than ping frequency doesn't make sense!");
|
||||
m_keepAliveDelay = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds of non-response before disconnecting because of time out. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public float ConnectionTimeout
|
||||
{
|
||||
get { return m_connectionTimeout; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_connectionTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds between latency calculation (rtt) pings
|
||||
/// </summary>
|
||||
public float PingFrequency
|
||||
{
|
||||
get { return m_pingFrequency; }
|
||||
set { m_pingFrequency = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of allowed bytes to be sent per second per connection; 0 means unlimited
|
||||
/// </summary>
|
||||
public int ThrottleBytesPerSecond
|
||||
{
|
||||
get { return m_throttleBytesPerSecond; }
|
||||
set
|
||||
{
|
||||
m_throttleBytesPerSecond = value;
|
||||
if (m_throttleBytesPerSecond < m_maximumTransmissionUnit)
|
||||
throw new NetException("ThrottleBytesPerSecond can not be lower than MaximumTransmissionUnit");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the peak number of bytes sent before throttling kicks in
|
||||
/// </summary>
|
||||
public int ThrottlePeakBytes
|
||||
{
|
||||
get { return m_throttlePeakBytes; }
|
||||
set
|
||||
{
|
||||
m_throttlePeakBytes = value;
|
||||
if (m_throttlePeakBytes < m_maximumTransmissionUnit)
|
||||
throw new NetException("ThrottlePeakBytes can not be lower than MaximumTransmissionUnit");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number between handshake attempts in seconds
|
||||
/// </summary>
|
||||
public float HandshakeAttemptDelay
|
||||
{
|
||||
get { return m_handshakeAttemptDelay; }
|
||||
set { m_handshakeAttemptDelay = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of handshake attempts before declaring failure to shake hands
|
||||
/// </summary>
|
||||
public int HandshakeMaxAttempts
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
128
Lidgren.Network/NetPeerStatistics.cs
Normal file
128
Lidgren.Network/NetPeerStatistics.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
/* 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.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public sealed class NetPeerStatistics
|
||||
{
|
||||
private NetPeer m_peer;
|
||||
|
||||
internal int m_sentPackets;
|
||||
internal int m_receivedPackets;
|
||||
|
||||
internal int m_sentMessages;
|
||||
internal int m_receivedMessages;
|
||||
|
||||
internal int m_sentBytes;
|
||||
internal int m_receivedBytes;
|
||||
|
||||
internal long m_bytesAllocated;
|
||||
|
||||
internal NetPeerStatistics(NetPeer peer)
|
||||
{
|
||||
m_peer = peer;
|
||||
Reset();
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_sentPackets = 0;
|
||||
m_receivedPackets = 0;
|
||||
|
||||
m_sentMessages = 0;
|
||||
m_receivedMessages = 0;
|
||||
|
||||
m_sentBytes = 0;
|
||||
m_receivedBytes = 0;
|
||||
|
||||
m_bytesAllocated = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of sent packets since the NetPeer was initialized
|
||||
/// </summary>
|
||||
public int SentPackets { get { return m_sentPackets; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of received packets since the NetPeer was initialized
|
||||
/// </summary>
|
||||
public int ReceivedPackets { get { return m_receivedPackets; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of sent messages since the NetPeer was initialized
|
||||
/// </summary>
|
||||
public int SentMessages { get { return m_sentMessages; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of received messages since the NetPeer was initialized
|
||||
/// </summary>
|
||||
public int ReceivedMessages { get { return m_receivedMessages; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of sent bytes since the NetPeer was initialized
|
||||
/// </summary>
|
||||
public int SentBytes { get { return m_sentBytes; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of received bytes since the NetPeer was initialized
|
||||
/// </summary>
|
||||
public int ReceivedBytes { get { return m_receivedBytes; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes allocated (and possibly garbage collected) for message storage
|
||||
/// </summary>
|
||||
public long BytesAllocated { get { return m_bytesAllocated; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes in the recycled pool
|
||||
/// </summary>
|
||||
public int BytesInRecyclePool { get { return m_peer.m_storedBytes; } }
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections");
|
||||
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");
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Lidgren.Network/NetPeerStatus.cs
Normal file
30
Lidgren.Network/NetPeerStatus.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public enum NetPeerStatus
|
||||
{
|
||||
NotRunning = 0,
|
||||
Starting = 1,
|
||||
Running = 2,
|
||||
ShutdownRequested = 3,
|
||||
}
|
||||
}
|
||||
164
Lidgren.Network/NetQueue.cs
Normal file
164
Lidgren.Network/NetQueue.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Thread safe queue with TryDequeue()
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Count={m_size}")]
|
||||
public sealed class NetQueue<T>
|
||||
{
|
||||
// Example:
|
||||
// m_capacity = 8
|
||||
// m_size = 6
|
||||
// m_head = 4
|
||||
//
|
||||
// [0] item
|
||||
// [1] item (tail = ((head + size - 1) % capacity)
|
||||
// [2]
|
||||
// [3]
|
||||
// [4] item (head)
|
||||
// [5] item
|
||||
// [6] item
|
||||
// [7] item
|
||||
//
|
||||
private T[] m_items;
|
||||
private object m_lock;
|
||||
private int m_size;
|
||||
private int m_head;
|
||||
|
||||
public int Count { get { return m_size; } }
|
||||
|
||||
public NetQueue(int initialCapacity)
|
||||
{
|
||||
m_lock = new object();
|
||||
m_items = new T[initialCapacity];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places an item last/tail of the queue
|
||||
/// </summary>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
#if DEBUG
|
||||
if (typeof(T) == typeof(NetOutgoingMessage))
|
||||
{
|
||||
NetOutgoingMessage om = item as NetOutgoingMessage;
|
||||
if (om != null)
|
||||
if (om.m_type == NetMessageType.Error)
|
||||
throw new NetException("Enqueuing error message!");
|
||||
}
|
||||
#endif
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == m_items.Length)
|
||||
SetCapacity(m_items.Length + 8);
|
||||
|
||||
int slot = (m_head + m_size) % m_items.Length;
|
||||
m_items[slot] = item;
|
||||
m_size++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Places an item first, at the head of the queue
|
||||
/// </summary>
|
||||
public void EnqueueFirst(T item)
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size >= m_items.Length)
|
||||
SetCapacity(m_items.Length + 8);
|
||||
|
||||
m_head--;
|
||||
if (m_head < 0)
|
||||
m_head = m_items.Length - 1;
|
||||
m_items[m_head] = item;
|
||||
m_size++;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetCapacity(int newCapacity)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
m_items = new T[newCapacity];
|
||||
m_head = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
T[] newItems = new T[newCapacity];
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_head + m_size - 1 < m_items.Length)
|
||||
{
|
||||
Array.Copy(m_items, m_head, newItems, 0, m_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(m_items, m_head, newItems, 0, m_items.Length - m_head);
|
||||
Array.Copy(m_items, 0, newItems, m_items.Length - m_head, (m_size - (m_items.Length - m_head)));
|
||||
}
|
||||
|
||||
m_items = newItems;
|
||||
m_head = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from the head of the queue, or returns default(T) if empty
|
||||
/// </summary>
|
||||
public T TryDequeue()
|
||||
{
|
||||
if (m_size == 0)
|
||||
return default(T);
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == 0)
|
||||
return default(T);
|
||||
|
||||
T retval = m_items[m_head];
|
||||
m_items[m_head] = default(T);
|
||||
|
||||
m_head = (m_head + 1) % m_items.Length;
|
||||
m_size--;
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
int ptr = m_head;
|
||||
for (int i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_items[ptr].Equals(item))
|
||||
return true;
|
||||
ptr = (ptr + 1) % m_items.Length;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
for (int i = 0; i < m_items.Length; i++)
|
||||
m_items[i] = default(T);
|
||||
m_head = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
399
Lidgren.Network/NetRandom.cs
Normal file
399
Lidgren.Network/NetRandom.cs
Normal file
@@ -0,0 +1,399 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// A fast random number generator for .NET
|
||||
/// Colin Green, January 2005
|
||||
///
|
||||
/// September 4th 2005
|
||||
/// Added NextBytesUnsafe() - commented out by default.
|
||||
/// Fixed bug in Reinitialise() - y,z and w variables were not being reset.
|
||||
///
|
||||
/// Key points:
|
||||
/// 1) Based on a simple and fast xor-shift pseudo random number generator (RNG) specified in:
|
||||
/// Marsaglia, George. (2003). Xorshift RNGs.
|
||||
/// http://www.jstatsoft.org/v08/i14/xorshift.pdf
|
||||
///
|
||||
/// This particular implementation of xorshift has a period of 2^128-1. See the above paper to see
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 4) Allows fast re-initialisation with a seed, unlike System.Random which accepts a seed at construction
|
||||
/// time which then executes a relatively expensive initialisation routine. This provides a vast speed improvement
|
||||
/// if you need to reset the pseudo-random number sequence many times, e.g. if you want to re-generate the same
|
||||
/// sequence many times. An alternative might be to cache random numbers in an array, but that approach is limited
|
||||
/// by memory capacity and the fact that you may also want a large number of different sequences cached. Each sequence
|
||||
/// can each be represented by a single seed value (int) when using FastRandom.
|
||||
///
|
||||
/// Notes.
|
||||
/// 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 static NetRandom Instance = new NetRandom();
|
||||
|
||||
protected override double Sample()
|
||||
{
|
||||
return NextDouble();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
private static int m_extraSeed = 42;
|
||||
|
||||
uint m_x, m_y, m_z, m_w;
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random seed based on time and working set
|
||||
/// </summary>
|
||||
public static int GetRandomSeed()
|
||||
{
|
||||
int seed = (int)Environment.TickCount;
|
||||
|
||||
try
|
||||
{
|
||||
// tickcount + gettimestamp + workingset should be random enough
|
||||
if (!string.IsNullOrEmpty(Environment.CommandLine))
|
||||
seed ^= Environment.CommandLine.GetHashCode();
|
||||
seed ^= (int)(Stopwatch.GetTimestamp());
|
||||
seed ^= (int)(Environment.WorkingSet); // will return 0 on mono
|
||||
}
|
||||
catch
|
||||
{
|
||||
// maybe commandline etc is not available, TickCount will have to do
|
||||
}
|
||||
|
||||
int extraSeed = Interlocked.Increment(ref m_extraSeed);
|
||||
|
||||
return seed + extraSeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance using time dependent seed.
|
||||
/// </summary>
|
||||
public NetRandom()
|
||||
{
|
||||
// Initialise using the system tick count
|
||||
Reinitialise(GetRandomSeed());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance using an int value as seed.
|
||||
/// This constructor signature is provided to maintain compatibility with
|
||||
/// System.Random
|
||||
/// </summary>
|
||||
public NetRandom(int seed)
|
||||
{
|
||||
Reinitialise(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reinitialises using an int value as a seed.
|
||||
/// </summary>
|
||||
/// <param name="seed"></param>
|
||||
public void Reinitialise(int seed)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <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)));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override 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))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
|
||||
/// </summary>
|
||||
public override int Next(int maxValue)
|
||||
{
|
||||
if (maxValue < 0)
|
||||
throw new ArgumentOutOfRangeException("maxValue", maxValue, "maxValue must be >=0");
|
||||
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range minValue to maxValue-1, and not including maxValue.
|
||||
/// maxValue must be >= minValue. minValue may be negative.
|
||||
/// </summary>
|
||||
public override int Next(int minValue, int maxValue)
|
||||
{
|
||||
if (minValue > maxValue)
|
||||
throw new ArgumentOutOfRangeException("maxValue", maxValue, "maxValue must be >=minValue");
|
||||
|
||||
uint t = (m_x ^ (m_x << 11));
|
||||
m_x = m_y; m_y = m_z; m_z = m_w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
int range = maxValue - minValue;
|
||||
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));
|
||||
}
|
||||
|
||||
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int anf 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);
|
||||
}
|
||||
|
||||
/// <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()
|
||||
{
|
||||
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 (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 1.0.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public float NextFloat()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/// <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'.
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
public override 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;
|
||||
int i = 0;
|
||||
uint t;
|
||||
for (; i < buffer.Length - 3; )
|
||||
{
|
||||
// Generate 4 bytes.
|
||||
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);
|
||||
}
|
||||
|
||||
// Fill up any remaining bytes in the buffer.
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
// Generate 4 bytes.
|
||||
t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte)(w & 0x000000FF);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)((w & 0x0000FF00) >> 8);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)((w & 0x00FF0000) >> 16);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i] = (byte)((w & 0xFF000000) >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.m_x = x; this.m_y = y; this.m_z = z; this.m_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.
|
||||
// /// </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");
|
||||
//
|
||||
// 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++)
|
||||
// {
|
||||
// t=(x^(x<<11));
|
||||
// x=y; y=z; z=w;
|
||||
// *pDWord++ = w = (w^(w>>19))^(t^(t>>8));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.x=x; this.y=y; this.z=z; this.w=w;
|
||||
// }
|
||||
|
||||
// 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;
|
||||
|
||||
/// <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.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool NextBool()
|
||||
{
|
||||
if (bitBufferIdx == 32)
|
||||
{
|
||||
// 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));
|
||||
|
||||
// Reset the idx that tells us which bit to read next.
|
||||
bitBufferIdx = 1;
|
||||
return (bitBuffer & 0x1) == 1;
|
||||
}
|
||||
|
||||
bitBufferIdx++;
|
||||
return ((bitBuffer >>= 1) & 0x1) == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
Lidgren.Network/NetServer.cs
Normal file
33
Lidgren.Network/NetServer.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public class NetServer : NetPeer
|
||||
{
|
||||
public NetServer(NetPeerConfiguration config)
|
||||
: base(config)
|
||||
{
|
||||
// force this to true
|
||||
config.AcceptIncomingConnections = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Lidgren.Network/NetTime.cs
Normal file
60
Lidgren.Network/NetTime.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
/* 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_STOPWATCH_AVAILABLE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Time service
|
||||
/// </summary>
|
||||
public static class NetTime
|
||||
{
|
||||
#if IS_STOPWATCH_AVAILABLE
|
||||
private static long s_timeInitialized = Stopwatch.GetTimestamp();
|
||||
private static double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
|
||||
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
|
||||
#else
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </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)
|
||||
{
|
||||
if (seconds > 60)
|
||||
return TimeSpan.FromSeconds(seconds).ToString();
|
||||
return (seconds * 1000.0).ToString("N2") + " ms";
|
||||
}
|
||||
}
|
||||
}
|
||||
262
Lidgren.Network/NetUtility.cs
Normal file
262
Lidgren.Network/NetUtility.cs
Normal file
@@ -0,0 +1,262 @@
|
||||
/* 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.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility methods
|
||||
/// </summary>
|
||||
public static class NetUtility
|
||||
{
|
||||
private static Regex s_regIP;
|
||||
|
||||
/// <summary>
|
||||
/// Get IP address from notation (xxx.xxx.xxx.xxx) or hostname
|
||||
/// </summary>
|
||||
public static IPAddress Resolve(string ipOrHost)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ipOrHost))
|
||||
throw new ArgumentException("Supplied string must not be empty", "ipOrHost");
|
||||
|
||||
ipOrHost = ipOrHost.Trim();
|
||||
|
||||
if (s_regIP == null)
|
||||
{
|
||||
string expression = "\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b";
|
||||
RegexOptions options = RegexOptions.Compiled;
|
||||
s_regIP = new Regex(expression, options);
|
||||
}
|
||||
|
||||
// is it an ip number string?
|
||||
IPAddress ipAddress = null;
|
||||
if (s_regIP.Match(ipOrHost).Success && IPAddress.TryParse(ipOrHost, out ipAddress))
|
||||
return ipAddress;
|
||||
|
||||
// ok must be a host name
|
||||
IPHostEntry entry;
|
||||
try
|
||||
{
|
||||
entry = Dns.GetHostEntry(ipOrHost);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
// check each entry for a valid IP address
|
||||
foreach (IPAddress ipCurrent in entry.AddressList)
|
||||
{
|
||||
string sIP = ipCurrent.ToString();
|
||||
bool isIP = s_regIP.Match(sIP).Success && IPAddress.TryParse(sIP, out ipAddress);
|
||||
if (isIP)
|
||||
break;
|
||||
}
|
||||
if (ipAddress == null)
|
||||
return null;
|
||||
|
||||
return ipAddress;
|
||||
}
|
||||
catch (SocketException ex)
|
||||
{
|
||||
if (ex.SocketErrorCode == SocketError.HostNotFound)
|
||||
{
|
||||
//LogWrite(string.Format(CultureInfo.InvariantCulture, "Failed to resolve host '{0}'.", ipOrHost));
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static NetworkInterface GetNetworkInterface()
|
||||
{
|
||||
IPGlobalProperties computerProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
if (computerProperties == null)
|
||||
return null;
|
||||
|
||||
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||
if (nics == null || nics.Length < 1)
|
||||
return null;
|
||||
|
||||
NetworkInterface best = null;
|
||||
foreach (NetworkInterface adapter in nics)
|
||||
{
|
||||
if (adapter.NetworkInterfaceType == NetworkInterfaceType.Loopback || adapter.NetworkInterfaceType == NetworkInterfaceType.Unknown)
|
||||
continue;
|
||||
if (!adapter.Supports(NetworkInterfaceComponent.IPv4))
|
||||
continue;
|
||||
if (best == null)
|
||||
best = adapter;
|
||||
if (adapter.OperationalStatus != OperationalStatus.Up)
|
||||
continue;
|
||||
|
||||
// A computer could have several adapters (more than one network card)
|
||||
// here but just return the first one for now...
|
||||
return adapter;
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
public static PhysicalAddress GetMacAddress()
|
||||
{
|
||||
NetworkInterface ni = GetNetworkInterface();
|
||||
if (ni == null)
|
||||
return null;
|
||||
return ni.GetPhysicalAddress();
|
||||
}
|
||||
|
||||
public static string ToHexString(long data)
|
||||
{
|
||||
return ToHexString(BitConverter.GetBytes(data));
|
||||
}
|
||||
|
||||
public static string ToHexString(byte[] data)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(data.Length * 2);
|
||||
foreach (byte b in data)
|
||||
{
|
||||
sb.AppendFormat("{0:X2}", b);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets my local IP address (not necessarily external) and subnet mask
|
||||
/// </summary>
|
||||
public static IPAddress GetMyAddress(out IPAddress mask)
|
||||
{
|
||||
NetworkInterface ni = GetNetworkInterface();
|
||||
if (ni == null)
|
||||
{
|
||||
mask = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
IPInterfaceProperties properties = ni.GetIPProperties();
|
||||
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
|
||||
{
|
||||
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
mask = unicastAddress.IPv4Mask;
|
||||
return unicastAddress.Address;
|
||||
}
|
||||
}
|
||||
|
||||
mask = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the IPEndPoint supplied is on the same subnet as this host
|
||||
/// </summary>
|
||||
public static bool IsLocal(IPEndPoint endpoint)
|
||||
{
|
||||
if (endpoint == null)
|
||||
return false;
|
||||
return IsLocal(endpoint.Address);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the IPAddress supplied is on the same subnet as this host
|
||||
/// </summary>
|
||||
public static bool IsLocal(IPAddress remote)
|
||||
{
|
||||
IPAddress mask;
|
||||
IPAddress local = GetMyAddress(out mask);
|
||||
|
||||
if (mask == null)
|
||||
return false;
|
||||
|
||||
uint maskBits = BitConverter.ToUInt32(mask.GetAddressBytes(), 0);
|
||||
uint remoteBits = BitConverter.ToUInt32(remote.GetAddressBytes(), 0);
|
||||
uint localBits = BitConverter.ToUInt32(local.GetAddressBytes(), 0);
|
||||
|
||||
// compare network portions
|
||||
return ((remoteBits & maskBits) == (localBits & maskBits));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns how many bits are necessary to hold a certain number
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public static int BitsToHoldUInt(uint value)
|
||||
{
|
||||
int bits = 1;
|
||||
while ((value >>= 1) != 0)
|
||||
bits++;
|
||||
return bits;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns how many bytes are required to hold a certain number of bits
|
||||
/// </summary>
|
||||
public static int BytesToHoldBits(int numBits)
|
||||
{
|
||||
return (numBits + 7) / 8;
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static UInt32 SwapByteOrder(UInt32 value)
|
||||
{
|
||||
return
|
||||
((value & 0xff000000) >> 24) |
|
||||
((value & 0x00ff0000) >> 8) |
|
||||
((value & 0x0000ff00) << 8) |
|
||||
((value & 0x000000ff) << 24);
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static UInt64 SwapByteOrder(UInt64 value)
|
||||
{
|
||||
return
|
||||
((value & 0xff00000000000000L) >> 56) |
|
||||
((value & 0x00ff000000000000L) >> 40) |
|
||||
((value & 0x0000ff0000000000L) >> 24) |
|
||||
((value & 0x000000ff00000000L) >> 8) |
|
||||
((value & 0x00000000ff000000L) << 8) |
|
||||
((value & 0x0000000000ff0000L) << 24) |
|
||||
((value & 0x000000000000ff00L) << 40) |
|
||||
((value & 0x00000000000000ffL) << 56);
|
||||
}
|
||||
|
||||
public static bool CompareElements(byte[] one, byte[] two)
|
||||
{
|
||||
if (one.Length != two.Length)
|
||||
return false;
|
||||
for (int i = 0; i < one.Length; i++)
|
||||
if (one[i] != two[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static byte[] ToByteArray(String hexString)
|
||||
{
|
||||
byte[] retval = new byte[hexString.Length / 2];
|
||||
for (int i = 0; i < hexString.Length; i += 2)
|
||||
retval[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Lidgren.Network/Properties/AssemblyInfo.cs
Normal file
37
Lidgren.Network/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
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("Lidgren.Network")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("Microsoft")]
|
||||
[assembly: AssemblyProduct("Lidgren.Network")]
|
||||
[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("c4914374-a2fb-4e56-bf94-60d4b81c10a1")]
|
||||
|
||||
// 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")]
|
||||
[assembly: System.CLSCompliant(true)]
|
||||
Reference in New Issue
Block a user