mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fa34d8cf8 | ||
|
|
540825cf42 | ||
|
|
4ae873b262 | ||
|
|
f4bceaf988 | ||
|
|
f9b27c0b21 | ||
|
|
0b1b9ce445 | ||
|
|
5273274f8f | ||
|
|
c8e2dcda8a | ||
|
|
1e5f539d50 | ||
|
|
7718d6bdc8 | ||
|
|
8cac0c1039 | ||
|
|
9727f67b8c | ||
|
|
348426faa1 | ||
|
|
514efc90d5 | ||
|
|
6d09b02d9f | ||
|
|
cbd4d21f3f | ||
|
|
d9e0abd3ff | ||
|
|
8e0c57760b | ||
|
|
a99a6af307 | ||
|
|
50ae510929 | ||
|
|
13ae3647be | ||
|
|
289913f14b | ||
|
|
e676142d72 | ||
|
|
79af327858 | ||
|
|
4adcba88f2 | ||
|
|
22fa2c3f40 | ||
|
|
001abd663c | ||
|
|
822070dfdc | ||
|
|
2f2aa40362 | ||
|
|
04ff5b945a | ||
|
|
e4b76abd46 | ||
|
|
16ec81b77a | ||
|
|
dfbef437f8 | ||
|
|
8b8f2354b3 | ||
|
|
6d653f8876 | ||
|
|
5a1f524e83 | ||
|
|
17d22f345f | ||
|
|
7df089bebe | ||
|
|
eefa09f93b | ||
|
|
db63c5cc2f | ||
|
|
b9267af6c1 | ||
|
|
860a8a8e03 | ||
|
|
36a5386a3d | ||
|
|
05b000bb5d | ||
|
|
2ec2a26391 | ||
|
|
afe3913893 | ||
|
|
e707138d14 | ||
|
|
9f8e9eaae2 | ||
|
|
19a8e0309c | ||
|
|
cc3f2dc801 | ||
|
|
787bb1801f | ||
|
|
bb06eccb87 | ||
|
|
3e966ca778 | ||
|
|
86ac47841c | ||
|
|
dcfa7cc4bf | ||
|
|
2fb9a980b8 |
@@ -12,7 +12,7 @@ pull_requests:
|
||||
image: Visual Studio 2017
|
||||
clone_depth: 10
|
||||
install:
|
||||
- cinst msbuild-sonarqube-runner
|
||||
- ps: if (-Not $env:APPVEYOR_PULL_REQUEST_NUMBER) { cinst msbuild-sonarqube-runner }
|
||||
|
||||
before_build:
|
||||
- cmd: nuget restore SpaceStation14.sln
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
root = true
|
||||
root = true
|
||||
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
charset = utf-8-bom
|
||||
|
||||
[*.{csproj,xml,yml,dll.config}]
|
||||
indent_size = 2
|
||||
|
||||
14
.github/CODEOWNERS
vendored
Normal file
14
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
# These owners will be the default owners for everything in the repo.
|
||||
# * @defunkt
|
||||
* @PJB3005 @Silvertorch5
|
||||
|
||||
# Order is important. The last matching pattern has the most precedence.
|
||||
# So if a pull request only touches javascript files, only these owners
|
||||
# will be requested to review.
|
||||
# *.js @octocat @github/js
|
||||
|
||||
# You can also use email addresses if you prefer.
|
||||
# docs/* docs@example.com
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -52,6 +52,7 @@ _ReSharper*/
|
||||
/Resources/textures/*_UserInterface.png
|
||||
/Resources/textures/*.TAI
|
||||
/Resources/SpriteRenderer/Ogre.log
|
||||
/Resources/Spriterenderer/output
|
||||
/Media
|
||||
/setenv.bat
|
||||
|
||||
@@ -73,3 +74,5 @@ project.lock.json
|
||||
|
||||
# Created by NUnit.
|
||||
TestResult.xml
|
||||
|
||||
NetSerializerDebug.dll
|
||||
@@ -15,7 +15,7 @@ before_script:
|
||||
- "nuget restore SpaceStation14.sln"
|
||||
|
||||
script:
|
||||
- "xbuild /p:Configuration=Release /p:HEADLESS=1 SpaceStation14.sln"
|
||||
- "msbuild /p:Configuration=Release /p:HEADLESS=1 SpaceStation14.sln"
|
||||
- "cd packages/NUnit.ConsoleRunner.3.6.1/tools"
|
||||
- "mono --debug nunit3-console.exe ../../../SS14.UnitTesting/bin/Release/SS14.UnitTesting.dll"
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cd bin\client
|
||||
start SpaceStation14.exe
|
||||
cd bin\client
|
||||
start SpaceStation14.exe
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
cd bin\server
|
||||
start SpaceStation14_Server.exe
|
||||
cd bin\server
|
||||
start SpaceStation14_Server.exe
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
|
||||
* The NetBuffer object is gone; instead there are NetOutgoingMessage and NetIncomingMessage objects
|
||||
|
||||
* No need to allocate a read buffer before calling ReadMessage
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* The NetBuffer object is gone; instead there are NetOutgoingMessage and NetIncomingMessage objects
|
||||
|
||||
* No need to allocate a read buffer before calling ReadMessage
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,113 +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>
|
||||
<!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>
|
||||
|
||||
@@ -1,22 +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
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -1,115 +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>
|
||||
<!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>
|
||||
|
||||
@@ -1,17 +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
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -1,206 +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 diagnostics messages (warnings, errors) 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>
|
||||
<!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 diagnostics messages (warnings, errors) 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>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for an encryption algorithm
|
||||
/// </summary>
|
||||
public interface INetEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Encrypt an outgoing message in place
|
||||
/// </summary>
|
||||
bool Encrypt(NetOutgoingMessage msg);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message in place
|
||||
/// </summary>
|
||||
bool Decrypt(NetIncomingMessage msg);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for an encryption algorithm
|
||||
/// </summary>
|
||||
public interface INetEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// Encrypt an outgoing message in place
|
||||
/// </summary>
|
||||
bool Encrypt(NetOutgoingMessage msg);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message in place
|
||||
/// </summary>
|
||||
bool Decrypt(NetIncomingMessage msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,163 +1,163 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// AES encryption
|
||||
/// </summary>
|
||||
public class NetAESEncryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetAESEncryption()
|
||||
{
|
||||
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in aes.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in aes.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetAESEncryption constructor
|
||||
/// </summary>
|
||||
public NetAESEncryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetAESEncryption constructor
|
||||
/// </summary>
|
||||
public NetAESEncryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetAESEncryption constructor
|
||||
/// </summary>
|
||||
public NetAESEncryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// AES encryption
|
||||
/// </summary>
|
||||
public class NetAESEncryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetAESEncryption()
|
||||
{
|
||||
AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in aes.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in aes.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetAESEncryption constructor
|
||||
/// </summary>
|
||||
public NetAESEncryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetAESEncryption constructor
|
||||
/// </summary>
|
||||
public NetAESEncryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetAESEncryption constructor
|
||||
/// </summary>
|
||||
public NetAESEncryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = aesCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +1,88 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for a non-threadsafe encryption class
|
||||
/// </summary>
|
||||
public abstract class NetBlockEncryptionBase : INetEncryption
|
||||
{
|
||||
// temporary space for one block to avoid reallocating every time
|
||||
private byte[] m_tmp;
|
||||
|
||||
/// <summary>
|
||||
/// Block size in bytes for this cipher
|
||||
/// </summary>
|
||||
public abstract int BlockSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// NetBlockEncryptionBase constructor
|
||||
/// </summary>
|
||||
public NetBlockEncryptionBase()
|
||||
{
|
||||
m_tmp = new byte[BlockSize];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int payloadBitLength = msg.LengthBits;
|
||||
int numBytes = msg.LengthBytes;
|
||||
int blockSize = BlockSize;
|
||||
int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize);
|
||||
int dstSize = numBlocks * blockSize;
|
||||
|
||||
msg.EnsureBufferSize(dstSize * 8 + (4 * 8)); // add 4 bytes for payload length at end
|
||||
msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written
|
||||
|
||||
for(int i=0;i<numBlocks;i++)
|
||||
{
|
||||
EncryptBlock(msg.m_data, (i * blockSize), m_tmp);
|
||||
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
|
||||
}
|
||||
|
||||
// add true payload length last
|
||||
msg.Write((UInt32)payloadBitLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message encrypted with corresponding Encrypt
|
||||
/// </summary>
|
||||
/// <param name="msg">message to decrypt</param>
|
||||
/// <returns>true if successful; false if failed</returns>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int numEncryptedBytes = msg.LengthBytes - 4; // last 4 bytes is true bit length
|
||||
int blockSize = BlockSize;
|
||||
int numBlocks = numEncryptedBytes / blockSize;
|
||||
if (numBlocks * blockSize != numEncryptedBytes)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
DecryptBlock(msg.m_data, (i * blockSize), m_tmp);
|
||||
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
|
||||
}
|
||||
|
||||
// read 32 bits of true payload length
|
||||
uint realSize = NetBitWriter.ReadUInt32(msg.m_data, 32, (numEncryptedBytes * 8));
|
||||
msg.m_bitLength = (int)realSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt a block of bytes
|
||||
/// </summary>
|
||||
protected abstract void EncryptBlock(byte[] source, int sourceOffset, byte[] destination);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt a block of bytes
|
||||
/// </summary>
|
||||
protected abstract void DecryptBlock(byte[] source, int sourceOffset, byte[] destination);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Base for a non-threadsafe encryption class
|
||||
/// </summary>
|
||||
public abstract class NetBlockEncryptionBase : INetEncryption
|
||||
{
|
||||
// temporary space for one block to avoid reallocating every time
|
||||
private byte[] m_tmp;
|
||||
|
||||
/// <summary>
|
||||
/// Block size in bytes for this cipher
|
||||
/// </summary>
|
||||
public abstract int BlockSize { get; }
|
||||
|
||||
/// <summary>
|
||||
/// NetBlockEncryptionBase constructor
|
||||
/// </summary>
|
||||
public NetBlockEncryptionBase()
|
||||
{
|
||||
m_tmp = new byte[BlockSize];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt am outgoing message with this algorithm; no writing can be done to the message after encryption, or message will be corrupted
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int payloadBitLength = msg.LengthBits;
|
||||
int numBytes = msg.LengthBytes;
|
||||
int blockSize = BlockSize;
|
||||
int numBlocks = (int)Math.Ceiling((double)numBytes / (double)blockSize);
|
||||
int dstSize = numBlocks * blockSize;
|
||||
|
||||
msg.EnsureBufferSize(dstSize * 8 + (4 * 8)); // add 4 bytes for payload length at end
|
||||
msg.LengthBits = dstSize * 8; // length will automatically adjust +4 bytes when payload length is written
|
||||
|
||||
for(int i=0;i<numBlocks;i++)
|
||||
{
|
||||
EncryptBlock(msg.m_data, (i * blockSize), m_tmp);
|
||||
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
|
||||
}
|
||||
|
||||
// add true payload length last
|
||||
msg.Write((UInt32)payloadBitLength);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message encrypted with corresponding Encrypt
|
||||
/// </summary>
|
||||
/// <param name="msg">message to decrypt</param>
|
||||
/// <returns>true if successful; false if failed</returns>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int numEncryptedBytes = msg.LengthBytes - 4; // last 4 bytes is true bit length
|
||||
int blockSize = BlockSize;
|
||||
int numBlocks = numEncryptedBytes / blockSize;
|
||||
if (numBlocks * blockSize != numEncryptedBytes)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
DecryptBlock(msg.m_data, (i * blockSize), m_tmp);
|
||||
Buffer.BlockCopy(m_tmp, 0, msg.m_data, (i * blockSize), m_tmp.Length);
|
||||
}
|
||||
|
||||
// read 32 bits of true payload length
|
||||
uint realSize = NetBitWriter.ReadUInt32(msg.m_data, 32, (numEncryptedBytes * 8));
|
||||
msg.m_bitLength = (int)realSize;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt a block of bytes
|
||||
/// </summary>
|
||||
protected abstract void EncryptBlock(byte[] source, int sourceOffset, byte[] destination);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt a block of bytes
|
||||
/// </summary>
|
||||
protected abstract void DecryptBlock(byte[] source, int sourceOffset, byte[] destination);
|
||||
}
|
||||
}
|
||||
|
||||
77
Lidgren.Network/Encryption/NetCryptoProviderBase.cs
Normal file
77
Lidgren.Network/Encryption/NetCryptoProviderBase.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public abstract class NetCryptoProviderBase : NetEncryption
|
||||
{
|
||||
protected SymmetricAlgorithm m_algorithm;
|
||||
|
||||
public NetCryptoProviderBase(NetPeer peer, SymmetricAlgorithm algo)
|
||||
: base(peer)
|
||||
{
|
||||
m_algorithm = algo;
|
||||
m_algorithm.GenerateKey();
|
||||
m_algorithm.GenerateIV();
|
||||
}
|
||||
|
||||
public override void SetKey(byte[] data, int offset, int count)
|
||||
{
|
||||
int len = m_algorithm.Key.Length;
|
||||
var key = new byte[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
key[i] = data[offset + (i % count)];
|
||||
m_algorithm.Key = key;
|
||||
|
||||
len = m_algorithm.IV.Length;
|
||||
key = new byte[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
key[len - 1 - i] = data[offset + (i % count)];
|
||||
m_algorithm.IV = key;
|
||||
}
|
||||
|
||||
public override bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int unEncLenBits = msg.LengthBits;
|
||||
|
||||
var ms = new MemoryStream();
|
||||
var cs = new CryptoStream(ms, m_algorithm.CreateEncryptor(), CryptoStreamMode.Write);
|
||||
cs.Write(msg.m_data, 0, msg.LengthBytes);
|
||||
cs.Close();
|
||||
|
||||
// get results
|
||||
var arr = ms.ToArray();
|
||||
ms.Close();
|
||||
|
||||
msg.EnsureBufferSize((arr.Length + 4) * 8);
|
||||
msg.LengthBits = 0; // reset write pointer
|
||||
msg.Write((uint)unEncLenBits);
|
||||
msg.Write(arr);
|
||||
msg.LengthBits = (arr.Length + 4) * 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int unEncLenBits = (int)msg.ReadUInt32();
|
||||
|
||||
var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4);
|
||||
var cs = new CryptoStream(ms, m_algorithm.CreateDecryptor(), CryptoStreamMode.Read);
|
||||
|
||||
var byteLen = NetUtility.BytesToHoldBits(unEncLenBits);
|
||||
var result = m_peer.GetStorage(byteLen);
|
||||
cs.Read(result, 0, byteLen);
|
||||
cs.Close();
|
||||
|
||||
// TODO: recycle existing msg
|
||||
|
||||
msg.m_data = result;
|
||||
msg.m_bitLength = unEncLenBits;
|
||||
msg.m_readPosition = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs
Normal file
59
Lidgren.Network/Encryption/NetCryptoProviderEncryption.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public abstract class NetCryptoProviderEncryption : NetEncryption
|
||||
{
|
||||
public NetCryptoProviderEncryption(NetPeer peer)
|
||||
: base(peer)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract CryptoStream GetEncryptStream(MemoryStream ms);
|
||||
|
||||
protected abstract CryptoStream GetDecryptStream(MemoryStream ms);
|
||||
|
||||
public override bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int unEncLenBits = msg.LengthBits;
|
||||
|
||||
var ms = new MemoryStream();
|
||||
var cs = GetEncryptStream(ms);
|
||||
cs.Write(msg.m_data, 0, msg.LengthBytes);
|
||||
cs.Close();
|
||||
|
||||
// get results
|
||||
var arr = ms.ToArray();
|
||||
ms.Close();
|
||||
|
||||
msg.EnsureBufferSize((arr.Length + 4) * 8);
|
||||
msg.LengthBits = 0; // reset write pointer
|
||||
msg.Write((uint)unEncLenBits);
|
||||
msg.Write(arr);
|
||||
msg.LengthBits = (arr.Length + 4) * 8;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int unEncLenBits = (int)msg.ReadUInt32();
|
||||
|
||||
var ms = new MemoryStream(msg.m_data, 4, msg.LengthBytes - 4);
|
||||
var cs = GetDecryptStream(ms);
|
||||
|
||||
var result = m_peer.GetStorage(unEncLenBits);
|
||||
cs.Read(result, 0, NetUtility.BytesToHoldBits(unEncLenBits));
|
||||
cs.Close();
|
||||
|
||||
// TODO: recycle existing msg
|
||||
|
||||
msg.m_data = result;
|
||||
msg.m_bitLength = unEncLenBits;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// DES encryption
|
||||
/// </summary>
|
||||
public class NetDESEncryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetDESEncryption()
|
||||
{
|
||||
|
||||
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in des.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in des.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetDESEncryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetDESEncryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetDESEncryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// DES encryption
|
||||
/// </summary>
|
||||
public class NetDESEncryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetDESEncryption()
|
||||
{
|
||||
|
||||
DESCryptoServiceProvider des = new DESCryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in des.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in des.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetDESEncryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetDESEncryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetDESEncryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (DESCryptoServiceProvider desCryptoServiceProvider = new DESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = desCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Lidgren.Network/Encryption/NetEncryption.cs
Normal file
45
Lidgren.Network/Encryption/NetEncryption.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for an encryption algorithm
|
||||
/// </summary>
|
||||
public abstract class NetEncryption
|
||||
{
|
||||
/// <summary>
|
||||
/// NetPeer
|
||||
/// </summary>
|
||||
protected NetPeer m_peer;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public NetEncryption(NetPeer peer)
|
||||
{
|
||||
if (peer == null)
|
||||
throw new NetException("Peer must not be null");
|
||||
m_peer = peer;
|
||||
}
|
||||
|
||||
public void SetKey(string str)
|
||||
{
|
||||
var bytes = System.Text.Encoding.ASCII.GetBytes(str);
|
||||
SetKey(bytes, 0, bytes.Length);
|
||||
}
|
||||
|
||||
public abstract void SetKey(byte[] data, int offset, int count);
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt an outgoing message in place
|
||||
/// </summary>
|
||||
public abstract bool Encrypt(NetOutgoingMessage msg);
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message in place
|
||||
/// </summary>
|
||||
public abstract bool Decrypt(NetIncomingMessage msg);
|
||||
}
|
||||
}
|
||||
@@ -1,165 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// RC2 encryption
|
||||
/// </summary>
|
||||
public class NetRC2Encryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetRC2Encryption()
|
||||
{
|
||||
|
||||
RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in rc2.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in rc2.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetRC2Encryption constructor
|
||||
/// </summary>
|
||||
public NetRC2Encryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetRC2Encryption constructor
|
||||
/// </summary>
|
||||
public NetRC2Encryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetRC2Encryption constructor
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public NetRC2Encryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// RC2 encryption
|
||||
/// </summary>
|
||||
public class NetRC2Encryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetRC2Encryption()
|
||||
{
|
||||
|
||||
RC2CryptoServiceProvider rc2 = new RC2CryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in rc2.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in rc2.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetRC2Encryption constructor
|
||||
/// </summary>
|
||||
public NetRC2Encryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetRC2Encryption constructor
|
||||
/// </summary>
|
||||
public NetRC2Encryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetRC2Encryption constructor
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
public NetRC2Encryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (RC2CryptoServiceProvider rc2CryptoServiceProvider = new RC2CryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = rc2CryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Triple DES encryption
|
||||
/// </summary>
|
||||
public class NetTripleDESEncryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetTripleDESEncryption()
|
||||
{
|
||||
|
||||
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in tripleDES.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in tripleDES.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetTriplsDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetTripleDESEncryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetTriplsDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetTripleDESEncryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetTriplsDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetTripleDESEncryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Triple DES encryption
|
||||
/// </summary>
|
||||
public class NetTripleDESEncryption : INetEncryption
|
||||
{
|
||||
private readonly byte[] m_key;
|
||||
private readonly byte[] m_iv;
|
||||
private readonly int m_bitSize;
|
||||
private static readonly List<int> m_keysizes;
|
||||
private static readonly List<int> m_blocksizes;
|
||||
|
||||
static NetTripleDESEncryption()
|
||||
{
|
||||
|
||||
TripleDESCryptoServiceProvider tripleDES = new TripleDESCryptoServiceProvider();
|
||||
List<int> temp = new List<int>();
|
||||
foreach (KeySizes keysize in tripleDES.LegalKeySizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_keysizes = temp;
|
||||
temp = new List<int>();
|
||||
foreach (KeySizes keysize in tripleDES.LegalBlockSizes)
|
||||
{
|
||||
for (int i = keysize.MinSize; i <= keysize.MaxSize; i += keysize.SkipSize)
|
||||
{
|
||||
|
||||
if (!temp.Contains(i))
|
||||
temp.Add(i);
|
||||
if (i == keysize.MaxSize)
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_blocksizes = temp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetTriplsDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetTripleDESEncryption(byte[] key, byte[] iv)
|
||||
{
|
||||
if (!m_keysizes.Contains(key.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
if (!m_blocksizes.Contains(iv.Length * 8))
|
||||
throw new NetException(string.Format("Not a valid iv size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_blocksizes)));
|
||||
|
||||
m_key = key;
|
||||
m_iv = iv;
|
||||
m_bitSize = m_key.Length * 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetTriplsDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetTripleDESEncryption(string key, int bitsize)
|
||||
{
|
||||
if (!m_keysizes.Contains(bitsize))
|
||||
throw new NetException(string.Format("Not a valid key size. (Valid values are: {0})", NetUtility.MakeCommaDelimitedList(m_keysizes)));
|
||||
|
||||
byte[] entropy = Encoding.UTF32.GetBytes(key);
|
||||
// I know hardcoding salts is bad, but in this case I think it is acceptable.
|
||||
HMACSHA512 hmacsha512 = new HMACSHA512(Convert.FromBase64String("i88NEiez3c50bHqr3YGasDc4p8jRrxJAaiRiqixpvp4XNAStP5YNoC2fXnWkURtkha6M8yY901Gj07IRVIRyGL=="));
|
||||
hmacsha512.Initialize();
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
entropy = hmacsha512.ComputeHash(entropy);
|
||||
}
|
||||
int keylen = bitsize / 8;
|
||||
m_key = new byte[keylen];
|
||||
Buffer.BlockCopy(entropy, 0, m_key, 0, keylen);
|
||||
m_iv = new byte[m_blocksizes[0] / 8];
|
||||
|
||||
Buffer.BlockCopy(entropy, entropy.Length - m_iv.Length - 1, m_iv, 0, m_iv.Length);
|
||||
m_bitSize = bitsize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetTriplsDESEncryption constructor
|
||||
/// </summary>
|
||||
public NetTripleDESEncryption(string key)
|
||||
: this(key, m_keysizes[0])
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateEncryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
// nested usings are fun!
|
||||
using (TripleDESCryptoServiceProvider tripleDESCryptoServiceProvider = new TripleDESCryptoServiceProvider { KeySize = m_bitSize, Mode = CipherMode.CBC })
|
||||
{
|
||||
using (ICryptoTransform cryptoTransform = tripleDESCryptoServiceProvider.CreateDecryptor(m_key, m_iv))
|
||||
{
|
||||
using (MemoryStream memoryStream = new MemoryStream())
|
||||
{
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform,
|
||||
CryptoStreamMode.Write))
|
||||
{
|
||||
cryptoStream.Write(msg.m_data, 0, msg.m_data.Length);
|
||||
}
|
||||
msg.m_data = memoryStream.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Example class; not very good encryption
|
||||
/// </summary>
|
||||
public class NetXorEncryption : INetEncryption
|
||||
{
|
||||
private byte[] m_key;
|
||||
|
||||
/// <summary>
|
||||
/// NetXorEncryption constructor
|
||||
/// </summary>
|
||||
public NetXorEncryption(byte[] key)
|
||||
{
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetXorEncryption constructor
|
||||
/// </summary>
|
||||
public NetXorEncryption(string key)
|
||||
{
|
||||
m_key = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt an outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int numBytes = msg.LengthBytes;
|
||||
for (int i = 0; i < numBytes; i++)
|
||||
{
|
||||
int offset = i % m_key.Length;
|
||||
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int numBytes = msg.LengthBytes;
|
||||
for (int i = 0; i < numBytes; i++)
|
||||
{
|
||||
int offset = i % m_key.Length;
|
||||
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Example class; not very good encryption
|
||||
/// </summary>
|
||||
public class NetXorEncryption : INetEncryption
|
||||
{
|
||||
private byte[] m_key;
|
||||
|
||||
/// <summary>
|
||||
/// NetXorEncryption constructor
|
||||
/// </summary>
|
||||
public NetXorEncryption(byte[] key)
|
||||
{
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetXorEncryption constructor
|
||||
/// </summary>
|
||||
public NetXorEncryption(string key)
|
||||
{
|
||||
m_key = Encoding.UTF8.GetBytes(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt an outgoing message
|
||||
/// </summary>
|
||||
public bool Encrypt(NetOutgoingMessage msg)
|
||||
{
|
||||
int numBytes = msg.LengthBytes;
|
||||
for (int i = 0; i < numBytes; i++)
|
||||
{
|
||||
int offset = i % m_key.Length;
|
||||
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt an incoming message
|
||||
/// </summary>
|
||||
public bool Decrypt(NetIncomingMessage msg)
|
||||
{
|
||||
int numBytes = msg.LengthBytes;
|
||||
for (int i = 0; i < numBytes; i++)
|
||||
{
|
||||
int offset = i % m_key.Length;
|
||||
msg.m_data[i] = (byte)(msg.m_data[i] ^ m_key[offset]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +1,146 @@
|
||||
/* 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.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Security;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Methods to encrypt and decrypt data using the XTEA algorithm
|
||||
/// </summary>
|
||||
public sealed class NetXtea : NetBlockEncryptionBase
|
||||
{
|
||||
private const int c_blockSize = 8;
|
||||
private const int c_keySize = 16;
|
||||
private const int c_delta = unchecked((int)0x9E3779B9);
|
||||
|
||||
private readonly int m_numRounds;
|
||||
private readonly uint[] m_sum0;
|
||||
private readonly uint[] m_sum1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block size for this cipher
|
||||
/// </summary>
|
||||
public override int BlockSize { get { return c_blockSize; } }
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte key
|
||||
/// </summary>
|
||||
public NetXtea(byte[] key, int rounds)
|
||||
{
|
||||
if (key.Length < c_keySize)
|
||||
throw new NetException("Key too short!");
|
||||
|
||||
m_numRounds = rounds;
|
||||
m_sum0 = new uint[m_numRounds];
|
||||
m_sum1 = new uint[m_numRounds];
|
||||
uint[] tmp = new uint[8];
|
||||
|
||||
int num2;
|
||||
int index = num2 = 0;
|
||||
while (index < 4)
|
||||
{
|
||||
tmp[index] = BitConverter.ToUInt32(key, num2);
|
||||
index++;
|
||||
num2 += 4;
|
||||
}
|
||||
for (index = num2 = 0; index < 32; index++)
|
||||
{
|
||||
m_sum0[index] = ((uint)num2) + tmp[num2 & 3];
|
||||
num2 += -1640531527;
|
||||
m_sum1[index] = ((uint)num2) + tmp[(num2 >> 11) & 3];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte key
|
||||
/// </summary>
|
||||
public NetXtea(byte[] key)
|
||||
: this(key, 32)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String to hash for key
|
||||
/// </summary>
|
||||
public NetXtea(string key)
|
||||
: this(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key)), 32)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a block of bytes
|
||||
/// </summary>
|
||||
protected override void EncryptBlock(byte[] source, int sourceOffset, byte[] destination)
|
||||
{
|
||||
uint v0 = BytesToUInt(source, sourceOffset);
|
||||
uint v1 = BytesToUInt(source, sourceOffset + 4);
|
||||
|
||||
for (int i = 0; i != m_numRounds; i++)
|
||||
{
|
||||
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
|
||||
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
|
||||
}
|
||||
|
||||
UIntToBytes(v0, destination, 0);
|
||||
UIntToBytes(v1, destination, 0 + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a block of bytes
|
||||
/// </summary>
|
||||
protected override void DecryptBlock(byte[] source, int sourceOffset, byte[] destination)
|
||||
{
|
||||
// Pack bytes into integers
|
||||
uint v0 = BytesToUInt(source, sourceOffset);
|
||||
uint v1 = BytesToUInt(source, sourceOffset + 4);
|
||||
|
||||
for (int i = m_numRounds - 1; i >= 0; i--)
|
||||
{
|
||||
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
|
||||
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
|
||||
}
|
||||
|
||||
UIntToBytes(v0, destination, 0);
|
||||
UIntToBytes(v1, destination, 0 + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private static uint BytesToUInt(byte[] bytes, int offset)
|
||||
{
|
||||
uint retval = (uint)(bytes[offset] << 24);
|
||||
retval |= (uint)(bytes[++offset] << 16);
|
||||
retval |= (uint)(bytes[++offset] << 8);
|
||||
return (retval | bytes[++offset]);
|
||||
}
|
||||
|
||||
private static void UIntToBytes(uint value, byte[] destination, int destinationOffset)
|
||||
{
|
||||
destination[destinationOffset++] = (byte)(value >> 24);
|
||||
destination[destinationOffset++] = (byte)(value >> 16);
|
||||
destination[destinationOffset++] = (byte)(value >> 8);
|
||||
destination[destinationOffset++] = (byte)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Security;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Methods to encrypt and decrypt data using the XTEA algorithm
|
||||
/// </summary>
|
||||
public sealed class NetXtea : NetBlockEncryptionBase
|
||||
{
|
||||
private const int c_blockSize = 8;
|
||||
private const int c_keySize = 16;
|
||||
private const int c_delta = unchecked((int)0x9E3779B9);
|
||||
|
||||
private readonly int m_numRounds;
|
||||
private readonly uint[] m_sum0;
|
||||
private readonly uint[] m_sum1;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the block size for this cipher
|
||||
/// </summary>
|
||||
public override int BlockSize { get { return c_blockSize; } }
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte key
|
||||
/// </summary>
|
||||
public NetXtea(byte[] key, int rounds)
|
||||
{
|
||||
if (key.Length < c_keySize)
|
||||
throw new NetException("Key too short!");
|
||||
|
||||
m_numRounds = rounds;
|
||||
m_sum0 = new uint[m_numRounds];
|
||||
m_sum1 = new uint[m_numRounds];
|
||||
uint[] tmp = new uint[8];
|
||||
|
||||
int num2;
|
||||
int index = num2 = 0;
|
||||
while (index < 4)
|
||||
{
|
||||
tmp[index] = BitConverter.ToUInt32(key, num2);
|
||||
index++;
|
||||
num2 += 4;
|
||||
}
|
||||
for (index = num2 = 0; index < 32; index++)
|
||||
{
|
||||
m_sum0[index] = ((uint)num2) + tmp[num2 & 3];
|
||||
num2 += -1640531527;
|
||||
m_sum1[index] = ((uint)num2) + tmp[(num2 >> 11) & 3];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte key
|
||||
/// </summary>
|
||||
public NetXtea(byte[] key)
|
||||
: this(key, 32)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String to hash for key
|
||||
/// </summary>
|
||||
public NetXtea(string key)
|
||||
: this(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key)), 32)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypts a block of bytes
|
||||
/// </summary>
|
||||
protected override void EncryptBlock(byte[] source, int sourceOffset, byte[] destination)
|
||||
{
|
||||
uint v0 = BytesToUInt(source, sourceOffset);
|
||||
uint v1 = BytesToUInt(source, sourceOffset + 4);
|
||||
|
||||
for (int i = 0; i != m_numRounds; i++)
|
||||
{
|
||||
v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
|
||||
v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
|
||||
}
|
||||
|
||||
UIntToBytes(v0, destination, 0);
|
||||
UIntToBytes(v1, destination, 0 + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypts a block of bytes
|
||||
/// </summary>
|
||||
protected override void DecryptBlock(byte[] source, int sourceOffset, byte[] destination)
|
||||
{
|
||||
// Pack bytes into integers
|
||||
uint v0 = BytesToUInt(source, sourceOffset);
|
||||
uint v1 = BytesToUInt(source, sourceOffset + 4);
|
||||
|
||||
for (int i = m_numRounds - 1; i >= 0; i--)
|
||||
{
|
||||
v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ m_sum1[i];
|
||||
v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ m_sum0[i];
|
||||
}
|
||||
|
||||
UIntToBytes(v0, destination, 0);
|
||||
UIntToBytes(v1, destination, 0 + 4);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private static uint BytesToUInt(byte[] bytes, int offset)
|
||||
{
|
||||
uint retval = (uint)(bytes[offset] << 24);
|
||||
retval |= (uint)(bytes[++offset] << 16);
|
||||
retval |= (uint)(bytes[++offset] << 8);
|
||||
return (retval | bytes[++offset]);
|
||||
}
|
||||
|
||||
private static void UIntToBytes(uint value, byte[] destination, int destinationOffset)
|
||||
{
|
||||
destination[destinationOffset++] = (byte)(value >> 24);
|
||||
destination[destinationOffset++] = (byte)(value >> 16);
|
||||
destination[destinationOffset++] = (byte)(value >> 8);
|
||||
destination[destinationOffset++] = (byte)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
|
||||
<PropertyGroup>
|
||||
<ProjectType>Local</ProjectType>
|
||||
@@ -297,7 +297,7 @@
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Lidgren Network Library
|
||||
/// </summary>
|
||||
internal class NamespaceDoc
|
||||
{
|
||||
// <include file='_Namespace.xml' path='Documentation/*' />
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Lidgren Network Library
|
||||
/// </summary>
|
||||
internal class NamespaceDoc
|
||||
{
|
||||
// <include file='_Namespace.xml' path='Documentation/*' />
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,172 +1,172 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed size vector of booleans
|
||||
/// </summary>
|
||||
public sealed class NetBitVector
|
||||
{
|
||||
private readonly int m_capacity;
|
||||
private readonly int[] m_data;
|
||||
private int m_numBitsSet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bits/booleans stored in this vector
|
||||
/// </summary>
|
||||
public int Capacity { get { return m_capacity; } }
|
||||
|
||||
/// <summary>
|
||||
/// NetBitVector constructor
|
||||
/// </summary>
|
||||
public NetBitVector(int bitsCapacity)
|
||||
{
|
||||
m_capacity = bitsCapacity;
|
||||
m_data = new int[(bitsCapacity + 31) / 32];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all bits/booleans are set to zero/false
|
||||
/// </summary>
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return (m_numBitsSet == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of bits/booleans set to one/true
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int Count()
|
||||
{
|
||||
return m_numBitsSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shift all bits one step down, cycling the first bit to the top
|
||||
/// </summary>
|
||||
public void RotateDown()
|
||||
{
|
||||
int lenMinusOne = m_data.Length - 1;
|
||||
|
||||
int firstBit = m_data[0] & 1;
|
||||
for (int i = 0; i < lenMinusOne; i++)
|
||||
m_data[i] = ((m_data[i] >> 1) & ~(1 << 31)) | m_data[i + 1] << 31;
|
||||
|
||||
int lastIndex = m_capacity - 1 - (32 * lenMinusOne);
|
||||
|
||||
// special handling of last int
|
||||
int cur = m_data[lenMinusOne];
|
||||
cur = cur >> 1;
|
||||
cur |= firstBit << lastIndex;
|
||||
|
||||
m_data[lenMinusOne] = cur;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first (lowest) index set to true
|
||||
/// </summary>
|
||||
public int GetFirstSetIndex()
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
int 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bit/bool at the specified index
|
||||
/// </summary>
|
||||
public bool Get(int bitIndex)
|
||||
{
|
||||
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
|
||||
|
||||
return (m_data[bitIndex / 32] & (1 << (bitIndex % 32))) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets or clears the bit/bool at the specified index
|
||||
/// </summary>
|
||||
public void Set(int bitIndex, bool value)
|
||||
{
|
||||
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
|
||||
|
||||
int idx = bitIndex / 32;
|
||||
if (value)
|
||||
{
|
||||
if ((m_data[idx] & (1 << (bitIndex % 32))) == 0)
|
||||
m_numBitsSet++;
|
||||
m_data[idx] |= (1 << (bitIndex % 32));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_data[idx] & (1 << (bitIndex % 32))) != 0)
|
||||
m_numBitsSet--;
|
||||
m_data[idx] &= (~(1 << (bitIndex % 32)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bit/bool at the specified index
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.IndexerName("Bit")]
|
||||
public bool this[int index]
|
||||
{
|
||||
get { return Get(index); }
|
||||
set { Set(index, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all bits/booleans to zero/false
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(m_data, 0, m_data.Length);
|
||||
m_numBitsSet = 0;
|
||||
NetException.Assert(this.IsEmpty());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder(m_capacity + 2);
|
||||
bdr.Append('[');
|
||||
for (int i = 0; i < m_capacity; i++)
|
||||
bdr.Append(Get(m_capacity - i - 1) ? '1' : '0');
|
||||
bdr.Append(']');
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Fixed size vector of booleans
|
||||
/// </summary>
|
||||
public sealed class NetBitVector
|
||||
{
|
||||
private readonly int m_capacity;
|
||||
private readonly int[] m_data;
|
||||
private int m_numBitsSet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bits/booleans stored in this vector
|
||||
/// </summary>
|
||||
public int Capacity { get { return m_capacity; } }
|
||||
|
||||
/// <summary>
|
||||
/// NetBitVector constructor
|
||||
/// </summary>
|
||||
public NetBitVector(int bitsCapacity)
|
||||
{
|
||||
m_capacity = bitsCapacity;
|
||||
m_data = new int[(bitsCapacity + 31) / 32];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all bits/booleans are set to zero/false
|
||||
/// </summary>
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return (m_numBitsSet == 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of bits/booleans set to one/true
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int Count()
|
||||
{
|
||||
return m_numBitsSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shift all bits one step down, cycling the first bit to the top
|
||||
/// </summary>
|
||||
public void RotateDown()
|
||||
{
|
||||
int lenMinusOne = m_data.Length - 1;
|
||||
|
||||
int firstBit = m_data[0] & 1;
|
||||
for (int i = 0; i < lenMinusOne; i++)
|
||||
m_data[i] = ((m_data[i] >> 1) & ~(1 << 31)) | m_data[i + 1] << 31;
|
||||
|
||||
int lastIndex = m_capacity - 1 - (32 * lenMinusOne);
|
||||
|
||||
// special handling of last int
|
||||
int cur = m_data[lenMinusOne];
|
||||
cur = cur >> 1;
|
||||
cur |= firstBit << lastIndex;
|
||||
|
||||
m_data[lenMinusOne] = cur;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first (lowest) index set to true
|
||||
/// </summary>
|
||||
public int GetFirstSetIndex()
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
int 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bit/bool at the specified index
|
||||
/// </summary>
|
||||
public bool Get(int bitIndex)
|
||||
{
|
||||
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
|
||||
|
||||
return (m_data[bitIndex / 32] & (1 << (bitIndex % 32))) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets or clears the bit/bool at the specified index
|
||||
/// </summary>
|
||||
public void Set(int bitIndex, bool value)
|
||||
{
|
||||
NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity);
|
||||
|
||||
int idx = bitIndex / 32;
|
||||
if (value)
|
||||
{
|
||||
if ((m_data[idx] & (1 << (bitIndex % 32))) == 0)
|
||||
m_numBitsSet++;
|
||||
m_data[idx] |= (1 << (bitIndex % 32));
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_data[idx] & (1 << (bitIndex % 32))) != 0)
|
||||
m_numBitsSet--;
|
||||
m_data[idx] &= (~(1 << (bitIndex % 32)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bit/bool at the specified index
|
||||
/// </summary>
|
||||
[System.Runtime.CompilerServices.IndexerName("Bit")]
|
||||
public bool this[int index]
|
||||
{
|
||||
get { return Get(index); }
|
||||
set { Set(index, value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets all bits/booleans to zero/false
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(m_data, 0, m_data.Length);
|
||||
m_numBitsSet = 0;
|
||||
NetException.Assert(this.IsEmpty());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder(m_capacity + 2);
|
||||
bdr.Append('[');
|
||||
for (int i = 0; i < m_capacity; i++)
|
||||
bdr.Append(Get(m_capacity - i - 1) ? '1' : '0');
|
||||
bdr.Append(']');
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,312 +1,312 @@
|
||||
/* 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 NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the internal data buffer
|
||||
/// </summary>
|
||||
public byte[] PeekDataBuffer() { return m_data; }
|
||||
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 1-bit Boolean without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an SByte without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte(int numberOfBits)
|
||||
{
|
||||
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int16 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int16 PeekInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
|
||||
return (short)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt16 without advancing the read pointer
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public UInt16 PeekUInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
|
||||
return (ushort)retval;
|
||||
}
|
||||
|
||||
//
|
||||
// 32 bit
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int32 PeekInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
return (Int32)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int64 PeekInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
unchecked
|
||||
{
|
||||
ulong retval = PeekUInt64();
|
||||
long longRetval = (long)retval;
|
||||
return longRetval;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekFloat()
|
||||
{
|
||||
return PeekSingle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekSingle()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = PeekBytes(4);
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 64-bit Double without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a string without advancing the read pointer
|
||||
/// </summary>
|
||||
public string PeekString()
|
||||
{
|
||||
int wasReadPosition = m_readPosition;
|
||||
string retval = ReadString();
|
||||
m_readPosition = wasReadPosition;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the internal data buffer
|
||||
/// </summary>
|
||||
public byte[] PeekDataBuffer() { return m_data; }
|
||||
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 1-bit Boolean without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an SByte without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte(int numberOfBits)
|
||||
{
|
||||
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int16 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int16 PeekInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
|
||||
return (short)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt16 without advancing the read pointer
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public UInt16 PeekUInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt16(m_data, 16, m_readPosition);
|
||||
return (ushort)retval;
|
||||
}
|
||||
|
||||
//
|
||||
// 32 bit
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int32 PeekInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
return (Int32)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int64 PeekInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
unchecked
|
||||
{
|
||||
ulong retval = PeekUInt64();
|
||||
long longRetval = (long)retval;
|
||||
return longRetval;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekFloat()
|
||||
{
|
||||
return PeekSingle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekSingle()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = PeekBytes(4);
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 64-bit Double without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a string without advancing the read pointer
|
||||
/// </summary>
|
||||
public string PeekString()
|
||||
{
|
||||
int wasReadPosition = m_readPosition;
|
||||
string retval = ReadString();
|
||||
m_readPosition = wasReadPosition;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,103 +1,103 @@
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllFields(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllProperties(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException("target");
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllFields(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllProperties(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException("target");
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,91 +1,91 @@
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllFields(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllProperties(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllFields(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllProperties(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,100 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of bytes to overallocate for each message to avoid resizing
|
||||
/// </summary>
|
||||
protected const int c_overAllocateAmount = 4;
|
||||
|
||||
private static readonly Dictionary<Type, MethodInfo> s_readMethods;
|
||||
private static readonly Dictionary<Type, MethodInfo> s_writeMethods;
|
||||
|
||||
internal byte[] m_data;
|
||||
internal int m_bitLength;
|
||||
internal int m_readPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the internal data buffer
|
||||
/// </summary>
|
||||
public byte[] Data
|
||||
{
|
||||
get { return m_data; }
|
||||
set { m_data = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the used portion 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 used portion of the buffer in bits
|
||||
/// </summary>
|
||||
public int LengthBits
|
||||
{
|
||||
get { return m_bitLength; }
|
||||
set
|
||||
{
|
||||
m_bitLength = value;
|
||||
InternalEnsureBufferSize(m_bitLength);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the read position in the buffer, in bits (not bytes)
|
||||
/// </summary>
|
||||
public long Position
|
||||
{
|
||||
get { return (long)m_readPosition; }
|
||||
set { m_readPosition = (int)value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position in the buffer in bytes; note that the bits of the first returned byte may already have been read - check the Position property to make sure.
|
||||
/// </summary>
|
||||
public int PositionInBytes
|
||||
{
|
||||
get { return (int)(m_readPosition / 8); }
|
||||
}
|
||||
|
||||
static NetBuffer()
|
||||
{
|
||||
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) && mi.Name.Substring(4) == mi.ReturnType.Name)
|
||||
{
|
||||
s_readMethods[mi.ReturnType] = mi;
|
||||
}
|
||||
}
|
||||
|
||||
s_writeMethods = new Dictionary<Type, MethodInfo>();
|
||||
methods = typeof(NetOutgoingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
|
||||
foreach (MethodInfo mi in methods)
|
||||
{
|
||||
if (mi.Name.Equals("Write", StringComparison.InvariantCulture))
|
||||
{
|
||||
ParameterInfo[] pis = mi.GetParameters();
|
||||
if (pis.Length == 1)
|
||||
s_writeMethods[pis[0].ParameterType] = mi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetBuffer
|
||||
{
|
||||
/// <summary>
|
||||
/// Number of bytes to overallocate for each message to avoid resizing
|
||||
/// </summary>
|
||||
protected const int c_overAllocateAmount = 4;
|
||||
|
||||
private static readonly Dictionary<Type, MethodInfo> s_readMethods;
|
||||
private static readonly Dictionary<Type, MethodInfo> s_writeMethods;
|
||||
|
||||
internal byte[] m_data;
|
||||
internal int m_bitLength;
|
||||
internal int m_readPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the internal data buffer
|
||||
/// </summary>
|
||||
public byte[] Data
|
||||
{
|
||||
get { return m_data; }
|
||||
set { m_data = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the used portion 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 used portion of the buffer in bits
|
||||
/// </summary>
|
||||
public int LengthBits
|
||||
{
|
||||
get { return m_bitLength; }
|
||||
set
|
||||
{
|
||||
m_bitLength = value;
|
||||
InternalEnsureBufferSize(m_bitLength);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the read position in the buffer, in bits (not bytes)
|
||||
/// </summary>
|
||||
public long Position
|
||||
{
|
||||
get { return (long)m_readPosition; }
|
||||
set { m_readPosition = (int)value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the position in the buffer in bytes; note that the bits of the first returned byte may already have been read - check the Position property to make sure.
|
||||
/// </summary>
|
||||
public int PositionInBytes
|
||||
{
|
||||
get { return (int)(m_readPosition / 8); }
|
||||
}
|
||||
|
||||
static NetBuffer()
|
||||
{
|
||||
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) && mi.Name.Substring(4) == mi.ReturnType.Name)
|
||||
{
|
||||
s_readMethods[mi.ReturnType] = mi;
|
||||
}
|
||||
}
|
||||
|
||||
s_writeMethods = new Dictionary<Type, MethodInfo>();
|
||||
methods = typeof(NetOutgoingMessage).GetMethods(BindingFlags.Instance | BindingFlags.Public);
|
||||
foreach (MethodInfo mi in methods)
|
||||
{
|
||||
if (mi.Name.Equals("Write", StringComparison.InvariantCulture))
|
||||
{
|
||||
ParameterInfo[] pis = mi.GetParameters();
|
||||
if (pis.Length == 1)
|
||||
s_writeMethods[pis[0].ParameterType] = mi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,171 +1,171 @@
|
||||
/* 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>
|
||||
/// Specialized version of NetPeer used for a "client" connection. It does not accept any incoming connections and maintains a ServerConnection property
|
||||
/// </summary>
|
||||
public class NetClient : NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connection status of the server connection (or NetConnectionStatus.Disconnected if no connection)
|
||||
/// </summary>
|
||||
public NetConnectionStatus ConnectionStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
var conn = ServerConnection;
|
||||
if (conn == null)
|
||||
return NetConnectionStatus.Disconnected;
|
||||
return conn.Status;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetClient constructor
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public NetClient(NetPeerConfiguration config)
|
||||
: base(config)
|
||||
{
|
||||
config.AcceptIncomingConnections = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to a remote server
|
||||
/// </summary>
|
||||
/// <param name="remoteEndPoint">The remote endpoint to connect to</param>
|
||||
/// <param name="hailMessage">The hail message to pass</param>
|
||||
/// <returns>server connection, or null if already connected</returns>
|
||||
public override NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
lock (m_connections)
|
||||
{
|
||||
if (m_connections.Count > 0)
|
||||
{
|
||||
LogWarning("Connect attempt failed; Already connected");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
lock (m_handshakes)
|
||||
{
|
||||
if (m_handshakes.Count > 0)
|
||||
{
|
||||
LogWarning("Connect attempt failed; Handshake already in progress");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return base.Connect(remoteEndPoint, hailMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from server
|
||||
/// </summary>
|
||||
/// <param name="byeMessage">reason for disconnect</param>
|
||||
public void Disconnect(string byeMessage)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
lock (m_handshakes)
|
||||
{
|
||||
if (m_handshakes.Count > 0)
|
||||
{
|
||||
LogVerbose("Aborting connection attempt");
|
||||
foreach(var hs in m_handshakes)
|
||||
hs.Value.Disconnect(byeMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LogWarning("Disconnect requested when not connected!");
|
||||
return;
|
||||
}
|
||||
serverConnection.Disconnect(byeMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
LogWarning("Cannot send message, no server connection!");
|
||||
return NetSendResult.FailedNotConnected;
|
||||
}
|
||||
|
||||
return serverConnection.SendMessage(msg, method, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
LogWarning("Cannot send message, no server connection!");
|
||||
return NetSendResult.FailedNotConnected;
|
||||
}
|
||||
|
||||
return serverConnection.SendMessage(msg, method, sequenceChannel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetClient " + ServerConnection + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/* 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>
|
||||
/// Specialized version of NetPeer used for a "client" connection. It does not accept any incoming connections and maintains a ServerConnection property
|
||||
/// </summary>
|
||||
public class NetClient : NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the connection status of the server connection (or NetConnectionStatus.Disconnected if no connection)
|
||||
/// </summary>
|
||||
public NetConnectionStatus ConnectionStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
var conn = ServerConnection;
|
||||
if (conn == null)
|
||||
return NetConnectionStatus.Disconnected;
|
||||
return conn.Status;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetClient constructor
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public NetClient(NetPeerConfiguration config)
|
||||
: base(config)
|
||||
{
|
||||
config.AcceptIncomingConnections = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to a remote server
|
||||
/// </summary>
|
||||
/// <param name="remoteEndPoint">The remote endpoint to connect to</param>
|
||||
/// <param name="hailMessage">The hail message to pass</param>
|
||||
/// <returns>server connection, or null if already connected</returns>
|
||||
public override NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
lock (m_connections)
|
||||
{
|
||||
if (m_connections.Count > 0)
|
||||
{
|
||||
LogWarning("Connect attempt failed; Already connected");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
lock (m_handshakes)
|
||||
{
|
||||
if (m_handshakes.Count > 0)
|
||||
{
|
||||
LogWarning("Connect attempt failed; Handshake already in progress");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return base.Connect(remoteEndPoint, hailMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from server
|
||||
/// </summary>
|
||||
/// <param name="byeMessage">reason for disconnect</param>
|
||||
public void Disconnect(string byeMessage)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
lock (m_handshakes)
|
||||
{
|
||||
if (m_handshakes.Count > 0)
|
||||
{
|
||||
LogVerbose("Aborting connection attempt");
|
||||
foreach(var hs in m_handshakes)
|
||||
hs.Value.Disconnect(byeMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LogWarning("Disconnect requested when not connected!");
|
||||
return;
|
||||
}
|
||||
serverConnection.Disconnect(byeMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
LogWarning("Cannot send message, no server connection!");
|
||||
return NetSendResult.FailedNotConnected;
|
||||
}
|
||||
|
||||
return serverConnection.SendMessage(msg, method, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends message to server
|
||||
/// </summary>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
NetConnection serverConnection = ServerConnection;
|
||||
if (serverConnection == null)
|
||||
{
|
||||
LogWarning("Cannot send message, no server connection!");
|
||||
return NetSendResult.FailedNotConnected;
|
||||
}
|
||||
|
||||
return serverConnection.SendMessage(msg, method, sequenceChannel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetClient " + ServerConnection + "]";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,479 +1,479 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
internal bool m_connectRequested;
|
||||
internal bool m_disconnectRequested;
|
||||
internal bool m_connectionInitiator;
|
||||
internal string m_disconnectMessage;
|
||||
internal NetIncomingMessage m_remoteHailMessage;
|
||||
internal float m_lastHandshakeSendTime;
|
||||
internal int m_handshakeAttempts;
|
||||
|
||||
/// <summary>
|
||||
/// The message that the remote part specified via Connect() or Approve() - can be null.
|
||||
/// </summary>
|
||||
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
|
||||
|
||||
// heartbeat called when connection still is in m_handshakes of NetPeer
|
||||
internal void UnconnectedHeartbeat(float now)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
if (m_disconnectRequested)
|
||||
ExecuteDisconnect(m_disconnectMessage, true);
|
||||
|
||||
if (m_connectRequested)
|
||||
{
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.Connected:
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// reconnect
|
||||
ExecuteDisconnect("Reconnecting", true);
|
||||
break;
|
||||
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// send another connect attempt
|
||||
SendConnect(now);
|
||||
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
|
||||
break;
|
||||
|
||||
case NetConnectionStatus.None:
|
||||
default:
|
||||
SendConnect(now);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (now - m_lastHandshakeSendTime > m_peerConfiguration.m_resendHandshakeInterval)
|
||||
{
|
||||
if (m_handshakeAttempts >= m_peerConfiguration.m_maximumHandshakeAttempts)
|
||||
{
|
||||
// failed to connect
|
||||
ExecuteDisconnect("Failed to establish connection - no response from remote host", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// resend handshake
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
SendConnect(now);
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
SendConnectResponse(now, true);
|
||||
break;
|
||||
case NetConnectionStatus.None:
|
||||
case NetConnectionStatus.ReceivedInitiation:
|
||||
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
|
||||
break;
|
||||
case NetConnectionStatus.RespondedAwaitingApproval:
|
||||
// awaiting approval
|
||||
m_lastHandshakeSendTime = now; // postpone handshake resend
|
||||
break;
|
||||
default:
|
||||
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExecuteDisconnect(string reason, bool sendByeMessage)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
//m_peer.LogDebug("Executing disconnect");
|
||||
|
||||
// clear send queues
|
||||
for (int i = 0; i < m_sendChannels.Length; i++)
|
||||
{
|
||||
NetSenderChannelBase channel = m_sendChannels[i];
|
||||
if (channel != null)
|
||||
channel.Reset();
|
||||
}
|
||||
|
||||
if (sendByeMessage)
|
||||
SendDisconnect(reason, true);
|
||||
|
||||
SetStatus(NetConnectionStatus.Disconnected, reason);
|
||||
|
||||
// in case we're still in handshake
|
||||
lock (m_peer.m_handshakes)
|
||||
m_peer.m_handshakes.Remove(m_remoteEndPoint);
|
||||
|
||||
m_disconnectRequested = false;
|
||||
m_connectRequested = false;
|
||||
m_handshakeAttempts = 0;
|
||||
}
|
||||
|
||||
internal void SendConnect(float now)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
int preAllocate = 13 + m_peerConfiguration.AppIdentifier.Length;
|
||||
preAllocate += (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes);
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(preAllocate);
|
||||
om.m_messageType = NetMessageType.Connect;
|
||||
om.Write(m_peerConfiguration.AppIdentifier);
|
||||
om.Write(m_peer.m_uniqueIdentifier);
|
||||
om.Write(now);
|
||||
|
||||
WriteLocalHail(om);
|
||||
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
|
||||
m_connectRequested = false;
|
||||
m_lastHandshakeSendTime = now;
|
||||
m_handshakeAttempts++;
|
||||
|
||||
if (m_handshakeAttempts > 1)
|
||||
m_peer.LogDebug("Resending Connect...");
|
||||
SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
|
||||
}
|
||||
|
||||
internal void SendConnectResponse(float now, bool onLibraryThread)
|
||||
{
|
||||
if (onLibraryThread)
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 13 + (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes));
|
||||
om.m_messageType = NetMessageType.ConnectResponse;
|
||||
om.Write(m_peerConfiguration.AppIdentifier);
|
||||
om.Write(m_peer.m_uniqueIdentifier);
|
||||
om.Write(now);
|
||||
|
||||
WriteLocalHail(om);
|
||||
|
||||
if (onLibraryThread)
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
else
|
||||
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
|
||||
|
||||
m_lastHandshakeSendTime = now;
|
||||
m_handshakeAttempts++;
|
||||
|
||||
if (m_handshakeAttempts > 1)
|
||||
m_peer.LogDebug("Resending ConnectResponse...");
|
||||
|
||||
SetStatus(NetConnectionStatus.RespondedConnect, "Remotely requested connect");
|
||||
}
|
||||
|
||||
internal void SendDisconnect(string reason, bool onLibraryThread)
|
||||
{
|
||||
if (onLibraryThread)
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(reason);
|
||||
om.m_messageType = NetMessageType.Disconnect;
|
||||
if (onLibraryThread)
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
else
|
||||
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
|
||||
}
|
||||
|
||||
private void WriteLocalHail(NetOutgoingMessage om)
|
||||
{
|
||||
if (m_localHailMessage != null)
|
||||
{
|
||||
byte[] hi = m_localHailMessage.Data;
|
||||
if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
|
||||
{
|
||||
if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10)
|
||||
throw new NetException("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes));
|
||||
om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SendConnectionEstablished()
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(4);
|
||||
om.m_messageType = NetMessageType.ConnectionEstablished;
|
||||
om.Write((float)NetTime.Now);
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
|
||||
m_handshakeAttempts = 0;
|
||||
|
||||
InitializePing();
|
||||
if (m_status != NetConnectionStatus.Connected)
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approves this connection; sending a connection response to the remote host
|
||||
/// </summary>
|
||||
public void Approve()
|
||||
{
|
||||
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
|
||||
{
|
||||
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
|
||||
return;
|
||||
}
|
||||
|
||||
m_localHailMessage = null;
|
||||
m_handshakeAttempts = 0;
|
||||
SendConnectResponse((float)NetTime.Now, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approves this connection; sending a connection response to the remote host
|
||||
/// </summary>
|
||||
/// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param>
|
||||
public void Approve(NetOutgoingMessage localHail)
|
||||
{
|
||||
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
|
||||
{
|
||||
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
|
||||
return;
|
||||
}
|
||||
|
||||
m_localHailMessage = localHail;
|
||||
m_handshakeAttempts = 0;
|
||||
SendConnectResponse((float)NetTime.Now, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Denies this connection; disconnecting it
|
||||
/// </summary>
|
||||
public void Deny()
|
||||
{
|
||||
Deny(string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Denies this connection; disconnecting it
|
||||
/// </summary>
|
||||
/// <param name="reason">The stated reason for the disconnect, readable as a string in the StatusChanged message on the remote host</param>
|
||||
public void Deny(string reason)
|
||||
{
|
||||
// send disconnect; remove from handshakes
|
||||
SendDisconnect(reason, false);
|
||||
|
||||
// remove from handshakes
|
||||
m_peer.m_handshakes.Remove(m_remoteEndPoint); // TODO: make this more thread safe? we're on user thread
|
||||
}
|
||||
|
||||
internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payloadLength)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
byte[] hail;
|
||||
switch (tp)
|
||||
{
|
||||
case NetMessageType.Connect:
|
||||
if (m_status == NetConnectionStatus.ReceivedInitiation)
|
||||
{
|
||||
// Whee! Server full has already been checked
|
||||
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
|
||||
if (ok)
|
||||
{
|
||||
if (hail != null)
|
||||
{
|
||||
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
|
||||
m_remoteHailMessage.LengthBits = (hail.Length * 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_remoteHailMessage = null;
|
||||
}
|
||||
|
||||
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
|
||||
{
|
||||
// ok, let's not add connection just yet
|
||||
NetIncomingMessage appMsg = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, (m_remoteHailMessage == null ? 0 : m_remoteHailMessage.LengthBytes));
|
||||
appMsg.m_receiveTime = now;
|
||||
appMsg.m_senderConnection = this;
|
||||
appMsg.m_senderEndPoint = this.m_remoteEndPoint;
|
||||
if (m_remoteHailMessage != null)
|
||||
appMsg.Write(m_remoteHailMessage.m_data, 0, m_remoteHailMessage.LengthBytes);
|
||||
SetStatus(NetConnectionStatus.RespondedAwaitingApproval, "Awaiting approval");
|
||||
m_peer.ReleaseMessage(appMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
SendConnectResponse((float)now, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_status == NetConnectionStatus.RespondedAwaitingApproval)
|
||||
{
|
||||
m_peer.LogWarning("Ignoring multiple Connect() most likely due to a delayed Approval");
|
||||
return;
|
||||
}
|
||||
if (m_status == NetConnectionStatus.RespondedConnect)
|
||||
{
|
||||
// our ConnectResponse must have been lost
|
||||
SendConnectResponse((float)now, true);
|
||||
return;
|
||||
}
|
||||
m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength);
|
||||
break;
|
||||
case NetMessageType.ConnectResponse:
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// awesome
|
||||
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
|
||||
if (ok)
|
||||
{
|
||||
if (hail != null)
|
||||
{
|
||||
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
|
||||
m_remoteHailMessage.LengthBits = (hail.Length * 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_remoteHailMessage = null;
|
||||
}
|
||||
|
||||
m_peer.AcceptConnection(this);
|
||||
SendConnectionEstablished();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// hello, wtf?
|
||||
break;
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
case NetConnectionStatus.Disconnected:
|
||||
case NetConnectionStatus.ReceivedInitiation:
|
||||
case NetConnectionStatus.None:
|
||||
// wtf? anyway, bye!
|
||||
break;
|
||||
case NetConnectionStatus.Connected:
|
||||
// my ConnectionEstablished must have been lost, send another one
|
||||
SendConnectionEstablished();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NetMessageType.ConnectionEstablished:
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.Connected:
|
||||
// ok...
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
case NetConnectionStatus.None:
|
||||
// too bad, almost made it
|
||||
break;
|
||||
case NetConnectionStatus.ReceivedInitiation:
|
||||
// uh, a little premature... ignore
|
||||
break;
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// weird, should have been RespondedConnect...
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// awesome
|
||||
|
||||
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
InitializeRemoteTimeOffset(msg.ReadSingle());
|
||||
|
||||
m_peer.AcceptConnection(this);
|
||||
InitializePing();
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case NetMessageType.Disconnect:
|
||||
// ouch
|
||||
string reason = "Ouch";
|
||||
try
|
||||
{
|
||||
NetIncomingMessage inc = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
reason = inc.ReadString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
ExecuteDisconnect(reason, false);
|
||||
break;
|
||||
|
||||
case NetMessageType.Discovery:
|
||||
m_peer.HandleIncomingDiscoveryRequest(now, m_remoteEndPoint, ptr, payloadLength);
|
||||
return;
|
||||
|
||||
case NetMessageType.DiscoveryResponse:
|
||||
m_peer.HandleIncomingDiscoveryResponse(now, m_remoteEndPoint, ptr, payloadLength);
|
||||
return;
|
||||
|
||||
case NetMessageType.Ping:
|
||||
// silently ignore
|
||||
return;
|
||||
|
||||
default:
|
||||
m_peer.LogDebug("Unhandled type during handshake: " + tp + " length: " + payloadLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail)
|
||||
{
|
||||
hail = null;
|
||||
|
||||
// create temporary incoming message
|
||||
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
try
|
||||
{
|
||||
string remoteAppIdentifier = msg.ReadString();
|
||||
long remoteUniqueIdentifier = msg.ReadInt64();
|
||||
InitializeRemoteTimeOffset(msg.ReadSingle());
|
||||
|
||||
int remainingBytes = payloadLength - (msg.PositionInBytes - ptr);
|
||||
if (remainingBytes > 0)
|
||||
hail = msg.ReadBytes(remainingBytes);
|
||||
|
||||
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
|
||||
{
|
||||
// wrong app identifier
|
||||
ExecuteDisconnect("Wrong application identifier!", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_remoteUniqueIdentifier = remoteUniqueIdentifier;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
// whatever; we failed
|
||||
ExecuteDisconnect("Handshake data validation failed", true);
|
||||
m_peer.LogWarning("ReadRemoteHandshakeData failed: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the remote peer
|
||||
/// </summary>
|
||||
/// <param name="byeMessage">the message to send with the disconnect message</param>
|
||||
public void Disconnect(string byeMessage)
|
||||
{
|
||||
// user or library thread
|
||||
if (m_status == NetConnectionStatus.None || m_status == NetConnectionStatus.Disconnected)
|
||||
return;
|
||||
|
||||
m_peer.LogVerbose("Disconnect requested for " + this);
|
||||
m_disconnectMessage = byeMessage;
|
||||
|
||||
if (m_status != NetConnectionStatus.Disconnected && m_status != NetConnectionStatus.None)
|
||||
SetStatus(NetConnectionStatus.Disconnecting, byeMessage);
|
||||
|
||||
m_handshakeAttempts = 0;
|
||||
m_disconnectRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
internal bool m_connectRequested;
|
||||
internal bool m_disconnectRequested;
|
||||
internal bool m_connectionInitiator;
|
||||
internal string m_disconnectMessage;
|
||||
internal NetIncomingMessage m_remoteHailMessage;
|
||||
internal float m_lastHandshakeSendTime;
|
||||
internal int m_handshakeAttempts;
|
||||
|
||||
/// <summary>
|
||||
/// The message that the remote part specified via Connect() or Approve() - can be null.
|
||||
/// </summary>
|
||||
public NetIncomingMessage RemoteHailMessage { get { return m_remoteHailMessage; } }
|
||||
|
||||
// heartbeat called when connection still is in m_handshakes of NetPeer
|
||||
internal void UnconnectedHeartbeat(float now)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
if (m_disconnectRequested)
|
||||
ExecuteDisconnect(m_disconnectMessage, true);
|
||||
|
||||
if (m_connectRequested)
|
||||
{
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.Connected:
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// reconnect
|
||||
ExecuteDisconnect("Reconnecting", true);
|
||||
break;
|
||||
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// send another connect attempt
|
||||
SendConnect(now);
|
||||
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
|
||||
break;
|
||||
|
||||
case NetConnectionStatus.None:
|
||||
default:
|
||||
SendConnect(now);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (now - m_lastHandshakeSendTime > m_peerConfiguration.m_resendHandshakeInterval)
|
||||
{
|
||||
if (m_handshakeAttempts >= m_peerConfiguration.m_maximumHandshakeAttempts)
|
||||
{
|
||||
// failed to connect
|
||||
ExecuteDisconnect("Failed to establish connection - no response from remote host", true);
|
||||
return;
|
||||
}
|
||||
|
||||
// resend handshake
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
SendConnect(now);
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
SendConnectResponse(now, true);
|
||||
break;
|
||||
case NetConnectionStatus.None:
|
||||
case NetConnectionStatus.ReceivedInitiation:
|
||||
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
|
||||
break;
|
||||
case NetConnectionStatus.RespondedAwaitingApproval:
|
||||
// awaiting approval
|
||||
m_lastHandshakeSendTime = now; // postpone handshake resend
|
||||
break;
|
||||
default:
|
||||
m_peer.LogWarning("Time to resend handshake, but status is " + m_status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExecuteDisconnect(string reason, bool sendByeMessage)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
//m_peer.LogDebug("Executing disconnect");
|
||||
|
||||
// clear send queues
|
||||
for (int i = 0; i < m_sendChannels.Length; i++)
|
||||
{
|
||||
NetSenderChannelBase channel = m_sendChannels[i];
|
||||
if (channel != null)
|
||||
channel.Reset();
|
||||
}
|
||||
|
||||
if (sendByeMessage)
|
||||
SendDisconnect(reason, true);
|
||||
|
||||
SetStatus(NetConnectionStatus.Disconnected, reason);
|
||||
|
||||
// in case we're still in handshake
|
||||
lock (m_peer.m_handshakes)
|
||||
m_peer.m_handshakes.Remove(m_remoteEndPoint);
|
||||
|
||||
m_disconnectRequested = false;
|
||||
m_connectRequested = false;
|
||||
m_handshakeAttempts = 0;
|
||||
}
|
||||
|
||||
internal void SendConnect(float now)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
int preAllocate = 13 + m_peerConfiguration.AppIdentifier.Length;
|
||||
preAllocate += (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes);
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(preAllocate);
|
||||
om.m_messageType = NetMessageType.Connect;
|
||||
om.Write(m_peerConfiguration.AppIdentifier);
|
||||
om.Write(m_peer.m_uniqueIdentifier);
|
||||
om.Write(now);
|
||||
|
||||
WriteLocalHail(om);
|
||||
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
|
||||
m_connectRequested = false;
|
||||
m_lastHandshakeSendTime = now;
|
||||
m_handshakeAttempts++;
|
||||
|
||||
if (m_handshakeAttempts > 1)
|
||||
m_peer.LogDebug("Resending Connect...");
|
||||
SetStatus(NetConnectionStatus.InitiatedConnect, "Locally requested connect");
|
||||
}
|
||||
|
||||
internal void SendConnectResponse(float now, bool onLibraryThread)
|
||||
{
|
||||
if (onLibraryThread)
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(m_peerConfiguration.AppIdentifier.Length + 13 + (m_localHailMessage == null ? 0 : m_localHailMessage.LengthBytes));
|
||||
om.m_messageType = NetMessageType.ConnectResponse;
|
||||
om.Write(m_peerConfiguration.AppIdentifier);
|
||||
om.Write(m_peer.m_uniqueIdentifier);
|
||||
om.Write(now);
|
||||
|
||||
WriteLocalHail(om);
|
||||
|
||||
if (onLibraryThread)
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
else
|
||||
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
|
||||
|
||||
m_lastHandshakeSendTime = now;
|
||||
m_handshakeAttempts++;
|
||||
|
||||
if (m_handshakeAttempts > 1)
|
||||
m_peer.LogDebug("Resending ConnectResponse...");
|
||||
|
||||
SetStatus(NetConnectionStatus.RespondedConnect, "Remotely requested connect");
|
||||
}
|
||||
|
||||
internal void SendDisconnect(string reason, bool onLibraryThread)
|
||||
{
|
||||
if (onLibraryThread)
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(reason);
|
||||
om.m_messageType = NetMessageType.Disconnect;
|
||||
if (onLibraryThread)
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
else
|
||||
m_peer.m_unsentUnconnectedMessages.Enqueue(new NetTuple<System.Net.IPEndPoint, NetOutgoingMessage>(m_remoteEndPoint, om));
|
||||
}
|
||||
|
||||
private void WriteLocalHail(NetOutgoingMessage om)
|
||||
{
|
||||
if (m_localHailMessage != null)
|
||||
{
|
||||
byte[] hi = m_localHailMessage.Data;
|
||||
if (hi != null && hi.Length >= m_localHailMessage.LengthBytes)
|
||||
{
|
||||
if (om.LengthBytes + m_localHailMessage.LengthBytes > m_peerConfiguration.m_maximumTransmissionUnit - 10)
|
||||
throw new NetException("Hail message too large; can maximally be " + (m_peerConfiguration.m_maximumTransmissionUnit - 10 - om.LengthBytes));
|
||||
om.Write(m_localHailMessage.Data, 0, m_localHailMessage.LengthBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void SendConnectionEstablished()
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(4);
|
||||
om.m_messageType = NetMessageType.ConnectionEstablished;
|
||||
om.Write((float)NetTime.Now);
|
||||
m_peer.SendLibrary(om, m_remoteEndPoint);
|
||||
|
||||
m_handshakeAttempts = 0;
|
||||
|
||||
InitializePing();
|
||||
if (m_status != NetConnectionStatus.Connected)
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approves this connection; sending a connection response to the remote host
|
||||
/// </summary>
|
||||
public void Approve()
|
||||
{
|
||||
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
|
||||
{
|
||||
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
|
||||
return;
|
||||
}
|
||||
|
||||
m_localHailMessage = null;
|
||||
m_handshakeAttempts = 0;
|
||||
SendConnectResponse((float)NetTime.Now, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Approves this connection; sending a connection response to the remote host
|
||||
/// </summary>
|
||||
/// <param name="localHail">The local hail message that will be set as RemoteHailMessage on the remote host</param>
|
||||
public void Approve(NetOutgoingMessage localHail)
|
||||
{
|
||||
if (m_status != NetConnectionStatus.RespondedAwaitingApproval)
|
||||
{
|
||||
m_peer.LogWarning("Approve() called in wrong status; expected RespondedAwaitingApproval; got " + m_status);
|
||||
return;
|
||||
}
|
||||
|
||||
m_localHailMessage = localHail;
|
||||
m_handshakeAttempts = 0;
|
||||
SendConnectResponse((float)NetTime.Now, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Denies this connection; disconnecting it
|
||||
/// </summary>
|
||||
public void Deny()
|
||||
{
|
||||
Deny(string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Denies this connection; disconnecting it
|
||||
/// </summary>
|
||||
/// <param name="reason">The stated reason for the disconnect, readable as a string in the StatusChanged message on the remote host</param>
|
||||
public void Deny(string reason)
|
||||
{
|
||||
// send disconnect; remove from handshakes
|
||||
SendDisconnect(reason, false);
|
||||
|
||||
// remove from handshakes
|
||||
m_peer.m_handshakes.Remove(m_remoteEndPoint); // TODO: make this more thread safe? we're on user thread
|
||||
}
|
||||
|
||||
internal void ReceivedHandshake(double now, NetMessageType tp, int ptr, int payloadLength)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
byte[] hail;
|
||||
switch (tp)
|
||||
{
|
||||
case NetMessageType.Connect:
|
||||
if (m_status == NetConnectionStatus.ReceivedInitiation)
|
||||
{
|
||||
// Whee! Server full has already been checked
|
||||
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
|
||||
if (ok)
|
||||
{
|
||||
if (hail != null)
|
||||
{
|
||||
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
|
||||
m_remoteHailMessage.LengthBits = (hail.Length * 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_remoteHailMessage = null;
|
||||
}
|
||||
|
||||
if (m_peerConfiguration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval))
|
||||
{
|
||||
// ok, let's not add connection just yet
|
||||
NetIncomingMessage appMsg = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionApproval, (m_remoteHailMessage == null ? 0 : m_remoteHailMessage.LengthBytes));
|
||||
appMsg.m_receiveTime = now;
|
||||
appMsg.m_senderConnection = this;
|
||||
appMsg.m_senderEndPoint = this.m_remoteEndPoint;
|
||||
if (m_remoteHailMessage != null)
|
||||
appMsg.Write(m_remoteHailMessage.m_data, 0, m_remoteHailMessage.LengthBytes);
|
||||
SetStatus(NetConnectionStatus.RespondedAwaitingApproval, "Awaiting approval");
|
||||
m_peer.ReleaseMessage(appMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
SendConnectResponse((float)now, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (m_status == NetConnectionStatus.RespondedAwaitingApproval)
|
||||
{
|
||||
m_peer.LogWarning("Ignoring multiple Connect() most likely due to a delayed Approval");
|
||||
return;
|
||||
}
|
||||
if (m_status == NetConnectionStatus.RespondedConnect)
|
||||
{
|
||||
// our ConnectResponse must have been lost
|
||||
SendConnectResponse((float)now, true);
|
||||
return;
|
||||
}
|
||||
m_peer.LogDebug("Unhandled Connect: " + tp + ", status is " + m_status + " length: " + payloadLength);
|
||||
break;
|
||||
case NetMessageType.ConnectResponse:
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// awesome
|
||||
bool ok = ValidateHandshakeData(ptr, payloadLength, out hail);
|
||||
if (ok)
|
||||
{
|
||||
if (hail != null)
|
||||
{
|
||||
m_remoteHailMessage = m_peer.CreateIncomingMessage(NetIncomingMessageType.Data, hail);
|
||||
m_remoteHailMessage.LengthBits = (hail.Length * 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_remoteHailMessage = null;
|
||||
}
|
||||
|
||||
m_peer.AcceptConnection(this);
|
||||
SendConnectionEstablished();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// hello, wtf?
|
||||
break;
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
case NetConnectionStatus.Disconnected:
|
||||
case NetConnectionStatus.ReceivedInitiation:
|
||||
case NetConnectionStatus.None:
|
||||
// wtf? anyway, bye!
|
||||
break;
|
||||
case NetConnectionStatus.Connected:
|
||||
// my ConnectionEstablished must have been lost, send another one
|
||||
SendConnectionEstablished();
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case NetMessageType.ConnectionEstablished:
|
||||
switch (m_status)
|
||||
{
|
||||
case NetConnectionStatus.Connected:
|
||||
// ok...
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
case NetConnectionStatus.Disconnecting:
|
||||
case NetConnectionStatus.None:
|
||||
// too bad, almost made it
|
||||
break;
|
||||
case NetConnectionStatus.ReceivedInitiation:
|
||||
// uh, a little premature... ignore
|
||||
break;
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// weird, should have been RespondedConnect...
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// awesome
|
||||
|
||||
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
InitializeRemoteTimeOffset(msg.ReadSingle());
|
||||
|
||||
m_peer.AcceptConnection(this);
|
||||
InitializePing();
|
||||
SetStatus(NetConnectionStatus.Connected, "Connected to " + NetUtility.ToHexString(m_remoteUniqueIdentifier));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case NetMessageType.Disconnect:
|
||||
// ouch
|
||||
string reason = "Ouch";
|
||||
try
|
||||
{
|
||||
NetIncomingMessage inc = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
reason = inc.ReadString();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
ExecuteDisconnect(reason, false);
|
||||
break;
|
||||
|
||||
case NetMessageType.Discovery:
|
||||
m_peer.HandleIncomingDiscoveryRequest(now, m_remoteEndPoint, ptr, payloadLength);
|
||||
return;
|
||||
|
||||
case NetMessageType.DiscoveryResponse:
|
||||
m_peer.HandleIncomingDiscoveryResponse(now, m_remoteEndPoint, ptr, payloadLength);
|
||||
return;
|
||||
|
||||
case NetMessageType.Ping:
|
||||
// silently ignore
|
||||
return;
|
||||
|
||||
default:
|
||||
m_peer.LogDebug("Unhandled type during handshake: " + tp + " length: " + payloadLength);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateHandshakeData(int ptr, int payloadLength, out byte[] hail)
|
||||
{
|
||||
hail = null;
|
||||
|
||||
// create temporary incoming message
|
||||
NetIncomingMessage msg = m_peer.SetupReadHelperMessage(ptr, payloadLength);
|
||||
try
|
||||
{
|
||||
string remoteAppIdentifier = msg.ReadString();
|
||||
long remoteUniqueIdentifier = msg.ReadInt64();
|
||||
InitializeRemoteTimeOffset(msg.ReadSingle());
|
||||
|
||||
int remainingBytes = payloadLength - (msg.PositionInBytes - ptr);
|
||||
if (remainingBytes > 0)
|
||||
hail = msg.ReadBytes(remainingBytes);
|
||||
|
||||
if (remoteAppIdentifier != m_peer.m_configuration.AppIdentifier)
|
||||
{
|
||||
// wrong app identifier
|
||||
ExecuteDisconnect("Wrong application identifier!", true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_remoteUniqueIdentifier = remoteUniqueIdentifier;
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
// whatever; we failed
|
||||
ExecuteDisconnect("Handshake data validation failed", true);
|
||||
m_peer.LogWarning("ReadRemoteHandshakeData failed: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the remote peer
|
||||
/// </summary>
|
||||
/// <param name="byeMessage">the message to send with the disconnect message</param>
|
||||
public void Disconnect(string byeMessage)
|
||||
{
|
||||
// user or library thread
|
||||
if (m_status == NetConnectionStatus.None || m_status == NetConnectionStatus.Disconnected)
|
||||
return;
|
||||
|
||||
m_peer.LogVerbose("Disconnect requested for " + this);
|
||||
m_disconnectMessage = byeMessage;
|
||||
|
||||
if (m_status != NetConnectionStatus.Disconnected && m_status != NetConnectionStatus.None)
|
||||
SetStatus(NetConnectionStatus.Disconnecting, byeMessage);
|
||||
|
||||
m_handshakeAttempts = 0;
|
||||
m_disconnectRequested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,147 +1,147 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
private float m_sentPingTime;
|
||||
private int m_sentPingNumber;
|
||||
private float m_averageRoundtripTime;
|
||||
private float m_timeoutDeadline = float.MaxValue;
|
||||
|
||||
// local time value + m_remoteTimeOffset = remote time value
|
||||
internal double m_remoteTimeOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current average roundtrip time in seconds
|
||||
/// </summary>
|
||||
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } }
|
||||
|
||||
/// <summary>
|
||||
/// Time offset between this peer and the remote peer
|
||||
/// </summary>
|
||||
public float RemoteTimeOffset { get { return (float)m_remoteTimeOffset; } }
|
||||
|
||||
// this might happen more than once
|
||||
internal void InitializeRemoteTimeOffset(float remoteSendTime)
|
||||
{
|
||||
m_remoteTimeOffset = (remoteSendTime + (m_averageRoundtripTime / 2.0)) - NetTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets local time value comparable to NetTime.Now from a remote value
|
||||
/// </summary>
|
||||
public double GetLocalTime(double remoteTimestamp)
|
||||
{
|
||||
return remoteTimestamp - m_remoteTimeOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remote time value for a local time value produced by NetTime.Now
|
||||
/// </summary>
|
||||
public double GetRemoteTime(double localTimestamp)
|
||||
{
|
||||
return localTimestamp + m_remoteTimeOffset;
|
||||
}
|
||||
|
||||
internal void InitializePing()
|
||||
{
|
||||
float now = (float)NetTime.Now;
|
||||
|
||||
// randomize ping sent time (0.25 - 1.0 x ping interval)
|
||||
m_sentPingTime = now;
|
||||
m_sentPingTime -= (m_peerConfiguration.PingInterval * 0.25f); // delay ping for a little while
|
||||
m_sentPingTime -= (NetRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
|
||||
m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0f); // initially allow a little more time
|
||||
|
||||
// make it better, quick :-)
|
||||
SendPing();
|
||||
}
|
||||
|
||||
internal void SendPing()
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
m_sentPingNumber++;
|
||||
|
||||
m_sentPingTime = (float)NetTime.Now;
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(1);
|
||||
om.Write((byte)m_sentPingNumber); // truncating to 0-255
|
||||
om.m_messageType = NetMessageType.Ping;
|
||||
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
internal void SendPong(int pingNumber)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(5);
|
||||
om.Write((byte)pingNumber);
|
||||
om.Write((float)NetTime.Now); // we should update this value to reflect the exact point in time the packet is SENT
|
||||
om.m_messageType = NetMessageType.Pong;
|
||||
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
|
||||
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
internal void ReceivedPong(float now, int pongNumber, float remoteSendTime)
|
||||
{
|
||||
if ((byte)pongNumber != (byte)m_sentPingNumber)
|
||||
{
|
||||
m_peer.LogVerbose("Ping/Pong mismatch; dropped message?");
|
||||
return;
|
||||
}
|
||||
|
||||
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
|
||||
|
||||
float rtt = now - m_sentPingTime;
|
||||
NetException.Assert(rtt >= 0);
|
||||
|
||||
double diff = (remoteSendTime + (rtt / 2.0)) - now;
|
||||
|
||||
if (m_averageRoundtripTime < 0)
|
||||
{
|
||||
m_remoteTimeOffset = diff;
|
||||
m_averageRoundtripTime = rtt;
|
||||
m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + " Remote time is: " + (now + diff));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f);
|
||||
|
||||
m_remoteTimeOffset = ((m_remoteTimeOffset * (double)(m_sentPingNumber - 1)) + diff) / (double)m_sentPingNumber;
|
||||
m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + ", remote time to " + (now + m_remoteTimeOffset) + " (ie. diff " + m_remoteTimeOffset + ")");
|
||||
}
|
||||
|
||||
// update resend delay for all channels
|
||||
float resendDelay = GetResendDelay();
|
||||
foreach (var chan in m_sendChannels)
|
||||
{
|
||||
var rchan = chan as NetReliableSenderChannel;
|
||||
if (rchan != null)
|
||||
rchan.m_resendDelay = resendDelay;
|
||||
}
|
||||
|
||||
// m_peer.LogVerbose("Timeout deadline pushed to " + m_timeoutDeadline);
|
||||
|
||||
// notify the application that average rtt changed
|
||||
if (m_peer.m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionLatencyUpdated))
|
||||
{
|
||||
NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4);
|
||||
update.m_senderConnection = this;
|
||||
update.m_senderEndPoint = this.m_remoteEndPoint;
|
||||
update.Write(rtt);
|
||||
m_peer.ReleaseMessage(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
private float m_sentPingTime;
|
||||
private int m_sentPingNumber;
|
||||
private float m_averageRoundtripTime;
|
||||
private float m_timeoutDeadline = float.MaxValue;
|
||||
|
||||
// local time value + m_remoteTimeOffset = remote time value
|
||||
internal double m_remoteTimeOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current average roundtrip time in seconds
|
||||
/// </summary>
|
||||
public float AverageRoundtripTime { get { return m_averageRoundtripTime; } }
|
||||
|
||||
/// <summary>
|
||||
/// Time offset between this peer and the remote peer
|
||||
/// </summary>
|
||||
public float RemoteTimeOffset { get { return (float)m_remoteTimeOffset; } }
|
||||
|
||||
// this might happen more than once
|
||||
internal void InitializeRemoteTimeOffset(float remoteSendTime)
|
||||
{
|
||||
m_remoteTimeOffset = (remoteSendTime + (m_averageRoundtripTime / 2.0)) - NetTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets local time value comparable to NetTime.Now from a remote value
|
||||
/// </summary>
|
||||
public double GetLocalTime(double remoteTimestamp)
|
||||
{
|
||||
return remoteTimestamp - m_remoteTimeOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the remote time value for a local time value produced by NetTime.Now
|
||||
/// </summary>
|
||||
public double GetRemoteTime(double localTimestamp)
|
||||
{
|
||||
return localTimestamp + m_remoteTimeOffset;
|
||||
}
|
||||
|
||||
internal void InitializePing()
|
||||
{
|
||||
float now = (float)NetTime.Now;
|
||||
|
||||
// randomize ping sent time (0.25 - 1.0 x ping interval)
|
||||
m_sentPingTime = now;
|
||||
m_sentPingTime -= (m_peerConfiguration.PingInterval * 0.25f); // delay ping for a little while
|
||||
m_sentPingTime -= (NetRandom.Instance.NextSingle() * (m_peerConfiguration.PingInterval * 0.75f));
|
||||
m_timeoutDeadline = now + (m_peerConfiguration.m_connectionTimeout * 2.0f); // initially allow a little more time
|
||||
|
||||
// make it better, quick :-)
|
||||
SendPing();
|
||||
}
|
||||
|
||||
internal void SendPing()
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
m_sentPingNumber++;
|
||||
|
||||
m_sentPingTime = (float)NetTime.Now;
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(1);
|
||||
om.Write((byte)m_sentPingNumber); // truncating to 0-255
|
||||
om.m_messageType = NetMessageType.Ping;
|
||||
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
internal void SendPong(int pingNumber)
|
||||
{
|
||||
m_peer.VerifyNetworkThread();
|
||||
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(5);
|
||||
om.Write((byte)pingNumber);
|
||||
om.Write((float)NetTime.Now); // we should update this value to reflect the exact point in time the packet is SENT
|
||||
om.m_messageType = NetMessageType.Pong;
|
||||
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
|
||||
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
internal void ReceivedPong(float now, int pongNumber, float remoteSendTime)
|
||||
{
|
||||
if ((byte)pongNumber != (byte)m_sentPingNumber)
|
||||
{
|
||||
m_peer.LogVerbose("Ping/Pong mismatch; dropped message?");
|
||||
return;
|
||||
}
|
||||
|
||||
m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout;
|
||||
|
||||
float rtt = now - m_sentPingTime;
|
||||
NetException.Assert(rtt >= 0);
|
||||
|
||||
double diff = (remoteSendTime + (rtt / 2.0)) - now;
|
||||
|
||||
if (m_averageRoundtripTime < 0)
|
||||
{
|
||||
m_remoteTimeOffset = diff;
|
||||
m_averageRoundtripTime = rtt;
|
||||
m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + " Remote time is: " + (now + diff));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f);
|
||||
|
||||
m_remoteTimeOffset = ((m_remoteTimeOffset * (double)(m_sentPingNumber - 1)) + diff) / (double)m_sentPingNumber;
|
||||
m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime) + ", remote time to " + (now + m_remoteTimeOffset) + " (ie. diff " + m_remoteTimeOffset + ")");
|
||||
}
|
||||
|
||||
// update resend delay for all channels
|
||||
float resendDelay = GetResendDelay();
|
||||
foreach (var chan in m_sendChannels)
|
||||
{
|
||||
var rchan = chan as NetReliableSenderChannel;
|
||||
if (rchan != null)
|
||||
rchan.m_resendDelay = resendDelay;
|
||||
}
|
||||
|
||||
// m_peer.LogVerbose("Timeout deadline pushed to " + m_timeoutDeadline);
|
||||
|
||||
// notify the application that average rtt changed
|
||||
if (m_peer.m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.ConnectionLatencyUpdated))
|
||||
{
|
||||
NetIncomingMessage update = m_peer.CreateIncomingMessage(NetIncomingMessageType.ConnectionLatencyUpdated, 4);
|
||||
update.m_senderConnection = this;
|
||||
update.m_senderEndPoint = this.m_remoteEndPoint;
|
||||
update.Write(rtt);
|
||||
m_peer.ReleaseMessage(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,175 +1,175 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
private enum ExpandMTUStatus
|
||||
{
|
||||
None,
|
||||
InProgress,
|
||||
Finished
|
||||
}
|
||||
|
||||
private const int c_protocolMaxMTU = (int)((((float)ushort.MaxValue / 8.0f) - 1.0f));
|
||||
|
||||
private ExpandMTUStatus m_expandMTUStatus;
|
||||
|
||||
private int m_largestSuccessfulMTU;
|
||||
private int m_smallestFailedMTU;
|
||||
|
||||
private int m_lastSentMTUAttemptSize;
|
||||
private double m_lastSentMTUAttemptTime;
|
||||
private int m_mtuAttemptFails;
|
||||
|
||||
internal int m_currentMTU;
|
||||
|
||||
internal void InitExpandMTU(double now)
|
||||
{
|
||||
m_lastSentMTUAttemptTime = now + m_peerConfiguration.m_expandMTUFrequency + 1.5f + m_averageRoundtripTime; // wait a tiny bit before starting to expand mtu
|
||||
m_largestSuccessfulMTU = 512;
|
||||
m_smallestFailedMTU = -1;
|
||||
m_currentMTU = m_peerConfiguration.MaximumTransmissionUnit;
|
||||
}
|
||||
|
||||
private void MTUExpansionHeartbeat(double now)
|
||||
{
|
||||
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
|
||||
return;
|
||||
|
||||
if (m_expandMTUStatus == ExpandMTUStatus.None)
|
||||
{
|
||||
if (m_peerConfiguration.m_autoExpandMTU == false)
|
||||
{
|
||||
FinalizeMTU(m_currentMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
// begin expansion
|
||||
ExpandMTU(now, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (now > m_lastSentMTUAttemptTime + m_peerConfiguration.ExpandMTUFrequency)
|
||||
{
|
||||
m_mtuAttemptFails++;
|
||||
if (m_mtuAttemptFails == 3)
|
||||
{
|
||||
FinalizeMTU(m_currentMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
// timed out; ie. failed
|
||||
m_smallestFailedMTU = m_lastSentMTUAttemptSize;
|
||||
ExpandMTU(now, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpandMTU(double now, bool succeeded)
|
||||
{
|
||||
int tryMTU;
|
||||
|
||||
// we've nevered encountered failure
|
||||
if (m_smallestFailedMTU == -1)
|
||||
{
|
||||
// we've never encountered failure; expand by 25% each time
|
||||
tryMTU = (int)((float)m_currentMTU * 1.25f);
|
||||
//m_peer.LogDebug("Trying MTU " + tryMTU);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we HAVE encountered failure; so try in between
|
||||
tryMTU = (int)(((float)m_smallestFailedMTU + (float)m_largestSuccessfulMTU) / 2.0f);
|
||||
//m_peer.LogDebug("Trying MTU " + m_smallestFailedMTU + " <-> " + m_largestSuccessfulMTU + " = " + tryMTU);
|
||||
}
|
||||
|
||||
if (tryMTU > c_protocolMaxMTU)
|
||||
tryMTU = c_protocolMaxMTU;
|
||||
|
||||
if (tryMTU == m_largestSuccessfulMTU)
|
||||
{
|
||||
//m_peer.LogDebug("Found optimal MTU - exiting");
|
||||
FinalizeMTU(m_largestSuccessfulMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
SendExpandMTU(now, tryMTU);
|
||||
}
|
||||
|
||||
private void SendExpandMTU(double now, int size)
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(size);
|
||||
byte[] tmp = new byte[size];
|
||||
om.Write(tmp);
|
||||
om.m_messageType = NetMessageType.ExpandMTURequest;
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
|
||||
bool ok = m_peer.SendMTUPacket(len, m_remoteEndPoint);
|
||||
if (ok == false)
|
||||
{
|
||||
//m_peer.LogDebug("Send MTU failed for size " + size);
|
||||
|
||||
// failure
|
||||
if (m_smallestFailedMTU == -1 || size < m_smallestFailedMTU)
|
||||
{
|
||||
m_smallestFailedMTU = size;
|
||||
m_mtuAttemptFails++;
|
||||
if (m_mtuAttemptFails >= m_peerConfiguration.ExpandMTUFailAttempts)
|
||||
{
|
||||
FinalizeMTU(m_largestSuccessfulMTU);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ExpandMTU(now, false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastSentMTUAttemptSize = size;
|
||||
m_lastSentMTUAttemptTime = now;
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
private void FinalizeMTU(int size)
|
||||
{
|
||||
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
|
||||
return;
|
||||
m_expandMTUStatus = ExpandMTUStatus.Finished;
|
||||
m_currentMTU = size;
|
||||
if (m_currentMTU != m_peerConfiguration.m_maximumTransmissionUnit)
|
||||
m_peer.LogDebug("Expanded Maximum Transmission Unit to: " + m_currentMTU + " bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
private void SendMTUSuccess(int size)
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(4);
|
||||
om.Write(size);
|
||||
om.m_messageType = NetMessageType.ExpandMTUSuccess;
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
|
||||
|
||||
// m_peer.LogDebug("Received MTU expand request for " + size + " bytes");
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
private void HandleExpandMTUSuccess(double now, int size)
|
||||
{
|
||||
if (size > m_largestSuccessfulMTU)
|
||||
m_largestSuccessfulMTU = size;
|
||||
|
||||
if (size < m_currentMTU)
|
||||
{
|
||||
//m_peer.LogDebug("Received low MTU expand success (size " + size + "); current mtu is " + m_currentMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
//m_peer.LogDebug("Expanding MTU to " + size);
|
||||
m_currentMTU = size;
|
||||
|
||||
ExpandMTU(now, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetConnection
|
||||
{
|
||||
private enum ExpandMTUStatus
|
||||
{
|
||||
None,
|
||||
InProgress,
|
||||
Finished
|
||||
}
|
||||
|
||||
private const int c_protocolMaxMTU = (int)((((float)ushort.MaxValue / 8.0f) - 1.0f));
|
||||
|
||||
private ExpandMTUStatus m_expandMTUStatus;
|
||||
|
||||
private int m_largestSuccessfulMTU;
|
||||
private int m_smallestFailedMTU;
|
||||
|
||||
private int m_lastSentMTUAttemptSize;
|
||||
private double m_lastSentMTUAttemptTime;
|
||||
private int m_mtuAttemptFails;
|
||||
|
||||
internal int m_currentMTU;
|
||||
|
||||
internal void InitExpandMTU(double now)
|
||||
{
|
||||
m_lastSentMTUAttemptTime = now + m_peerConfiguration.m_expandMTUFrequency + 1.5f + m_averageRoundtripTime; // wait a tiny bit before starting to expand mtu
|
||||
m_largestSuccessfulMTU = 512;
|
||||
m_smallestFailedMTU = -1;
|
||||
m_currentMTU = m_peerConfiguration.MaximumTransmissionUnit;
|
||||
}
|
||||
|
||||
private void MTUExpansionHeartbeat(double now)
|
||||
{
|
||||
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
|
||||
return;
|
||||
|
||||
if (m_expandMTUStatus == ExpandMTUStatus.None)
|
||||
{
|
||||
if (m_peerConfiguration.m_autoExpandMTU == false)
|
||||
{
|
||||
FinalizeMTU(m_currentMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
// begin expansion
|
||||
ExpandMTU(now, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (now > m_lastSentMTUAttemptTime + m_peerConfiguration.ExpandMTUFrequency)
|
||||
{
|
||||
m_mtuAttemptFails++;
|
||||
if (m_mtuAttemptFails == 3)
|
||||
{
|
||||
FinalizeMTU(m_currentMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
// timed out; ie. failed
|
||||
m_smallestFailedMTU = m_lastSentMTUAttemptSize;
|
||||
ExpandMTU(now, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void ExpandMTU(double now, bool succeeded)
|
||||
{
|
||||
int tryMTU;
|
||||
|
||||
// we've nevered encountered failure
|
||||
if (m_smallestFailedMTU == -1)
|
||||
{
|
||||
// we've never encountered failure; expand by 25% each time
|
||||
tryMTU = (int)((float)m_currentMTU * 1.25f);
|
||||
//m_peer.LogDebug("Trying MTU " + tryMTU);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we HAVE encountered failure; so try in between
|
||||
tryMTU = (int)(((float)m_smallestFailedMTU + (float)m_largestSuccessfulMTU) / 2.0f);
|
||||
//m_peer.LogDebug("Trying MTU " + m_smallestFailedMTU + " <-> " + m_largestSuccessfulMTU + " = " + tryMTU);
|
||||
}
|
||||
|
||||
if (tryMTU > c_protocolMaxMTU)
|
||||
tryMTU = c_protocolMaxMTU;
|
||||
|
||||
if (tryMTU == m_largestSuccessfulMTU)
|
||||
{
|
||||
//m_peer.LogDebug("Found optimal MTU - exiting");
|
||||
FinalizeMTU(m_largestSuccessfulMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
SendExpandMTU(now, tryMTU);
|
||||
}
|
||||
|
||||
private void SendExpandMTU(double now, int size)
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(size);
|
||||
byte[] tmp = new byte[size];
|
||||
om.Write(tmp);
|
||||
om.m_messageType = NetMessageType.ExpandMTURequest;
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
|
||||
bool ok = m_peer.SendMTUPacket(len, m_remoteEndPoint);
|
||||
if (ok == false)
|
||||
{
|
||||
//m_peer.LogDebug("Send MTU failed for size " + size);
|
||||
|
||||
// failure
|
||||
if (m_smallestFailedMTU == -1 || size < m_smallestFailedMTU)
|
||||
{
|
||||
m_smallestFailedMTU = size;
|
||||
m_mtuAttemptFails++;
|
||||
if (m_mtuAttemptFails >= m_peerConfiguration.ExpandMTUFailAttempts)
|
||||
{
|
||||
FinalizeMTU(m_largestSuccessfulMTU);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ExpandMTU(now, false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_lastSentMTUAttemptSize = size;
|
||||
m_lastSentMTUAttemptTime = now;
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
private void FinalizeMTU(int size)
|
||||
{
|
||||
if (m_expandMTUStatus == ExpandMTUStatus.Finished)
|
||||
return;
|
||||
m_expandMTUStatus = ExpandMTUStatus.Finished;
|
||||
m_currentMTU = size;
|
||||
if (m_currentMTU != m_peerConfiguration.m_maximumTransmissionUnit)
|
||||
m_peer.LogDebug("Expanded Maximum Transmission Unit to: " + m_currentMTU + " bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
private void SendMTUSuccess(int size)
|
||||
{
|
||||
NetOutgoingMessage om = m_peer.CreateMessage(4);
|
||||
om.Write(size);
|
||||
om.m_messageType = NetMessageType.ExpandMTUSuccess;
|
||||
int len = om.Encode(m_peer.m_sendBuffer, 0, 0);
|
||||
bool connectionReset;
|
||||
m_peer.SendPacket(len, m_remoteEndPoint, 1, out connectionReset);
|
||||
|
||||
// m_peer.LogDebug("Received MTU expand request for " + size + " bytes");
|
||||
|
||||
m_statistics.PacketSent(len, 1);
|
||||
}
|
||||
|
||||
private void HandleExpandMTUSuccess(double now, int size)
|
||||
{
|
||||
if (size > m_largestSuccessfulMTU)
|
||||
m_largestSuccessfulMTU = size;
|
||||
|
||||
if (size < m_currentMTU)
|
||||
{
|
||||
//m_peer.LogDebug("Received low MTU expand success (size " + size + "); current mtu is " + m_currentMTU);
|
||||
return;
|
||||
}
|
||||
|
||||
//m_peer.LogDebug("Expanding MTU to " + size);
|
||||
m_currentMTU = size;
|
||||
|
||||
ExpandMTU(now, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,204 +1,204 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
// Uncomment the line below to get statistics in RELEASE builds
|
||||
#define USE_RELEASE_STATISTICS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal enum MessageResendReason
|
||||
{
|
||||
Delay,
|
||||
HoleInSequence
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Statistics for a NetConnection instance
|
||||
/// </summary>
|
||||
public sealed class NetConnectionStatistics
|
||||
{
|
||||
private readonly 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 int m_resentMessagesDueToDelay;
|
||||
internal int m_resentMessagesDueToHole;
|
||||
|
||||
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; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of resent reliable messages for this connection
|
||||
/// </summary>
|
||||
public int ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } }
|
||||
|
||||
// public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void MessageResent(MessageResendReason reason)
|
||||
{
|
||||
if (reason == MessageResendReason.Delay)
|
||||
m_resentMessagesDueToDelay++;
|
||||
else
|
||||
m_resentMessagesDueToHole++;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void MessageResent(MessageResendReason reason)
|
||||
{
|
||||
if (reason == MessageResendReason.Delay)
|
||||
m_resentMessagesDueToDelay++;
|
||||
else
|
||||
m_resentMessagesDueToHole++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
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");
|
||||
|
||||
if (m_resentMessagesDueToDelay > 0)
|
||||
bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay);
|
||||
if (m_resentMessagesDueToDelay > 0)
|
||||
bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole);
|
||||
|
||||
int numUnsent = 0;
|
||||
int numStored = 0;
|
||||
foreach (NetSenderChannelBase sendChan in m_connection.m_sendChannels)
|
||||
{
|
||||
if (sendChan == null)
|
||||
continue;
|
||||
numUnsent += sendChan.m_queuedSends.Count;
|
||||
|
||||
var relSendChan = sendChan as NetReliableSenderChannel;
|
||||
if (relSendChan != null)
|
||||
{
|
||||
for (int i = 0; i < relSendChan.m_storedMessages.Length; i++)
|
||||
if (relSendChan.m_storedMessages[i].Message != null)
|
||||
numStored++;
|
||||
}
|
||||
}
|
||||
|
||||
int numWithheld = 0;
|
||||
foreach (NetReceiverChannelBase recChan in m_connection.m_receiveChannels)
|
||||
{
|
||||
var relRecChan = recChan as NetReliableOrderedReceiver;
|
||||
if (relRecChan != null)
|
||||
{
|
||||
for (int i = 0; i < relRecChan.m_withheldMessages.Length; i++)
|
||||
if (relRecChan.m_withheldMessages[i] != null)
|
||||
numWithheld++;
|
||||
}
|
||||
}
|
||||
|
||||
bdr.AppendLine("Unsent messages: " + numUnsent);
|
||||
bdr.AppendLine("Stored messages: " + numStored);
|
||||
bdr.AppendLine("Withheld messages: " + numWithheld);
|
||||
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
// Uncomment the line below to get statistics in RELEASE builds
|
||||
#define USE_RELEASE_STATISTICS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal enum MessageResendReason
|
||||
{
|
||||
Delay,
|
||||
HoleInSequence
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Statistics for a NetConnection instance
|
||||
/// </summary>
|
||||
public sealed class NetConnectionStatistics
|
||||
{
|
||||
private readonly 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 int m_resentMessagesDueToDelay;
|
||||
internal int m_resentMessagesDueToHole;
|
||||
|
||||
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; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of resent reliable messages for this connection
|
||||
/// </summary>
|
||||
public int ResentMessages { get { return m_resentMessagesDueToHole + m_resentMessagesDueToDelay; } }
|
||||
|
||||
// public double LastSendRespondedTo { get { return m_connection.m_lastSendRespondedTo; } }
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
NetException.Assert(numBytes > 0 && numMessages > 0);
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void MessageResent(MessageResendReason reason)
|
||||
{
|
||||
if (reason == MessageResendReason.Delay)
|
||||
m_resentMessagesDueToDelay++;
|
||||
else
|
||||
m_resentMessagesDueToHole++;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void MessageResent(MessageResendReason reason)
|
||||
{
|
||||
if (reason == MessageResendReason.Delay)
|
||||
m_resentMessagesDueToDelay++;
|
||||
else
|
||||
m_resentMessagesDueToHole++;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
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");
|
||||
|
||||
if (m_resentMessagesDueToDelay > 0)
|
||||
bdr.AppendLine("Resent messages (delay): " + m_resentMessagesDueToDelay);
|
||||
if (m_resentMessagesDueToDelay > 0)
|
||||
bdr.AppendLine("Resent messages (holes): " + m_resentMessagesDueToHole);
|
||||
|
||||
int numUnsent = 0;
|
||||
int numStored = 0;
|
||||
foreach (NetSenderChannelBase sendChan in m_connection.m_sendChannels)
|
||||
{
|
||||
if (sendChan == null)
|
||||
continue;
|
||||
numUnsent += sendChan.m_queuedSends.Count;
|
||||
|
||||
var relSendChan = sendChan as NetReliableSenderChannel;
|
||||
if (relSendChan != null)
|
||||
{
|
||||
for (int i = 0; i < relSendChan.m_storedMessages.Length; i++)
|
||||
if (relSendChan.m_storedMessages[i].Message != null)
|
||||
numStored++;
|
||||
}
|
||||
}
|
||||
|
||||
int numWithheld = 0;
|
||||
foreach (NetReceiverChannelBase recChan in m_connection.m_receiveChannels)
|
||||
{
|
||||
var relRecChan = recChan as NetReliableOrderedReceiver;
|
||||
if (relRecChan != null)
|
||||
{
|
||||
for (int i = 0; i < relRecChan.m_withheldMessages.Length; i++)
|
||||
if (relRecChan.m_withheldMessages[i] != null)
|
||||
numWithheld++;
|
||||
}
|
||||
}
|
||||
|
||||
bdr.AppendLine("Unsent messages: " + numUnsent);
|
||||
bdr.AppendLine("Stored messages: " + numStored);
|
||||
bdr.AppendLine("Withheld messages: " + numWithheld);
|
||||
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +1,68 @@
|
||||
/* 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 for a NetConnection instance
|
||||
/// </summary>
|
||||
public enum NetConnectionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// No connection, or attempt, in place
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Connect has been sent; waiting for ConnectResponse
|
||||
/// </summary>
|
||||
InitiatedConnect,
|
||||
|
||||
/// <summary>
|
||||
/// Connect was received, but ConnectResponse hasn't been sent yet
|
||||
/// </summary>
|
||||
ReceivedInitiation,
|
||||
|
||||
/// <summary>
|
||||
/// Connect was received and ApprovalMessage released to the application; awaiting Approve() or Deny()
|
||||
/// </summary>
|
||||
RespondedAwaitingApproval, // We got Connect, released ApprovalMessage
|
||||
|
||||
/// <summary>
|
||||
/// Connect was received and ConnectResponse has been sent; waiting for ConnectionEstablished
|
||||
/// </summary>
|
||||
RespondedConnect, // we got Connect, sent ConnectResponse
|
||||
|
||||
/// <summary>
|
||||
/// Connected
|
||||
/// </summary>
|
||||
Connected, // we received ConnectResponse (if initiator) or ConnectionEstablished (if passive)
|
||||
|
||||
/// <summary>
|
||||
/// In the process of disconnecting
|
||||
/// </summary>
|
||||
Disconnecting,
|
||||
|
||||
/// <summary>
|
||||
/// Disconnected
|
||||
/// </summary>
|
||||
Disconnected
|
||||
}
|
||||
}
|
||||
/* 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 for a NetConnection instance
|
||||
/// </summary>
|
||||
public enum NetConnectionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// No connection, or attempt, in place
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Connect has been sent; waiting for ConnectResponse
|
||||
/// </summary>
|
||||
InitiatedConnect,
|
||||
|
||||
/// <summary>
|
||||
/// Connect was received, but ConnectResponse hasn't been sent yet
|
||||
/// </summary>
|
||||
ReceivedInitiation,
|
||||
|
||||
/// <summary>
|
||||
/// Connect was received and ApprovalMessage released to the application; awaiting Approve() or Deny()
|
||||
/// </summary>
|
||||
RespondedAwaitingApproval, // We got Connect, released ApprovalMessage
|
||||
|
||||
/// <summary>
|
||||
/// Connect was received and ConnectResponse has been sent; waiting for ConnectionEstablished
|
||||
/// </summary>
|
||||
RespondedConnect, // we got Connect, sent ConnectResponse
|
||||
|
||||
/// <summary>
|
||||
/// Connected
|
||||
/// </summary>
|
||||
Connected, // we received ConnectResponse (if initiator) or ConnectionEstablished (if passive)
|
||||
|
||||
/// <summary>
|
||||
/// In the process of disconnecting
|
||||
/// </summary>
|
||||
Disconnecting,
|
||||
|
||||
/// <summary>
|
||||
/// Disconnected
|
||||
/// </summary>
|
||||
Disconnected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
/* 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>
|
||||
/// All the constants used when compiling the library
|
||||
/// </summary>
|
||||
internal static class NetConstants
|
||||
{
|
||||
internal const int NumTotalChannels = 99;
|
||||
|
||||
internal const int NetChannelsPerDeliveryMethod = 32;
|
||||
|
||||
internal const int NumSequenceNumbers = 1024;
|
||||
|
||||
internal const int HeaderByteSize = 5;
|
||||
|
||||
internal const int UnreliableWindowSize = 128;
|
||||
internal const int ReliableOrderedWindowSize = 64;
|
||||
internal const int ReliableSequencedWindowSize = 64;
|
||||
internal const int DefaultWindowSize = 64;
|
||||
|
||||
internal const int MaxFragmentationGroups = ushort.MaxValue - 1;
|
||||
|
||||
internal const int UnfragmentedMessageHeaderSize = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Number of channels which needs a sequence number to work
|
||||
/// </summary>
|
||||
internal const int NumSequencedChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced1;
|
||||
|
||||
/// <summary>
|
||||
/// Number of reliable channels
|
||||
/// </summary>
|
||||
internal const int NumReliableChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserReliableUnordered;
|
||||
|
||||
internal const string ConnResetMessage = "Connection was reset by remote host";
|
||||
}
|
||||
}
|
||||
/* 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>
|
||||
/// All the constants used when compiling the library
|
||||
/// </summary>
|
||||
internal static class NetConstants
|
||||
{
|
||||
internal const int NumTotalChannels = 99;
|
||||
|
||||
internal const int NetChannelsPerDeliveryMethod = 32;
|
||||
|
||||
internal const int NumSequenceNumbers = 1024;
|
||||
|
||||
internal const int HeaderByteSize = 5;
|
||||
|
||||
internal const int UnreliableWindowSize = 128;
|
||||
internal const int ReliableOrderedWindowSize = 64;
|
||||
internal const int ReliableSequencedWindowSize = 64;
|
||||
internal const int DefaultWindowSize = 64;
|
||||
|
||||
internal const int MaxFragmentationGroups = ushort.MaxValue - 1;
|
||||
|
||||
internal const int UnfragmentedMessageHeaderSize = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Number of channels which needs a sequence number to work
|
||||
/// </summary>
|
||||
internal const int NumSequencedChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserSequenced1;
|
||||
|
||||
/// <summary>
|
||||
/// Number of reliable channels
|
||||
/// </summary>
|
||||
internal const int NumReliableChannels = ((int)NetMessageType.UserReliableOrdered1 + NetConstants.NetChannelsPerDeliveryMethod) - (int)NetMessageType.UserReliableUnordered;
|
||||
|
||||
internal const string ConnResetMessage = "Connection was reset by remote host";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// How the library deals with resends and handling of late messages
|
||||
/// </summary>
|
||||
public enum NetDeliveryMethod : byte
|
||||
{
|
||||
//
|
||||
// Actually a publicly visible subset of NetMessageType
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Indicates an error
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Unreliable, unordered delivery
|
||||
/// </summary>
|
||||
Unreliable = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unreliable delivery, but automatically dropping late messages
|
||||
/// </summary>
|
||||
UnreliableSequenced = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Reliable delivery, but unordered
|
||||
/// </summary>
|
||||
ReliableUnordered = 34,
|
||||
|
||||
/// <summary>
|
||||
/// Reliable delivery, except for late messages which are dropped
|
||||
/// </summary>
|
||||
ReliableSequenced = 35,
|
||||
|
||||
/// <summary>
|
||||
/// Reliable, ordered delivery
|
||||
/// </summary>
|
||||
ReliableOrdered = 67,
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// How the library deals with resends and handling of late messages
|
||||
/// </summary>
|
||||
public enum NetDeliveryMethod : byte
|
||||
{
|
||||
//
|
||||
// Actually a publicly visible subset of NetMessageType
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Indicates an error
|
||||
/// </summary>
|
||||
Unknown = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Unreliable, unordered delivery
|
||||
/// </summary>
|
||||
Unreliable = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Unreliable delivery, but automatically dropping late messages
|
||||
/// </summary>
|
||||
UnreliableSequenced = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Reliable delivery, but unordered
|
||||
/// </summary>
|
||||
ReliableUnordered = 34,
|
||||
|
||||
/// <summary>
|
||||
/// Reliable delivery, except for late messages which are dropped
|
||||
/// </summary>
|
||||
ReliableSequenced = 35,
|
||||
|
||||
/// <summary>
|
||||
/// Reliable, ordered delivery
|
||||
/// </summary>
|
||||
ReliableOrdered = 67,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +1,83 @@
|
||||
/* 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.Runtime.Serialization;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown in the Lidgren Network Library
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class NetException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
public NetException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
public NetException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
public NetException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Runtime.Serialization;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception thrown in the Lidgren Network Library
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class NetException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
public NetException()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
public NetException(string message)
|
||||
: base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
public NetException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// NetException constructor
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,174 +1,174 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal static class NetFragmentationHelper
|
||||
{
|
||||
internal static int WriteHeader(
|
||||
byte[] destination,
|
||||
int ptr,
|
||||
int group,
|
||||
int totalBits,
|
||||
int chunkByteSize,
|
||||
int chunkNumber)
|
||||
{
|
||||
uint num1 = (uint)group;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num1 | 0x80);
|
||||
num1 = num1 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num1;
|
||||
|
||||
// write variable length fragment total bits
|
||||
uint num2 = (uint)totalBits;
|
||||
while (num2 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num2 | 0x80);
|
||||
num2 = num2 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num2;
|
||||
|
||||
// write variable length fragment chunk size
|
||||
uint num3 = (uint)chunkByteSize;
|
||||
while (num3 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num3 | 0x80);
|
||||
num3 = num3 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num3;
|
||||
|
||||
// write variable length fragment chunk number
|
||||
uint num4 = (uint)chunkNumber;
|
||||
while (num4 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num4 | 0x80);
|
||||
num4 = num4 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num4;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal static int ReadHeader(byte[] buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber)
|
||||
{
|
||||
int num1 = 0;
|
||||
int num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
group = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
totalBits = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
chunkByteSize = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
chunkNumber = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal static int GetFragmentationHeaderSize(int groupId, int totalBytes, int chunkByteSize, int numChunks)
|
||||
{
|
||||
int len = 4;
|
||||
|
||||
// write variable length fragment group id
|
||||
uint num1 = (uint)groupId;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num1 = num1 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment total bits
|
||||
uint num2 = (uint)(totalBytes * 8);
|
||||
while (num2 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num2 = num2 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment chunk byte size
|
||||
uint num3 = (uint)chunkByteSize;
|
||||
while (num3 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num3 = num3 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment chunk number
|
||||
uint num4 = (uint)numChunks;
|
||||
while (num4 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num4 = num4 >> 7;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
internal static int GetBestChunkSize(int group, int totalBytes, int mtu)
|
||||
{
|
||||
int tryNumChunks = (totalBytes / (mtu - 8)) + 1;
|
||||
int tryChunkSize = (totalBytes / tryNumChunks) + 1; // +1 since we immediately decrement it in the loop
|
||||
|
||||
int headerSize = 0;
|
||||
do
|
||||
{
|
||||
tryChunkSize--; // keep reducing chunk size until it fits within MTU including header
|
||||
|
||||
int numChunks = totalBytes / tryChunkSize;
|
||||
if (numChunks * tryChunkSize < totalBytes)
|
||||
numChunks++;
|
||||
|
||||
headerSize = GetFragmentationHeaderSize(group, totalBytes, tryChunkSize, numChunks);
|
||||
|
||||
} while (tryChunkSize + headerSize + 5 + 1 >= mtu);
|
||||
|
||||
return tryChunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal static class NetFragmentationHelper
|
||||
{
|
||||
internal static int WriteHeader(
|
||||
byte[] destination,
|
||||
int ptr,
|
||||
int group,
|
||||
int totalBits,
|
||||
int chunkByteSize,
|
||||
int chunkNumber)
|
||||
{
|
||||
uint num1 = (uint)group;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num1 | 0x80);
|
||||
num1 = num1 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num1;
|
||||
|
||||
// write variable length fragment total bits
|
||||
uint num2 = (uint)totalBits;
|
||||
while (num2 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num2 | 0x80);
|
||||
num2 = num2 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num2;
|
||||
|
||||
// write variable length fragment chunk size
|
||||
uint num3 = (uint)chunkByteSize;
|
||||
while (num3 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num3 | 0x80);
|
||||
num3 = num3 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num3;
|
||||
|
||||
// write variable length fragment chunk number
|
||||
uint num4 = (uint)chunkNumber;
|
||||
while (num4 >= 0x80)
|
||||
{
|
||||
destination[ptr++] = (byte)(num4 | 0x80);
|
||||
num4 = num4 >> 7;
|
||||
}
|
||||
destination[ptr++] = (byte)num4;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal static int ReadHeader(byte[] buffer, int ptr, out int group, out int totalBits, out int chunkByteSize, out int chunkNumber)
|
||||
{
|
||||
int num1 = 0;
|
||||
int num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
group = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
totalBits = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
chunkByteSize = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
num1 = 0;
|
||||
num2 = 0;
|
||||
while (true)
|
||||
{
|
||||
byte num3 = buffer[ptr++];
|
||||
num1 |= (num3 & 0x7f) << (num2 & 0x1f);
|
||||
num2 += 7;
|
||||
if ((num3 & 0x80) == 0)
|
||||
{
|
||||
chunkNumber = num1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal static int GetFragmentationHeaderSize(int groupId, int totalBytes, int chunkByteSize, int numChunks)
|
||||
{
|
||||
int len = 4;
|
||||
|
||||
// write variable length fragment group id
|
||||
uint num1 = (uint)groupId;
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num1 = num1 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment total bits
|
||||
uint num2 = (uint)(totalBytes * 8);
|
||||
while (num2 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num2 = num2 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment chunk byte size
|
||||
uint num3 = (uint)chunkByteSize;
|
||||
while (num3 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num3 = num3 >> 7;
|
||||
}
|
||||
|
||||
// write variable length fragment chunk number
|
||||
uint num4 = (uint)numChunks;
|
||||
while (num4 >= 0x80)
|
||||
{
|
||||
len++;
|
||||
num4 = num4 >> 7;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
internal static int GetBestChunkSize(int group, int totalBytes, int mtu)
|
||||
{
|
||||
int tryNumChunks = (totalBytes / (mtu - 8)) + 1;
|
||||
int tryChunkSize = (totalBytes / tryNumChunks) + 1; // +1 since we immediately decrement it in the loop
|
||||
|
||||
int headerSize = 0;
|
||||
do
|
||||
{
|
||||
tryChunkSize--; // keep reducing chunk size until it fits within MTU including header
|
||||
|
||||
int numChunks = totalBytes / tryChunkSize;
|
||||
if (numChunks * tryChunkSize < totalBytes)
|
||||
numChunks++;
|
||||
|
||||
headerSize = GetFragmentationHeaderSize(group, totalBytes, tryChunkSize, numChunks);
|
||||
|
||||
} while (tryChunkSize + headerSize + 5 + 1 >= mtu);
|
||||
|
||||
return tryChunkSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public sealed class NetFragmentationInfo
|
||||
{
|
||||
public int TotalFragmentCount;
|
||||
public bool[] Received;
|
||||
public int TotalReceived;
|
||||
public int FragmentSize;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public sealed class NetFragmentationInfo
|
||||
{
|
||||
public int TotalFragmentCount;
|
||||
public bool[] Received;
|
||||
public int TotalReceived;
|
||||
public int FragmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,314 +1,314 @@
|
||||
/* 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the internal data buffer, don't modify
|
||||
/// </summary>
|
||||
public byte[] PeekDataBuffer()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 1-bit Boolean without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an SByte without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte(int numberOfBits)
|
||||
{
|
||||
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int16 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int16 PeekInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
|
||||
return (short)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt16 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int32 PeekInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
return (Int32)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int64 PeekInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
unchecked
|
||||
{
|
||||
ulong retval = PeekUInt64();
|
||||
long longRetval = (long)retval;
|
||||
return longRetval;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekFloat()
|
||||
{
|
||||
return PeekSingle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekSingle()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = PeekBytes(4);
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 64-bit Double without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a string without advancing the read pointer
|
||||
/// </summary>
|
||||
public string PeekString()
|
||||
{
|
||||
int wasReadPosition = m_readPosition;
|
||||
string retval = ReadString();
|
||||
m_readPosition = wasReadPosition;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the internal data buffer, don't modify
|
||||
/// </summary>
|
||||
public byte[] PeekDataBuffer()
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
//
|
||||
// 1 bit
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 1-bit Boolean without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 8, c_readOverflowError);
|
||||
byte retval = NetBitWriter.ReadByte(m_data, 8, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an SByte without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a Byte without advancing the read pointer
|
||||
/// </summary>
|
||||
public byte PeekByte(int numberOfBits)
|
||||
{
|
||||
byte retval = NetBitWriter.ReadByte(m_data, numberOfBits, m_readPosition);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bytes without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int16 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int16 PeekInt16()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 16, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 16, m_readPosition);
|
||||
return (short)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt16 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int32 PeekInt32()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
uint retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition);
|
||||
return (Int32)retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int32 without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into a UInt32 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
public Int64 PeekInt64()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError);
|
||||
unchecked
|
||||
{
|
||||
ulong retval = PeekUInt64();
|
||||
long longRetval = (long)retval;
|
||||
return longRetval;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an UInt64 without advancing the read pointer
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the specified number of bits into an Int64 without advancing the read pointer
|
||||
/// </summary>
|
||||
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
|
||||
//
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekFloat()
|
||||
{
|
||||
return PeekSingle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 32-bit Single without advancing the read pointer
|
||||
/// </summary>
|
||||
public float PeekSingle()
|
||||
{
|
||||
NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError);
|
||||
|
||||
if ((m_readPosition & 7) == 0) // read directly
|
||||
{
|
||||
float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3);
|
||||
return retval;
|
||||
}
|
||||
|
||||
byte[] bytes = PeekBytes(4);
|
||||
return BitConverter.ToSingle(bytes, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a 64-bit Double without advancing the read pointer
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a string without advancing the read pointer
|
||||
/// </summary>
|
||||
public string PeekString()
|
||||
{
|
||||
int wasReadPosition = m_readPosition;
|
||||
string retval = ReadString();
|
||||
m_readPosition = wasReadPosition;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,103 +1,103 @@
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetIncomingMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllFields(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllProperties(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException("target");
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetIncomingMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Reads all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllFields(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void ReadAllProperties(object target, BindingFlags flags)
|
||||
{
|
||||
if (target == null)
|
||||
throw new ArgumentNullException("target");
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
Type tp = target.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,475 +1,475 @@
|
||||
/* 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 + 2) * 8));
|
||||
WriteVariableUInt32((uint)bytes.Length);
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an endpoint description
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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 + 2) * 8));
|
||||
WriteVariableUInt32((uint)bytes.Length);
|
||||
Write(bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an endpoint description
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,115 +1,115 @@
|
||||
/* 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.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Incoming message either sent from a remote peer or generated within the library
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Type={MessageType} LengthBits={LengthBits}")]
|
||||
public sealed class NetIncomingMessage : NetBuffer
|
||||
{
|
||||
internal NetIncomingMessageType m_incomingMessageType;
|
||||
internal IPEndPoint m_senderEndPoint;
|
||||
internal NetConnection m_senderConnection;
|
||||
internal int m_sequenceNumber;
|
||||
internal NetMessageType m_receivedMessageType;
|
||||
internal bool m_isFragment;
|
||||
internal double m_receiveTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of this incoming message
|
||||
/// </summary>
|
||||
public NetIncomingMessageType MessageType { get { return m_incomingMessageType; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delivery method this message was sent with (if user data)
|
||||
/// </summary>
|
||||
public NetDeliveryMethod DeliveryMethod { get { return NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence channel this message was sent with (if user data)
|
||||
/// </summary>
|
||||
public int SequenceChannel { get { return (int)m_receivedMessageType - (int)NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
|
||||
|
||||
/// <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; } }
|
||||
|
||||
/// <summary>
|
||||
/// What local time the message was received from the network
|
||||
/// </summary>
|
||||
public double ReceiveTime { get { return m_receiveTime; } }
|
||||
|
||||
internal NetIncomingMessage()
|
||||
{
|
||||
}
|
||||
|
||||
internal NetIncomingMessage(NetIncomingMessageType tp)
|
||||
{
|
||||
m_incomingMessageType = tp;
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_incomingMessageType = NetIncomingMessageType.Error;
|
||||
m_readPosition = 0;
|
||||
m_receivedMessageType = NetMessageType.LibraryError;
|
||||
m_senderConnection = null;
|
||||
m_bitLength = 0;
|
||||
m_isFragment = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt a message
|
||||
/// </summary>
|
||||
/// <param name="encryption">The encryption algorithm used to encrypt the message</param>
|
||||
/// <returns>true on success</returns>
|
||||
public bool Decrypt(INetEncryption encryption)
|
||||
{
|
||||
return encryption.Decrypt(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a value, in local time comparable to NetTime.Now, written using WriteTime()
|
||||
/// Must have a connected sender
|
||||
/// </summary>
|
||||
public double ReadTime(bool highPrecision)
|
||||
{
|
||||
return ReadTime(m_senderConnection, highPrecision);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetIncomingMessage #" + m_sequenceNumber + " " + this.LengthBytes + " bytes]";
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Incoming message either sent from a remote peer or generated within the library
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Type={MessageType} LengthBits={LengthBits}")]
|
||||
public sealed class NetIncomingMessage : NetBuffer
|
||||
{
|
||||
internal NetIncomingMessageType m_incomingMessageType;
|
||||
internal IPEndPoint m_senderEndPoint;
|
||||
internal NetConnection m_senderConnection;
|
||||
internal int m_sequenceNumber;
|
||||
internal NetMessageType m_receivedMessageType;
|
||||
internal bool m_isFragment;
|
||||
internal double m_receiveTime;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of this incoming message
|
||||
/// </summary>
|
||||
public NetIncomingMessageType MessageType { get { return m_incomingMessageType; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delivery method this message was sent with (if user data)
|
||||
/// </summary>
|
||||
public NetDeliveryMethod DeliveryMethod { get { return NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence channel this message was sent with (if user data)
|
||||
/// </summary>
|
||||
public int SequenceChannel { get { return (int)m_receivedMessageType - (int)NetUtility.GetDeliveryMethod(m_receivedMessageType); } }
|
||||
|
||||
/// <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; } }
|
||||
|
||||
/// <summary>
|
||||
/// What local time the message was received from the network
|
||||
/// </summary>
|
||||
public double ReceiveTime { get { return m_receiveTime; } }
|
||||
|
||||
internal NetIncomingMessage()
|
||||
{
|
||||
}
|
||||
|
||||
internal NetIncomingMessage(NetIncomingMessageType tp)
|
||||
{
|
||||
m_incomingMessageType = tp;
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_incomingMessageType = NetIncomingMessageType.Error;
|
||||
m_readPosition = 0;
|
||||
m_receivedMessageType = NetMessageType.LibraryError;
|
||||
m_senderConnection = null;
|
||||
m_bitLength = 0;
|
||||
m_isFragment = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrypt a message
|
||||
/// </summary>
|
||||
/// <param name="encryption">The encryption algorithm used to encrypt the message</param>
|
||||
/// <returns>true on success</returns>
|
||||
public bool Decrypt(INetEncryption encryption)
|
||||
{
|
||||
return encryption.Decrypt(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a value, in local time comparable to NetTime.Now, written using WriteTime()
|
||||
/// Must have a connected sender
|
||||
/// </summary>
|
||||
public double ReadTime(bool highPrecision)
|
||||
{
|
||||
return ReadTime(m_senderConnection, highPrecision);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetIncomingMessage #" + m_sequenceNumber + " " + this.LengthBytes + " bytes]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +1,105 @@
|
||||
/* 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>
|
||||
/// The type of a NetIncomingMessage
|
||||
/// </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
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Error; this value should never appear
|
||||
/// </summary>
|
||||
Error = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Status for a connection changed
|
||||
/// </summary>
|
||||
StatusChanged = 1 << 0, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Data sent using SendUnconnectedMessage
|
||||
/// </summary>
|
||||
UnconnectedData = 1 << 1, // Data Based on data received
|
||||
|
||||
/// <summary>
|
||||
/// Connection approval is needed
|
||||
/// </summary>
|
||||
ConnectionApproval = 1 << 2, // Data
|
||||
|
||||
/// <summary>
|
||||
/// Application data
|
||||
/// </summary>
|
||||
Data = 1 << 3, // Data Based on data received
|
||||
|
||||
/// <summary>
|
||||
/// Receipt of delivery
|
||||
/// </summary>
|
||||
Receipt = 1 << 4, // Data
|
||||
|
||||
/// <summary>
|
||||
/// Discovery request for a response
|
||||
/// </summary>
|
||||
DiscoveryRequest = 1 << 5, // (no data)
|
||||
|
||||
/// <summary>
|
||||
/// Discovery response to a request
|
||||
/// </summary>
|
||||
DiscoveryResponse = 1 << 6, // Data
|
||||
|
||||
/// <summary>
|
||||
/// Verbose debug message
|
||||
/// </summary>
|
||||
VerboseDebugMessage = 1 << 7, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Debug message
|
||||
/// </summary>
|
||||
DebugMessage = 1 << 8, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Warning message
|
||||
/// </summary>
|
||||
WarningMessage = 1 << 9, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Error message
|
||||
/// </summary>
|
||||
ErrorMessage = 1 << 10, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// NAT introduction was successful
|
||||
/// </summary>
|
||||
NatIntroductionSuccess = 1 << 11, // Data (as passed to master server)
|
||||
|
||||
/// <summary>
|
||||
/// A roundtrip was measured and NetConnection.AverageRoundtripTime was updated
|
||||
/// </summary>
|
||||
ConnectionLatencyUpdated = 1 << 12, // Seconds as a Single
|
||||
}
|
||||
}
|
||||
/* 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>
|
||||
/// The type of a NetIncomingMessage
|
||||
/// </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
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Error; this value should never appear
|
||||
/// </summary>
|
||||
Error = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Status for a connection changed
|
||||
/// </summary>
|
||||
StatusChanged = 1 << 0, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Data sent using SendUnconnectedMessage
|
||||
/// </summary>
|
||||
UnconnectedData = 1 << 1, // Data Based on data received
|
||||
|
||||
/// <summary>
|
||||
/// Connection approval is needed
|
||||
/// </summary>
|
||||
ConnectionApproval = 1 << 2, // Data
|
||||
|
||||
/// <summary>
|
||||
/// Application data
|
||||
/// </summary>
|
||||
Data = 1 << 3, // Data Based on data received
|
||||
|
||||
/// <summary>
|
||||
/// Receipt of delivery
|
||||
/// </summary>
|
||||
Receipt = 1 << 4, // Data
|
||||
|
||||
/// <summary>
|
||||
/// Discovery request for a response
|
||||
/// </summary>
|
||||
DiscoveryRequest = 1 << 5, // (no data)
|
||||
|
||||
/// <summary>
|
||||
/// Discovery response to a request
|
||||
/// </summary>
|
||||
DiscoveryResponse = 1 << 6, // Data
|
||||
|
||||
/// <summary>
|
||||
/// Verbose debug message
|
||||
/// </summary>
|
||||
VerboseDebugMessage = 1 << 7, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Debug message
|
||||
/// </summary>
|
||||
DebugMessage = 1 << 8, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Warning message
|
||||
/// </summary>
|
||||
WarningMessage = 1 << 9, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// Error message
|
||||
/// </summary>
|
||||
ErrorMessage = 1 << 10, // Data (string)
|
||||
|
||||
/// <summary>
|
||||
/// NAT introduction was successful
|
||||
/// </summary>
|
||||
NatIntroductionSuccess = 1 << 11, // Data (as passed to master server)
|
||||
|
||||
/// <summary>
|
||||
/// A roundtrip was measured and NetConnection.AverageRoundtripTime was updated
|
||||
/// </summary>
|
||||
ConnectionLatencyUpdated = 1 << 12, // Seconds as a Single
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,175 +1,175 @@
|
||||
/* 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 enum NetMessageType : byte
|
||||
{
|
||||
Unconnected = 0,
|
||||
|
||||
UserUnreliable = 1,
|
||||
|
||||
UserSequenced1 = 2,
|
||||
UserSequenced2 = 3,
|
||||
UserSequenced3 = 4,
|
||||
UserSequenced4 = 5,
|
||||
UserSequenced5 = 6,
|
||||
UserSequenced6 = 7,
|
||||
UserSequenced7 = 8,
|
||||
UserSequenced8 = 9,
|
||||
UserSequenced9 = 10,
|
||||
UserSequenced10 = 11,
|
||||
UserSequenced11 = 12,
|
||||
UserSequenced12 = 13,
|
||||
UserSequenced13 = 14,
|
||||
UserSequenced14 = 15,
|
||||
UserSequenced15 = 16,
|
||||
UserSequenced16 = 17,
|
||||
UserSequenced17 = 18,
|
||||
UserSequenced18 = 19,
|
||||
UserSequenced19 = 20,
|
||||
UserSequenced20 = 21,
|
||||
UserSequenced21 = 22,
|
||||
UserSequenced22 = 23,
|
||||
UserSequenced23 = 24,
|
||||
UserSequenced24 = 25,
|
||||
UserSequenced25 = 26,
|
||||
UserSequenced26 = 27,
|
||||
UserSequenced27 = 28,
|
||||
UserSequenced28 = 29,
|
||||
UserSequenced29 = 30,
|
||||
UserSequenced30 = 31,
|
||||
UserSequenced31 = 32,
|
||||
UserSequenced32 = 33,
|
||||
|
||||
UserReliableUnordered = 34,
|
||||
|
||||
UserReliableSequenced1 = 35,
|
||||
UserReliableSequenced2 = 36,
|
||||
UserReliableSequenced3 = 37,
|
||||
UserReliableSequenced4 = 38,
|
||||
UserReliableSequenced5 = 39,
|
||||
UserReliableSequenced6 = 40,
|
||||
UserReliableSequenced7 = 41,
|
||||
UserReliableSequenced8 = 42,
|
||||
UserReliableSequenced9 = 43,
|
||||
UserReliableSequenced10 = 44,
|
||||
UserReliableSequenced11 = 45,
|
||||
UserReliableSequenced12 = 46,
|
||||
UserReliableSequenced13 = 47,
|
||||
UserReliableSequenced14 = 48,
|
||||
UserReliableSequenced15 = 49,
|
||||
UserReliableSequenced16 = 50,
|
||||
UserReliableSequenced17 = 51,
|
||||
UserReliableSequenced18 = 52,
|
||||
UserReliableSequenced19 = 53,
|
||||
UserReliableSequenced20 = 54,
|
||||
UserReliableSequenced21 = 55,
|
||||
UserReliableSequenced22 = 56,
|
||||
UserReliableSequenced23 = 57,
|
||||
UserReliableSequenced24 = 58,
|
||||
UserReliableSequenced25 = 59,
|
||||
UserReliableSequenced26 = 60,
|
||||
UserReliableSequenced27 = 61,
|
||||
UserReliableSequenced28 = 62,
|
||||
UserReliableSequenced29 = 63,
|
||||
UserReliableSequenced30 = 64,
|
||||
UserReliableSequenced31 = 65,
|
||||
UserReliableSequenced32 = 66,
|
||||
|
||||
UserReliableOrdered1 = 67,
|
||||
UserReliableOrdered2 = 68,
|
||||
UserReliableOrdered3 = 69,
|
||||
UserReliableOrdered4 = 70,
|
||||
UserReliableOrdered5 = 71,
|
||||
UserReliableOrdered6 = 72,
|
||||
UserReliableOrdered7 = 73,
|
||||
UserReliableOrdered8 = 74,
|
||||
UserReliableOrdered9 = 75,
|
||||
UserReliableOrdered10 = 76,
|
||||
UserReliableOrdered11 = 77,
|
||||
UserReliableOrdered12 = 78,
|
||||
UserReliableOrdered13 = 79,
|
||||
UserReliableOrdered14 = 80,
|
||||
UserReliableOrdered15 = 81,
|
||||
UserReliableOrdered16 = 82,
|
||||
UserReliableOrdered17 = 83,
|
||||
UserReliableOrdered18 = 84,
|
||||
UserReliableOrdered19 = 85,
|
||||
UserReliableOrdered20 = 86,
|
||||
UserReliableOrdered21 = 87,
|
||||
UserReliableOrdered22 = 88,
|
||||
UserReliableOrdered23 = 89,
|
||||
UserReliableOrdered24 = 90,
|
||||
UserReliableOrdered25 = 91,
|
||||
UserReliableOrdered26 = 92,
|
||||
UserReliableOrdered27 = 93,
|
||||
UserReliableOrdered28 = 94,
|
||||
UserReliableOrdered29 = 95,
|
||||
UserReliableOrdered30 = 96,
|
||||
UserReliableOrdered31 = 97,
|
||||
UserReliableOrdered32 = 98,
|
||||
|
||||
Unused1 = 99,
|
||||
Unused2 = 100,
|
||||
Unused3 = 101,
|
||||
Unused4 = 102,
|
||||
Unused5 = 103,
|
||||
Unused6 = 104,
|
||||
Unused7 = 105,
|
||||
Unused8 = 106,
|
||||
Unused9 = 107,
|
||||
Unused10 = 108,
|
||||
Unused11 = 109,
|
||||
Unused12 = 110,
|
||||
Unused13 = 111,
|
||||
Unused14 = 112,
|
||||
Unused15 = 113,
|
||||
Unused16 = 114,
|
||||
Unused17 = 115,
|
||||
Unused18 = 116,
|
||||
Unused19 = 117,
|
||||
Unused20 = 118,
|
||||
Unused21 = 119,
|
||||
Unused22 = 120,
|
||||
Unused23 = 121,
|
||||
Unused24 = 122,
|
||||
Unused25 = 123,
|
||||
Unused26 = 124,
|
||||
Unused27 = 125,
|
||||
Unused28 = 126,
|
||||
Unused29 = 127,
|
||||
|
||||
LibraryError = 128,
|
||||
Ping = 129, // used for RTT calculation
|
||||
Pong = 130, // used for RTT calculation
|
||||
Connect = 131,
|
||||
ConnectResponse = 132,
|
||||
ConnectionEstablished = 133,
|
||||
Acknowledge = 134,
|
||||
Disconnect = 135,
|
||||
Discovery = 136,
|
||||
DiscoveryResponse = 137,
|
||||
NatPunchMessage = 138, // send between peers
|
||||
NatIntroduction = 139, // send to master server
|
||||
ExpandMTURequest = 140,
|
||||
ExpandMTUSuccess = 141,
|
||||
}
|
||||
/* 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 enum NetMessageType : byte
|
||||
{
|
||||
Unconnected = 0,
|
||||
|
||||
UserUnreliable = 1,
|
||||
|
||||
UserSequenced1 = 2,
|
||||
UserSequenced2 = 3,
|
||||
UserSequenced3 = 4,
|
||||
UserSequenced4 = 5,
|
||||
UserSequenced5 = 6,
|
||||
UserSequenced6 = 7,
|
||||
UserSequenced7 = 8,
|
||||
UserSequenced8 = 9,
|
||||
UserSequenced9 = 10,
|
||||
UserSequenced10 = 11,
|
||||
UserSequenced11 = 12,
|
||||
UserSequenced12 = 13,
|
||||
UserSequenced13 = 14,
|
||||
UserSequenced14 = 15,
|
||||
UserSequenced15 = 16,
|
||||
UserSequenced16 = 17,
|
||||
UserSequenced17 = 18,
|
||||
UserSequenced18 = 19,
|
||||
UserSequenced19 = 20,
|
||||
UserSequenced20 = 21,
|
||||
UserSequenced21 = 22,
|
||||
UserSequenced22 = 23,
|
||||
UserSequenced23 = 24,
|
||||
UserSequenced24 = 25,
|
||||
UserSequenced25 = 26,
|
||||
UserSequenced26 = 27,
|
||||
UserSequenced27 = 28,
|
||||
UserSequenced28 = 29,
|
||||
UserSequenced29 = 30,
|
||||
UserSequenced30 = 31,
|
||||
UserSequenced31 = 32,
|
||||
UserSequenced32 = 33,
|
||||
|
||||
UserReliableUnordered = 34,
|
||||
|
||||
UserReliableSequenced1 = 35,
|
||||
UserReliableSequenced2 = 36,
|
||||
UserReliableSequenced3 = 37,
|
||||
UserReliableSequenced4 = 38,
|
||||
UserReliableSequenced5 = 39,
|
||||
UserReliableSequenced6 = 40,
|
||||
UserReliableSequenced7 = 41,
|
||||
UserReliableSequenced8 = 42,
|
||||
UserReliableSequenced9 = 43,
|
||||
UserReliableSequenced10 = 44,
|
||||
UserReliableSequenced11 = 45,
|
||||
UserReliableSequenced12 = 46,
|
||||
UserReliableSequenced13 = 47,
|
||||
UserReliableSequenced14 = 48,
|
||||
UserReliableSequenced15 = 49,
|
||||
UserReliableSequenced16 = 50,
|
||||
UserReliableSequenced17 = 51,
|
||||
UserReliableSequenced18 = 52,
|
||||
UserReliableSequenced19 = 53,
|
||||
UserReliableSequenced20 = 54,
|
||||
UserReliableSequenced21 = 55,
|
||||
UserReliableSequenced22 = 56,
|
||||
UserReliableSequenced23 = 57,
|
||||
UserReliableSequenced24 = 58,
|
||||
UserReliableSequenced25 = 59,
|
||||
UserReliableSequenced26 = 60,
|
||||
UserReliableSequenced27 = 61,
|
||||
UserReliableSequenced28 = 62,
|
||||
UserReliableSequenced29 = 63,
|
||||
UserReliableSequenced30 = 64,
|
||||
UserReliableSequenced31 = 65,
|
||||
UserReliableSequenced32 = 66,
|
||||
|
||||
UserReliableOrdered1 = 67,
|
||||
UserReliableOrdered2 = 68,
|
||||
UserReliableOrdered3 = 69,
|
||||
UserReliableOrdered4 = 70,
|
||||
UserReliableOrdered5 = 71,
|
||||
UserReliableOrdered6 = 72,
|
||||
UserReliableOrdered7 = 73,
|
||||
UserReliableOrdered8 = 74,
|
||||
UserReliableOrdered9 = 75,
|
||||
UserReliableOrdered10 = 76,
|
||||
UserReliableOrdered11 = 77,
|
||||
UserReliableOrdered12 = 78,
|
||||
UserReliableOrdered13 = 79,
|
||||
UserReliableOrdered14 = 80,
|
||||
UserReliableOrdered15 = 81,
|
||||
UserReliableOrdered16 = 82,
|
||||
UserReliableOrdered17 = 83,
|
||||
UserReliableOrdered18 = 84,
|
||||
UserReliableOrdered19 = 85,
|
||||
UserReliableOrdered20 = 86,
|
||||
UserReliableOrdered21 = 87,
|
||||
UserReliableOrdered22 = 88,
|
||||
UserReliableOrdered23 = 89,
|
||||
UserReliableOrdered24 = 90,
|
||||
UserReliableOrdered25 = 91,
|
||||
UserReliableOrdered26 = 92,
|
||||
UserReliableOrdered27 = 93,
|
||||
UserReliableOrdered28 = 94,
|
||||
UserReliableOrdered29 = 95,
|
||||
UserReliableOrdered30 = 96,
|
||||
UserReliableOrdered31 = 97,
|
||||
UserReliableOrdered32 = 98,
|
||||
|
||||
Unused1 = 99,
|
||||
Unused2 = 100,
|
||||
Unused3 = 101,
|
||||
Unused4 = 102,
|
||||
Unused5 = 103,
|
||||
Unused6 = 104,
|
||||
Unused7 = 105,
|
||||
Unused8 = 106,
|
||||
Unused9 = 107,
|
||||
Unused10 = 108,
|
||||
Unused11 = 109,
|
||||
Unused12 = 110,
|
||||
Unused13 = 111,
|
||||
Unused14 = 112,
|
||||
Unused15 = 113,
|
||||
Unused16 = 114,
|
||||
Unused17 = 115,
|
||||
Unused18 = 116,
|
||||
Unused19 = 117,
|
||||
Unused20 = 118,
|
||||
Unused21 = 119,
|
||||
Unused22 = 120,
|
||||
Unused23 = 121,
|
||||
Unused24 = 122,
|
||||
Unused25 = 123,
|
||||
Unused26 = 124,
|
||||
Unused27 = 125,
|
||||
Unused28 = 126,
|
||||
Unused29 = 127,
|
||||
|
||||
LibraryError = 128,
|
||||
Ping = 129, // used for RTT calculation
|
||||
Pong = 130, // used for RTT calculation
|
||||
Connect = 131,
|
||||
ConnectResponse = 132,
|
||||
ConnectionEstablished = 133,
|
||||
Acknowledge = 134,
|
||||
Disconnect = 135,
|
||||
Discovery = 136,
|
||||
DiscoveryResponse = 137,
|
||||
NatPunchMessage = 138, // send between peers
|
||||
NatIntroduction = 139, // send to master server
|
||||
ExpandMTURequest = 140,
|
||||
ExpandMTUSuccess = 141,
|
||||
}
|
||||
}
|
||||
@@ -1,110 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Send NetIntroduction to hostExternal and clientExternal; introducing client to host
|
||||
/// </summary>
|
||||
public void Introduce(
|
||||
IPEndPoint hostInternal,
|
||||
IPEndPoint hostExternal,
|
||||
IPEndPoint clientInternal,
|
||||
IPEndPoint clientExternal,
|
||||
string token)
|
||||
{
|
||||
// send message to client
|
||||
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1);
|
||||
msg.m_messageType = NetMessageType.NatIntroduction;
|
||||
msg.Write((byte)0);
|
||||
msg.Write(hostInternal);
|
||||
msg.Write(hostExternal);
|
||||
msg.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(clientExternal, msg));
|
||||
|
||||
// send message to host
|
||||
msg = CreateMessage(10 + token.Length + 1);
|
||||
msg.m_messageType = NetMessageType.NatIntroduction;
|
||||
msg.Write((byte)1);
|
||||
msg.Write(clientInternal);
|
||||
msg.Write(clientExternal);
|
||||
msg.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(hostExternal, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when host/client receives a NatIntroduction message from a master server
|
||||
/// </summary>
|
||||
private void HandleNatIntroduction(int ptr)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
// read intro
|
||||
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
|
||||
|
||||
byte hostByte = tmp.ReadByte();
|
||||
IPEndPoint remoteInternal = tmp.ReadIPEndPoint();
|
||||
IPEndPoint remoteExternal = tmp.ReadIPEndPoint();
|
||||
string token = tmp.ReadString();
|
||||
bool isHost = (hostByte != 0);
|
||||
|
||||
LogDebug("NAT introduction received; we are designated " + (isHost ? "host" : "client"));
|
||||
|
||||
NetOutgoingMessage punch;
|
||||
|
||||
if (!isHost && m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess) == false)
|
||||
return; // no need to punch - we're not listening for nat intros!
|
||||
|
||||
// send internal punch
|
||||
punch = CreateMessage(1);
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write(hostByte);
|
||||
punch.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteInternal, punch));
|
||||
|
||||
// send external punch
|
||||
punch = CreateMessage(1);
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write(hostByte);
|
||||
punch.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteExternal, punch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when receiving a NatPunchMessage from a remote endpoint
|
||||
/// </summary>
|
||||
private void HandleNatPunch(int ptr, IPEndPoint senderEndPoint)
|
||||
{
|
||||
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
|
||||
|
||||
byte fromHostByte = tmp.ReadByte();
|
||||
if (fromHostByte == 0)
|
||||
{
|
||||
// it's from client
|
||||
LogDebug("NAT punch received from " + senderEndPoint + " we're host, so we ignore this");
|
||||
return; // don't alert hosts about nat punch successes; only clients
|
||||
}
|
||||
string token = tmp.ReadString();
|
||||
|
||||
LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we've succeeded - token is " + token);
|
||||
|
||||
//
|
||||
// Release punch success to client; enabling him to Connect() to msg.SenderIPEndPoint if token is ok
|
||||
//
|
||||
NetIncomingMessage punchSuccess = CreateIncomingMessage(NetIncomingMessageType.NatIntroductionSuccess, 10);
|
||||
punchSuccess.m_senderEndPoint = senderEndPoint;
|
||||
punchSuccess.Write(token);
|
||||
ReleaseMessage(punchSuccess);
|
||||
|
||||
// send a return punch just for good measure
|
||||
var punch = CreateMessage(1);
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write((byte)0);
|
||||
punch.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(senderEndPoint, punch));
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Send NetIntroduction to hostExternal and clientExternal; introducing client to host
|
||||
/// </summary>
|
||||
public void Introduce(
|
||||
IPEndPoint hostInternal,
|
||||
IPEndPoint hostExternal,
|
||||
IPEndPoint clientInternal,
|
||||
IPEndPoint clientExternal,
|
||||
string token)
|
||||
{
|
||||
// send message to client
|
||||
NetOutgoingMessage msg = CreateMessage(10 + token.Length + 1);
|
||||
msg.m_messageType = NetMessageType.NatIntroduction;
|
||||
msg.Write((byte)0);
|
||||
msg.Write(hostInternal);
|
||||
msg.Write(hostExternal);
|
||||
msg.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(clientExternal, msg));
|
||||
|
||||
// send message to host
|
||||
msg = CreateMessage(10 + token.Length + 1);
|
||||
msg.m_messageType = NetMessageType.NatIntroduction;
|
||||
msg.Write((byte)1);
|
||||
msg.Write(clientInternal);
|
||||
msg.Write(clientExternal);
|
||||
msg.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(hostExternal, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when host/client receives a NatIntroduction message from a master server
|
||||
/// </summary>
|
||||
private void HandleNatIntroduction(int ptr)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
|
||||
// read intro
|
||||
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
|
||||
|
||||
byte hostByte = tmp.ReadByte();
|
||||
IPEndPoint remoteInternal = tmp.ReadIPEndPoint();
|
||||
IPEndPoint remoteExternal = tmp.ReadIPEndPoint();
|
||||
string token = tmp.ReadString();
|
||||
bool isHost = (hostByte != 0);
|
||||
|
||||
LogDebug("NAT introduction received; we are designated " + (isHost ? "host" : "client"));
|
||||
|
||||
NetOutgoingMessage punch;
|
||||
|
||||
if (!isHost && m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.NatIntroductionSuccess) == false)
|
||||
return; // no need to punch - we're not listening for nat intros!
|
||||
|
||||
// send internal punch
|
||||
punch = CreateMessage(1);
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write(hostByte);
|
||||
punch.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteInternal, punch));
|
||||
|
||||
// send external punch
|
||||
punch = CreateMessage(1);
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write(hostByte);
|
||||
punch.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(remoteExternal, punch));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when receiving a NatPunchMessage from a remote endpoint
|
||||
/// </summary>
|
||||
private void HandleNatPunch(int ptr, IPEndPoint senderEndPoint)
|
||||
{
|
||||
NetIncomingMessage tmp = SetupReadHelperMessage(ptr, 1000); // never mind length
|
||||
|
||||
byte fromHostByte = tmp.ReadByte();
|
||||
if (fromHostByte == 0)
|
||||
{
|
||||
// it's from client
|
||||
LogDebug("NAT punch received from " + senderEndPoint + " we're host, so we ignore this");
|
||||
return; // don't alert hosts about nat punch successes; only clients
|
||||
}
|
||||
string token = tmp.ReadString();
|
||||
|
||||
LogDebug("NAT punch received from " + senderEndPoint + " we're client, so we've succeeded - token is " + token);
|
||||
|
||||
//
|
||||
// Release punch success to client; enabling him to Connect() to msg.SenderIPEndPoint if token is ok
|
||||
//
|
||||
NetIncomingMessage punchSuccess = CreateIncomingMessage(NetIncomingMessageType.NatIntroductionSuccess, 10);
|
||||
punchSuccess.m_senderEndPoint = senderEndPoint;
|
||||
punchSuccess.Write(token);
|
||||
ReleaseMessage(punchSuccess);
|
||||
|
||||
// send a return punch just for good measure
|
||||
var punch = CreateMessage(1);
|
||||
punch.m_messageType = NetMessageType.NatPunchMessage;
|
||||
punch.Write((byte)0);
|
||||
punch.Write(token);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(senderEndPoint, punch));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +1,91 @@
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetOutgoingMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllFields(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllProperties(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Reflection;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetOutgoingMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes all public and private declared instance fields of the object in alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllFields(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
FieldInfo[] fields = tp.GetFields(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 alphabetical 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 alphabetical order using reflection
|
||||
/// </summary>
|
||||
public void WriteAllProperties(object ob, BindingFlags flags)
|
||||
{
|
||||
if (ob == null)
|
||||
return;
|
||||
Type tp = ob.GetType();
|
||||
|
||||
PropertyInfo[] fields = tp.GetProperties(flags);
|
||||
NetUtility.SortMembersList(fields);
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,132 +1,132 @@
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Outgoing message used to send data to remote peer(s)
|
||||
/// </summary>
|
||||
[DebuggerDisplay("LengthBits={LengthBits}")]
|
||||
public sealed class NetOutgoingMessage : NetBuffer
|
||||
{
|
||||
internal NetMessageType m_messageType;
|
||||
internal bool m_isSent;
|
||||
internal int m_recyclingCount;
|
||||
|
||||
internal int m_fragmentGroup; // which group of fragments ths belongs to
|
||||
internal int m_fragmentGroupTotalBits; // total number of bits in this group
|
||||
internal int m_fragmentChunkByteSize; // size, in bytes, of every chunk but the last one
|
||||
internal int m_fragmentChunkNumber; // which number chunk this is, starting with 0
|
||||
|
||||
internal NetOutgoingMessage()
|
||||
{
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_messageType = NetMessageType.LibraryError;
|
||||
m_bitLength = 0;
|
||||
m_isSent = false;
|
||||
m_recyclingCount = 0;
|
||||
m_fragmentGroup = 0;
|
||||
}
|
||||
|
||||
internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
|
||||
{
|
||||
// 8 bits - NetMessageType
|
||||
// 1 bit - Fragment?
|
||||
// 15 bits - Sequence number
|
||||
// 16 bits - Payload length in bits
|
||||
|
||||
intoBuffer[ptr++] = (byte)m_messageType;
|
||||
|
||||
byte low = (byte)((sequenceNumber << 1) | (m_fragmentGroup == 0 ? 0 : 1));
|
||||
intoBuffer[ptr++] = low;
|
||||
intoBuffer[ptr++] = (byte)(sequenceNumber >> 7);
|
||||
|
||||
if (m_fragmentGroup == 0)
|
||||
{
|
||||
intoBuffer[ptr++] = (byte)m_bitLength;
|
||||
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
|
||||
|
||||
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
|
||||
if (byteLen > 0)
|
||||
{
|
||||
Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
|
||||
ptr += byteLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int wasPtr = ptr;
|
||||
intoBuffer[ptr++] = (byte)m_bitLength;
|
||||
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
|
||||
|
||||
//
|
||||
// write fragmentation header
|
||||
//
|
||||
ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
|
||||
int hdrLen = ptr - wasPtr - 2;
|
||||
|
||||
// update length
|
||||
int realBitLength = m_bitLength + (hdrLen * 8);
|
||||
intoBuffer[wasPtr] = (byte)realBitLength;
|
||||
intoBuffer[wasPtr + 1] = (byte)(realBitLength >> 8);
|
||||
|
||||
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
|
||||
if (byteLen > 0)
|
||||
{
|
||||
Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
|
||||
ptr += byteLen;
|
||||
}
|
||||
}
|
||||
|
||||
NetException.Assert(ptr > 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal int GetEncodedSize()
|
||||
{
|
||||
int retval = NetConstants.UnfragmentedMessageHeaderSize; // regular headers
|
||||
if (m_fragmentGroup != 0)
|
||||
retval += NetFragmentationHelper.GetFragmentationHeaderSize(m_fragmentGroup, m_fragmentGroupTotalBits / 8, m_fragmentChunkByteSize, m_fragmentChunkNumber);
|
||||
retval += this.LengthBytes;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt!
|
||||
/// </summary>
|
||||
public bool Encrypt(INetEncryption encryption)
|
||||
{
|
||||
return encryption.Encrypt(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetOutgoingMessage " + m_messageType + " " + this.LengthBytes + " bytes]";
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Outgoing message used to send data to remote peer(s)
|
||||
/// </summary>
|
||||
[DebuggerDisplay("LengthBits={LengthBits}")]
|
||||
public sealed class NetOutgoingMessage : NetBuffer
|
||||
{
|
||||
internal NetMessageType m_messageType;
|
||||
internal bool m_isSent;
|
||||
internal int m_recyclingCount;
|
||||
|
||||
internal int m_fragmentGroup; // which group of fragments ths belongs to
|
||||
internal int m_fragmentGroupTotalBits; // total number of bits in this group
|
||||
internal int m_fragmentChunkByteSize; // size, in bytes, of every chunk but the last one
|
||||
internal int m_fragmentChunkNumber; // which number chunk this is, starting with 0
|
||||
|
||||
internal NetOutgoingMessage()
|
||||
{
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
m_messageType = NetMessageType.LibraryError;
|
||||
m_bitLength = 0;
|
||||
m_isSent = false;
|
||||
m_recyclingCount = 0;
|
||||
m_fragmentGroup = 0;
|
||||
}
|
||||
|
||||
internal int Encode(byte[] intoBuffer, int ptr, int sequenceNumber)
|
||||
{
|
||||
// 8 bits - NetMessageType
|
||||
// 1 bit - Fragment?
|
||||
// 15 bits - Sequence number
|
||||
// 16 bits - Payload length in bits
|
||||
|
||||
intoBuffer[ptr++] = (byte)m_messageType;
|
||||
|
||||
byte low = (byte)((sequenceNumber << 1) | (m_fragmentGroup == 0 ? 0 : 1));
|
||||
intoBuffer[ptr++] = low;
|
||||
intoBuffer[ptr++] = (byte)(sequenceNumber >> 7);
|
||||
|
||||
if (m_fragmentGroup == 0)
|
||||
{
|
||||
intoBuffer[ptr++] = (byte)m_bitLength;
|
||||
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
|
||||
|
||||
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
|
||||
if (byteLen > 0)
|
||||
{
|
||||
Buffer.BlockCopy(m_data, 0, intoBuffer, ptr, byteLen);
|
||||
ptr += byteLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int wasPtr = ptr;
|
||||
intoBuffer[ptr++] = (byte)m_bitLength;
|
||||
intoBuffer[ptr++] = (byte)(m_bitLength >> 8);
|
||||
|
||||
//
|
||||
// write fragmentation header
|
||||
//
|
||||
ptr = NetFragmentationHelper.WriteHeader(intoBuffer, ptr, m_fragmentGroup, m_fragmentGroupTotalBits, m_fragmentChunkByteSize, m_fragmentChunkNumber);
|
||||
int hdrLen = ptr - wasPtr - 2;
|
||||
|
||||
// update length
|
||||
int realBitLength = m_bitLength + (hdrLen * 8);
|
||||
intoBuffer[wasPtr] = (byte)realBitLength;
|
||||
intoBuffer[wasPtr + 1] = (byte)(realBitLength >> 8);
|
||||
|
||||
int byteLen = NetUtility.BytesToHoldBits(m_bitLength);
|
||||
if (byteLen > 0)
|
||||
{
|
||||
Buffer.BlockCopy(m_data, (int)(m_fragmentChunkNumber * m_fragmentChunkByteSize), intoBuffer, ptr, byteLen);
|
||||
ptr += byteLen;
|
||||
}
|
||||
}
|
||||
|
||||
NetException.Assert(ptr > 0);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
internal int GetEncodedSize()
|
||||
{
|
||||
int retval = NetConstants.UnfragmentedMessageHeaderSize; // regular headers
|
||||
if (m_fragmentGroup != 0)
|
||||
retval += NetFragmentationHelper.GetFragmentationHeaderSize(m_fragmentGroup, m_fragmentGroupTotalBits / 8, m_fragmentChunkByteSize, m_fragmentChunkNumber);
|
||||
retval += this.LengthBytes;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encrypt this message using the provided algorithm; no more writing can be done before sending it or the message will be corrupt!
|
||||
/// </summary>
|
||||
public bool Encrypt(INetEncryption encryption)
|
||||
{
|
||||
return encryption.Encrypt(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetOutgoingMessage " + m_messageType + " " + this.LengthBytes + " bytes]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,60 @@
|
||||
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(0);
|
||||
om.m_messageType = NetMessageType.Discovery;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(IPAddress.Broadcast, serverPort), om));
|
||||
}
|
||||
|
||||
/// <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;
|
||||
DiscoverKnownPeer(new IPEndPoint(address, serverPort));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a discovery signal to a single known host
|
||||
/// </summary>
|
||||
public void DiscoverKnownPeer(IPEndPoint endPoint)
|
||||
{
|
||||
NetOutgoingMessage om = CreateMessage(0);
|
||||
om.m_messageType = NetMessageType.Discovery;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(endPoint, om));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a discovery response message
|
||||
/// </summary>
|
||||
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg == null)
|
||||
msg = CreateMessage(0);
|
||||
else if (msg.m_isSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
|
||||
if (msg.LengthBytes >= m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)");
|
||||
|
||||
msg.m_messageType = NetMessageType.DiscoveryResponse;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
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(0);
|
||||
om.m_messageType = NetMessageType.Discovery;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(IPAddress.Broadcast, serverPort), om));
|
||||
}
|
||||
|
||||
/// <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;
|
||||
DiscoverKnownPeer(new IPEndPoint(address, serverPort));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Emit a discovery signal to a single known host
|
||||
/// </summary>
|
||||
public void DiscoverKnownPeer(IPEndPoint endPoint)
|
||||
{
|
||||
NetOutgoingMessage om = CreateMessage(0);
|
||||
om.m_messageType = NetMessageType.Discovery;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(endPoint, om));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a discovery response message
|
||||
/// </summary>
|
||||
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg == null)
|
||||
msg = CreateMessage(0);
|
||||
else if (msg.m_isSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
|
||||
if (msg.LengthBytes >= m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Cannot send discovery message larger than MTU (currently " + m_configuration.MaximumTransmissionUnit + " bytes)");
|
||||
|
||||
msg.m_messageType = NetMessageType.DiscoveryResponse;
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,159 +1,159 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal class ReceivedFragmentGroup
|
||||
{
|
||||
public float LastReceived;
|
||||
public byte[] Data;
|
||||
public NetBitVector ReceivedChunks;
|
||||
}
|
||||
|
||||
public partial class NetPeer
|
||||
{
|
||||
private int m_lastUsedFragmentGroup;
|
||||
|
||||
private Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>> m_receivedFragmentGroups;
|
||||
|
||||
// on user thread
|
||||
private void SendFragmentedMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
// Note: this group id is PER SENDING/NetPeer; ie. same id is sent to all recipients;
|
||||
// this should be ok however; as long as recipients differentiate between same id but different sender
|
||||
int group = Interlocked.Increment(ref m_lastUsedFragmentGroup);
|
||||
if (group >= NetConstants.MaxFragmentationGroups)
|
||||
{
|
||||
// @TODO: not thread safe; but in practice probably not an issue
|
||||
m_lastUsedFragmentGroup = 1;
|
||||
group = 1;
|
||||
}
|
||||
msg.m_fragmentGroup = group;
|
||||
|
||||
// do not send msg; but set fragmentgroup in case user tries to recycle it immediately
|
||||
|
||||
// create fragmentation specifics
|
||||
int totalBytes = msg.LengthBytes;
|
||||
|
||||
// determine minimum mtu for all recipients
|
||||
int mtu = GetMTU(recipients);
|
||||
int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);
|
||||
|
||||
int numChunks = totalBytes / bytesPerChunk;
|
||||
if (numChunks * bytesPerChunk < totalBytes)
|
||||
numChunks++;
|
||||
|
||||
int bitsPerChunk = bytesPerChunk * 8;
|
||||
int bitsLeft = msg.LengthBits;
|
||||
for (int i = 0; i < numChunks; i++)
|
||||
{
|
||||
NetOutgoingMessage chunk = CreateMessage(mtu);
|
||||
|
||||
chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
|
||||
chunk.m_data = msg.m_data;
|
||||
chunk.m_fragmentGroup = group;
|
||||
chunk.m_fragmentGroupTotalBits = totalBytes * 8;
|
||||
chunk.m_fragmentChunkByteSize = bytesPerChunk;
|
||||
chunk.m_fragmentChunkNumber = i;
|
||||
|
||||
NetException.Assert(chunk.m_bitLength != 0);
|
||||
NetException.Assert(chunk.GetEncodedSize() < mtu);
|
||||
|
||||
Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);
|
||||
|
||||
foreach (NetConnection recipient in recipients)
|
||||
recipient.EnqueueMessage(chunk, method, sequenceChannel);
|
||||
|
||||
bitsLeft -= bitsPerChunk;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void HandleReleasedFragment(NetIncomingMessage im)
|
||||
{
|
||||
//
|
||||
// read fragmentation header and combine fragments
|
||||
//
|
||||
int group;
|
||||
int totalBits;
|
||||
int chunkByteSize;
|
||||
int chunkNumber;
|
||||
int ptr = NetFragmentationHelper.ReadHeader(
|
||||
im.m_data, 0,
|
||||
out group,
|
||||
out totalBits,
|
||||
out chunkByteSize,
|
||||
out chunkNumber
|
||||
);
|
||||
|
||||
NetException.Assert(im.LengthBytes > ptr);
|
||||
|
||||
NetException.Assert(group > 0);
|
||||
NetException.Assert(totalBits > 0);
|
||||
NetException.Assert(chunkByteSize > 0);
|
||||
|
||||
int totalBytes = NetUtility.BytesToHoldBits((int)totalBits);
|
||||
int totalNumChunks = totalBytes / chunkByteSize;
|
||||
if (totalNumChunks * chunkByteSize < totalBytes)
|
||||
totalNumChunks++;
|
||||
|
||||
NetException.Assert(chunkNumber < totalNumChunks);
|
||||
|
||||
if (chunkNumber >= totalNumChunks)
|
||||
{
|
||||
LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<int, ReceivedFragmentGroup> groups;
|
||||
if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
|
||||
{
|
||||
groups = new Dictionary<int, ReceivedFragmentGroup>();
|
||||
m_receivedFragmentGroups[im.SenderConnection] = groups;
|
||||
}
|
||||
|
||||
ReceivedFragmentGroup info;
|
||||
if (!groups.TryGetValue(group, out info))
|
||||
{
|
||||
info = new ReceivedFragmentGroup();
|
||||
info.Data = new byte[totalBytes];
|
||||
info.ReceivedChunks = new NetBitVector(totalNumChunks);
|
||||
groups[group] = info;
|
||||
}
|
||||
|
||||
info.ReceivedChunks[chunkNumber] = true;
|
||||
info.LastReceived = (float)NetTime.Now;
|
||||
|
||||
// copy to data
|
||||
int offset = (chunkNumber * chunkByteSize);
|
||||
Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr);
|
||||
|
||||
int cnt = info.ReceivedChunks.Count();
|
||||
//LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")");
|
||||
|
||||
LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");
|
||||
|
||||
if (info.ReceivedChunks.Count() == totalNumChunks)
|
||||
{
|
||||
// Done! Transform this incoming message
|
||||
im.m_data = info.Data;
|
||||
im.m_bitLength = (int)totalBits;
|
||||
im.m_isFragment = false;
|
||||
|
||||
LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");
|
||||
groups.Remove(group);
|
||||
|
||||
ReleaseMessage(im);
|
||||
}
|
||||
else
|
||||
{
|
||||
// data has been copied; recycle this incoming message
|
||||
Recycle(im);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal class ReceivedFragmentGroup
|
||||
{
|
||||
public float LastReceived;
|
||||
public byte[] Data;
|
||||
public NetBitVector ReceivedChunks;
|
||||
}
|
||||
|
||||
public partial class NetPeer
|
||||
{
|
||||
private int m_lastUsedFragmentGroup;
|
||||
|
||||
private Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>> m_receivedFragmentGroups;
|
||||
|
||||
// on user thread
|
||||
private void SendFragmentedMessage(NetOutgoingMessage msg, IList<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
// Note: this group id is PER SENDING/NetPeer; ie. same id is sent to all recipients;
|
||||
// this should be ok however; as long as recipients differentiate between same id but different sender
|
||||
int group = Interlocked.Increment(ref m_lastUsedFragmentGroup);
|
||||
if (group >= NetConstants.MaxFragmentationGroups)
|
||||
{
|
||||
// @TODO: not thread safe; but in practice probably not an issue
|
||||
m_lastUsedFragmentGroup = 1;
|
||||
group = 1;
|
||||
}
|
||||
msg.m_fragmentGroup = group;
|
||||
|
||||
// do not send msg; but set fragmentgroup in case user tries to recycle it immediately
|
||||
|
||||
// create fragmentation specifics
|
||||
int totalBytes = msg.LengthBytes;
|
||||
|
||||
// determine minimum mtu for all recipients
|
||||
int mtu = GetMTU(recipients);
|
||||
int bytesPerChunk = NetFragmentationHelper.GetBestChunkSize(group, totalBytes, mtu);
|
||||
|
||||
int numChunks = totalBytes / bytesPerChunk;
|
||||
if (numChunks * bytesPerChunk < totalBytes)
|
||||
numChunks++;
|
||||
|
||||
int bitsPerChunk = bytesPerChunk * 8;
|
||||
int bitsLeft = msg.LengthBits;
|
||||
for (int i = 0; i < numChunks; i++)
|
||||
{
|
||||
NetOutgoingMessage chunk = CreateMessage(mtu);
|
||||
|
||||
chunk.m_bitLength = (bitsLeft > bitsPerChunk ? bitsPerChunk : bitsLeft);
|
||||
chunk.m_data = msg.m_data;
|
||||
chunk.m_fragmentGroup = group;
|
||||
chunk.m_fragmentGroupTotalBits = totalBytes * 8;
|
||||
chunk.m_fragmentChunkByteSize = bytesPerChunk;
|
||||
chunk.m_fragmentChunkNumber = i;
|
||||
|
||||
NetException.Assert(chunk.m_bitLength != 0);
|
||||
NetException.Assert(chunk.GetEncodedSize() < mtu);
|
||||
|
||||
Interlocked.Add(ref chunk.m_recyclingCount, recipients.Count);
|
||||
|
||||
foreach (NetConnection recipient in recipients)
|
||||
recipient.EnqueueMessage(chunk, method, sequenceChannel);
|
||||
|
||||
bitsLeft -= bitsPerChunk;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void HandleReleasedFragment(NetIncomingMessage im)
|
||||
{
|
||||
//
|
||||
// read fragmentation header and combine fragments
|
||||
//
|
||||
int group;
|
||||
int totalBits;
|
||||
int chunkByteSize;
|
||||
int chunkNumber;
|
||||
int ptr = NetFragmentationHelper.ReadHeader(
|
||||
im.m_data, 0,
|
||||
out group,
|
||||
out totalBits,
|
||||
out chunkByteSize,
|
||||
out chunkNumber
|
||||
);
|
||||
|
||||
NetException.Assert(im.LengthBytes > ptr);
|
||||
|
||||
NetException.Assert(group > 0);
|
||||
NetException.Assert(totalBits > 0);
|
||||
NetException.Assert(chunkByteSize > 0);
|
||||
|
||||
int totalBytes = NetUtility.BytesToHoldBits((int)totalBits);
|
||||
int totalNumChunks = totalBytes / chunkByteSize;
|
||||
if (totalNumChunks * chunkByteSize < totalBytes)
|
||||
totalNumChunks++;
|
||||
|
||||
NetException.Assert(chunkNumber < totalNumChunks);
|
||||
|
||||
if (chunkNumber >= totalNumChunks)
|
||||
{
|
||||
LogWarning("Index out of bounds for chunk " + chunkNumber + " (total chunks " + totalNumChunks + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<int, ReceivedFragmentGroup> groups;
|
||||
if (!m_receivedFragmentGroups.TryGetValue(im.SenderConnection, out groups))
|
||||
{
|
||||
groups = new Dictionary<int, ReceivedFragmentGroup>();
|
||||
m_receivedFragmentGroups[im.SenderConnection] = groups;
|
||||
}
|
||||
|
||||
ReceivedFragmentGroup info;
|
||||
if (!groups.TryGetValue(group, out info))
|
||||
{
|
||||
info = new ReceivedFragmentGroup();
|
||||
info.Data = new byte[totalBytes];
|
||||
info.ReceivedChunks = new NetBitVector(totalNumChunks);
|
||||
groups[group] = info;
|
||||
}
|
||||
|
||||
info.ReceivedChunks[chunkNumber] = true;
|
||||
info.LastReceived = (float)NetTime.Now;
|
||||
|
||||
// copy to data
|
||||
int offset = (chunkNumber * chunkByteSize);
|
||||
Buffer.BlockCopy(im.m_data, ptr, info.Data, offset, im.LengthBytes - ptr);
|
||||
|
||||
int cnt = info.ReceivedChunks.Count();
|
||||
//LogVerbose("Found fragment #" + chunkNumber + " in group " + group + " offset " + offset + " of total bits " + totalBits + " (total chunks done " + cnt + ")");
|
||||
|
||||
LogVerbose("Received fragment " + chunkNumber + " of " + totalNumChunks + " (" + cnt + " chunks received)");
|
||||
|
||||
if (info.ReceivedChunks.Count() == totalNumChunks)
|
||||
{
|
||||
// Done! Transform this incoming message
|
||||
im.m_data = info.Data;
|
||||
im.m_bitLength = (int)totalBits;
|
||||
im.m_isFragment = false;
|
||||
|
||||
LogVerbose("Fragment group #" + group + " fully received in " + totalNumChunks + " chunks (" + totalBits + " bits)");
|
||||
groups.Remove(group);
|
||||
|
||||
ReleaseMessage(im);
|
||||
}
|
||||
else
|
||||
{
|
||||
// data has been copied; recycle this incoming message
|
||||
Recycle(im);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#if !__ANDROID__ && !IOS
|
||||
#if !__ANDROID__ && !IOS
|
||||
#define IS_MAC_AVAILABLE
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,304 +1,304 @@
|
||||
/* 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 USE_RELEASE_STATISTICS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
private readonly 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, out bool connectionReset)
|
||||
{
|
||||
connectionReset = false;
|
||||
|
||||
// simulate loss
|
||||
float loss = m_configuration.m_loss;
|
||||
if (loss > 0.0f)
|
||||
{
|
||||
if ((float)NetRandom.Instance.NextDouble() < 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");
|
||||
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
|
||||
// TODO: handle wasSent == false?
|
||||
return;
|
||||
}
|
||||
|
||||
int num = 1;
|
||||
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
|
||||
num++;
|
||||
|
||||
float delay = 0;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextSingle() * 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;
|
||||
|
||||
bool connectionReset;
|
||||
|
||||
RestartDelaySending:
|
||||
foreach (DelayedPacket p in m_delayedPackets)
|
||||
{
|
||||
if (now > p.DelayedUntil)
|
||||
{
|
||||
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
|
||||
m_delayedPackets.Remove(p);
|
||||
goto RestartDelaySending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FlushDelayedPackets()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool connectionReset;
|
||||
foreach (DelayedPacket p in m_delayedPackets)
|
||||
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
|
||||
m_delayedPackets.Clear();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
internal bool ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target, out bool connectionReset)
|
||||
{
|
||||
connectionReset = false;
|
||||
try
|
||||
{
|
||||
// TODO: refactor this check outta here
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
{
|
||||
// Some networks do not allow
|
||||
// a global broadcast so we use the BroadcastAddress from the configuration
|
||||
// this can be resolved to a local broadcast addresss e.g 192.168.x.255
|
||||
target.Address = m_configuration.BroadcastAddress;
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
}
|
||||
|
||||
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!");
|
||||
|
||||
// LogDebug("Sent " + numBytes + " bytes");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return false;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
|
||||
connectionReset = true;
|
||||
return false;
|
||||
}
|
||||
LogError("Failed to send packet: " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket.DontFragment = true;
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
|
||||
m_statistics.PacketSent(numBytes, 1);
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.MessageSize)
|
||||
return false;
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return true;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
return true;
|
||||
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_socket.DontFragment = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket.DontFragment = true;
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.MessageSize)
|
||||
return false;
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return true;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
return true;
|
||||
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_socket.DontFragment = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Release - just send the packet straight away
|
||||
//
|
||||
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
|
||||
{
|
||||
#if USE_RELEASE_STATISTICS
|
||||
m_statistics.PacketSent(numBytes, numMessages);
|
||||
#endif
|
||||
connectionReset = false;
|
||||
try
|
||||
{
|
||||
// TODO: refactor this check outta here
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
|
||||
connectionReset = true;
|
||||
return;
|
||||
}
|
||||
LogError("Failed to send packet: " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void FlushDelayedPackets()
|
||||
{
|
||||
}
|
||||
|
||||
private void SendCallBack(IAsyncResult res)
|
||||
{
|
||||
NetException.Assert(res.IsCompleted == true);
|
||||
m_socket.EndSendTo(res);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/* 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 USE_RELEASE_STATISTICS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
|
||||
#if DEBUG
|
||||
private readonly 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, out bool connectionReset)
|
||||
{
|
||||
connectionReset = false;
|
||||
|
||||
// simulate loss
|
||||
float loss = m_configuration.m_loss;
|
||||
if (loss > 0.0f)
|
||||
{
|
||||
if ((float)NetRandom.Instance.NextDouble() < 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");
|
||||
bool wasSent = ActuallySendPacket(m_sendBuffer, numBytes, target, out connectionReset);
|
||||
// TODO: handle wasSent == false?
|
||||
return;
|
||||
}
|
||||
|
||||
int num = 1;
|
||||
if (m_configuration.m_duplicates > 0.0f && NetRandom.Instance.NextSingle() < m_configuration.m_duplicates)
|
||||
num++;
|
||||
|
||||
float delay = 0;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
delay = m_configuration.m_minimumOneWayLatency + (NetRandom.Instance.NextSingle() * 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;
|
||||
|
||||
bool connectionReset;
|
||||
|
||||
RestartDelaySending:
|
||||
foreach (DelayedPacket p in m_delayedPackets)
|
||||
{
|
||||
if (now > p.DelayedUntil)
|
||||
{
|
||||
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
|
||||
m_delayedPackets.Remove(p);
|
||||
goto RestartDelaySending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FlushDelayedPackets()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool connectionReset;
|
||||
foreach (DelayedPacket p in m_delayedPackets)
|
||||
ActuallySendPacket(p.Data, p.Data.Length, p.Target, out connectionReset);
|
||||
m_delayedPackets.Clear();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
internal bool ActuallySendPacket(byte[] data, int numBytes, IPEndPoint target, out bool connectionReset)
|
||||
{
|
||||
connectionReset = false;
|
||||
try
|
||||
{
|
||||
// TODO: refactor this check outta here
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
{
|
||||
// Some networks do not allow
|
||||
// a global broadcast so we use the BroadcastAddress from the configuration
|
||||
// this can be resolved to a local broadcast addresss e.g 192.168.x.255
|
||||
target.Address = m_configuration.BroadcastAddress;
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
}
|
||||
|
||||
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!");
|
||||
|
||||
// LogDebug("Sent " + numBytes + " bytes");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return false;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
|
||||
connectionReset = true;
|
||||
return false;
|
||||
}
|
||||
LogError("Failed to send packet: " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket.DontFragment = true;
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
|
||||
m_statistics.PacketSent(numBytes, 1);
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.MessageSize)
|
||||
return false;
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return true;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
return true;
|
||||
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_socket.DontFragment = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
internal bool SendMTUPacket(int numBytes, IPEndPoint target)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_socket.DontFragment = true;
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.MessageSize)
|
||||
return false;
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return true;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
return true;
|
||||
LogError("Failed to send packet: (" + sx.SocketErrorCode + ") " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_socket.DontFragment = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Release - just send the packet straight away
|
||||
//
|
||||
internal void SendPacket(int numBytes, IPEndPoint target, int numMessages, out bool connectionReset)
|
||||
{
|
||||
#if USE_RELEASE_STATISTICS
|
||||
m_statistics.PacketSent(numBytes, numMessages);
|
||||
#endif
|
||||
connectionReset = false;
|
||||
try
|
||||
{
|
||||
// TODO: refactor this check outta here
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
|
||||
int bytesSent = m_socket.SendTo(m_sendBuffer, 0, numBytes, SocketFlags.None, target);
|
||||
if (numBytes != bytesSent)
|
||||
LogWarning("Failed to send the full " + numBytes + "; only " + bytesSent + " bytes sent in packet!");
|
||||
}
|
||||
catch (SocketException sx)
|
||||
{
|
||||
if (sx.SocketErrorCode == SocketError.WouldBlock)
|
||||
{
|
||||
// send buffer full?
|
||||
LogWarning("Socket threw exception; would block - send buffer full? Increase in NetPeerConfiguration");
|
||||
return;
|
||||
}
|
||||
if (sx.SocketErrorCode == SocketError.ConnectionReset)
|
||||
{
|
||||
// connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable"
|
||||
connectionReset = true;
|
||||
return;
|
||||
}
|
||||
LogError("Failed to send packet: " + sx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogError("Failed to send packet: " + ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (target.Address == IPAddress.Broadcast)
|
||||
m_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private void FlushDelayedPackets()
|
||||
{
|
||||
}
|
||||
|
||||
private void SendCallBack(IAsyncResult res)
|
||||
{
|
||||
NetException.Assert(res.IsCompleted == true);
|
||||
m_socket.EndSendTo(res);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,51 +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));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,228 +1,228 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
private List<byte[]> m_storagePool; // sorted smallest to largest
|
||||
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool;
|
||||
private NetQueue<NetIncomingMessage> m_incomingMessagesPool;
|
||||
|
||||
internal int m_storagePoolBytes;
|
||||
|
||||
private void InitializePools()
|
||||
{
|
||||
if (m_configuration.UseMessageRecycling)
|
||||
{
|
||||
m_storagePool = new List<byte[]>(16);
|
||||
m_outgoingMessagesPool = new NetQueue<NetOutgoingMessage>(4);
|
||||
m_incomingMessagesPool = new NetQueue<NetIncomingMessage>(4);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_storagePool = null;
|
||||
m_outgoingMessagesPool = null;
|
||||
m_incomingMessagesPool = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] GetStorage(int minimumCapacityInBytes)
|
||||
{
|
||||
if (m_storagePool == null)
|
||||
return new byte[minimumCapacityInBytes];
|
||||
|
||||
lock (m_storagePool)
|
||||
{
|
||||
for (int i = 0; i < m_storagePool.Count; i++)
|
||||
{
|
||||
byte[] retval = m_storagePool[i];
|
||||
if (retval != null && retval.Length >= minimumCapacityInBytes)
|
||||
{
|
||||
m_storagePool[i] = null;
|
||||
m_storagePoolBytes -= retval.Length;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_statistics.m_bytesAllocated += minimumCapacityInBytes;
|
||||
return new byte[minimumCapacityInBytes];
|
||||
}
|
||||
|
||||
internal void Recycle(byte[] storage)
|
||||
{
|
||||
if (m_storagePool == null)
|
||||
return;
|
||||
|
||||
lock (m_storagePool)
|
||||
{
|
||||
m_storagePoolBytes += storage.Length;
|
||||
int cnt = m_storagePool.Count;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (m_storagePool[i] == null)
|
||||
{
|
||||
m_storagePool[i] = storage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_storagePool.Add(storage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
public NetOutgoingMessage CreateMessage()
|
||||
{
|
||||
return CreateMessage(m_configuration.m_defaultOutgoingMessageCapacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending and writes the provided string to it
|
||||
/// </summary>
|
||||
public NetOutgoingMessage CreateMessage(string content)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
||||
NetOutgoingMessage om = CreateMessage(2 + bytes.Length);
|
||||
om.WriteVariableUInt32((uint)bytes.Length);
|
||||
om.Write(bytes);
|
||||
return om;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">initial capacity in bytes</param>
|
||||
public NetOutgoingMessage CreateMessage(int initialCapacity)
|
||||
{
|
||||
NetOutgoingMessage retval;
|
||||
if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetOutgoingMessage();
|
||||
|
||||
byte[] storage = GetStorage(initialCapacity);
|
||||
retval.m_data = storage;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetIncomingMessage(tp);
|
||||
else
|
||||
retval.m_incomingMessageType = tp;
|
||||
retval.m_data = useStorageData;
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int minimumByteSize)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetIncomingMessage(tp);
|
||||
else
|
||||
retval.m_incomingMessageType = tp;
|
||||
retval.m_data = GetStorage(minimumByteSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles a NetIncomingMessage instance for reuse; taking pressure off the garbage collector
|
||||
/// </summary>
|
||||
public void Recycle(NetIncomingMessage msg)
|
||||
{
|
||||
if (m_incomingMessagesPool == null)
|
||||
return;
|
||||
#if DEBUG
|
||||
if (m_incomingMessagesPool.Contains(msg))
|
||||
throw new NetException("Recyling already recycled message! Thread race?");
|
||||
#endif
|
||||
byte[] storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
Recycle(storage);
|
||||
msg.Reset();
|
||||
m_incomingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles a list of NetIncomingMessage instances for reuse; taking pressure off the garbage collector
|
||||
/// </summary>
|
||||
public void Recycle(IEnumerable<NetIncomingMessage> toRecycle)
|
||||
{
|
||||
if (m_incomingMessagesPool == null)
|
||||
return;
|
||||
|
||||
// first recycle the storage of each message
|
||||
if (m_storagePool != null)
|
||||
{
|
||||
lock (m_storagePool)
|
||||
{
|
||||
foreach (var msg in toRecycle)
|
||||
{
|
||||
var storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
m_storagePoolBytes += storage.Length;
|
||||
int cnt = m_storagePool.Count;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (m_storagePool[i] == null)
|
||||
{
|
||||
m_storagePool[i] = storage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
msg.Reset();
|
||||
m_storagePool.Add(storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then recycle the message objects
|
||||
m_incomingMessagesPool.Enqueue(toRecycle);
|
||||
}
|
||||
|
||||
internal void Recycle(NetOutgoingMessage msg)
|
||||
{
|
||||
if (m_outgoingMessagesPool == null)
|
||||
return;
|
||||
#if DEBUG
|
||||
if (m_outgoingMessagesPool.Contains(msg))
|
||||
throw new NetException("Recyling already recycled message! Thread race?");
|
||||
#endif
|
||||
|
||||
byte[] storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
|
||||
// message fragments cannot be recycled
|
||||
// TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them
|
||||
if (msg.m_fragmentGroup == 0)
|
||||
Recycle(storage);
|
||||
|
||||
msg.Reset();
|
||||
m_outgoingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an incoming message with the required capacity for releasing to the application
|
||||
/// </summary>
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, string text)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
retval = CreateIncomingMessage(tp, 1);
|
||||
retval.Write(string.Empty);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int numBytes = System.Text.Encoding.UTF8.GetByteCount(text);
|
||||
retval = CreateIncomingMessage(tp, numBytes + (numBytes > 127 ? 2 : 1));
|
||||
retval.Write(text);
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
private List<byte[]> m_storagePool; // sorted smallest to largest
|
||||
private NetQueue<NetOutgoingMessage> m_outgoingMessagesPool;
|
||||
private NetQueue<NetIncomingMessage> m_incomingMessagesPool;
|
||||
|
||||
internal int m_storagePoolBytes;
|
||||
|
||||
private void InitializePools()
|
||||
{
|
||||
if (m_configuration.UseMessageRecycling)
|
||||
{
|
||||
m_storagePool = new List<byte[]>(16);
|
||||
m_outgoingMessagesPool = new NetQueue<NetOutgoingMessage>(4);
|
||||
m_incomingMessagesPool = new NetQueue<NetIncomingMessage>(4);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_storagePool = null;
|
||||
m_outgoingMessagesPool = null;
|
||||
m_incomingMessagesPool = null;
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] GetStorage(int minimumCapacityInBytes)
|
||||
{
|
||||
if (m_storagePool == null)
|
||||
return new byte[minimumCapacityInBytes];
|
||||
|
||||
lock (m_storagePool)
|
||||
{
|
||||
for (int i = 0; i < m_storagePool.Count; i++)
|
||||
{
|
||||
byte[] retval = m_storagePool[i];
|
||||
if (retval != null && retval.Length >= minimumCapacityInBytes)
|
||||
{
|
||||
m_storagePool[i] = null;
|
||||
m_storagePoolBytes -= retval.Length;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_statistics.m_bytesAllocated += minimumCapacityInBytes;
|
||||
return new byte[minimumCapacityInBytes];
|
||||
}
|
||||
|
||||
internal void Recycle(byte[] storage)
|
||||
{
|
||||
if (m_storagePool == null)
|
||||
return;
|
||||
|
||||
lock (m_storagePool)
|
||||
{
|
||||
m_storagePoolBytes += storage.Length;
|
||||
int cnt = m_storagePool.Count;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (m_storagePool[i] == null)
|
||||
{
|
||||
m_storagePool[i] = storage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_storagePool.Add(storage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
public NetOutgoingMessage CreateMessage()
|
||||
{
|
||||
return CreateMessage(m_configuration.m_defaultOutgoingMessageCapacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending and writes the provided string to it
|
||||
/// </summary>
|
||||
public NetOutgoingMessage CreateMessage(string content)
|
||||
{
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(content);
|
||||
NetOutgoingMessage om = CreateMessage(2 + bytes.Length);
|
||||
om.WriteVariableUInt32((uint)bytes.Length);
|
||||
om.Write(bytes);
|
||||
return om;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message for sending
|
||||
/// </summary>
|
||||
/// <param name="initialCapacity">initial capacity in bytes</param>
|
||||
public NetOutgoingMessage CreateMessage(int initialCapacity)
|
||||
{
|
||||
NetOutgoingMessage retval;
|
||||
if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetOutgoingMessage();
|
||||
|
||||
byte[] storage = GetStorage(initialCapacity);
|
||||
retval.m_data = storage;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, byte[] useStorageData)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetIncomingMessage(tp);
|
||||
else
|
||||
retval.m_incomingMessageType = tp;
|
||||
retval.m_data = useStorageData;
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, int minimumByteSize)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_incomingMessagesPool == null || !m_incomingMessagesPool.TryDequeue(out retval))
|
||||
retval = new NetIncomingMessage(tp);
|
||||
else
|
||||
retval.m_incomingMessageType = tp;
|
||||
retval.m_data = GetStorage(minimumByteSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles a NetIncomingMessage instance for reuse; taking pressure off the garbage collector
|
||||
/// </summary>
|
||||
public void Recycle(NetIncomingMessage msg)
|
||||
{
|
||||
if (m_incomingMessagesPool == null)
|
||||
return;
|
||||
#if DEBUG
|
||||
if (m_incomingMessagesPool.Contains(msg))
|
||||
throw new NetException("Recyling already recycled message! Thread race?");
|
||||
#endif
|
||||
byte[] storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
Recycle(storage);
|
||||
msg.Reset();
|
||||
m_incomingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recycles a list of NetIncomingMessage instances for reuse; taking pressure off the garbage collector
|
||||
/// </summary>
|
||||
public void Recycle(IEnumerable<NetIncomingMessage> toRecycle)
|
||||
{
|
||||
if (m_incomingMessagesPool == null)
|
||||
return;
|
||||
|
||||
// first recycle the storage of each message
|
||||
if (m_storagePool != null)
|
||||
{
|
||||
lock (m_storagePool)
|
||||
{
|
||||
foreach (var msg in toRecycle)
|
||||
{
|
||||
var storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
m_storagePoolBytes += storage.Length;
|
||||
int cnt = m_storagePool.Count;
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (m_storagePool[i] == null)
|
||||
{
|
||||
m_storagePool[i] = storage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
msg.Reset();
|
||||
m_storagePool.Add(storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then recycle the message objects
|
||||
m_incomingMessagesPool.Enqueue(toRecycle);
|
||||
}
|
||||
|
||||
internal void Recycle(NetOutgoingMessage msg)
|
||||
{
|
||||
if (m_outgoingMessagesPool == null)
|
||||
return;
|
||||
#if DEBUG
|
||||
if (m_outgoingMessagesPool.Contains(msg))
|
||||
throw new NetException("Recyling already recycled message! Thread race?");
|
||||
#endif
|
||||
|
||||
byte[] storage = msg.m_data;
|
||||
msg.m_data = null;
|
||||
|
||||
// message fragments cannot be recycled
|
||||
// TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them
|
||||
if (msg.m_fragmentGroup == 0)
|
||||
Recycle(storage);
|
||||
|
||||
msg.Reset();
|
||||
m_outgoingMessagesPool.Enqueue(msg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an incoming message with the required capacity for releasing to the application
|
||||
/// </summary>
|
||||
internal NetIncomingMessage CreateIncomingMessage(NetIncomingMessageType tp, string text)
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
retval = CreateIncomingMessage(tp, 1);
|
||||
retval.Write(string.Empty);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int numBytes = System.Text.Encoding.UTF8.GetByteCount(text);
|
||||
retval = CreateIncomingMessage(tp, numBytes + (numBytes > 127 ? 2 : 1));
|
||||
retval.Write(text);
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,226 +1,226 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Send a message to a specific connection
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipient">The recipient connection</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method)
|
||||
{
|
||||
return SendMessage(msg, recipient, method, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a specific connection
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipient">The recipient connection</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod)
|
||||
throw new ArgumentOutOfRangeException("sequenceChannel");
|
||||
|
||||
NetException.Assert(
|
||||
((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) ||
|
||||
((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)),
|
||||
"Delivery method " + method + " cannot use sequence channels other than 0!"
|
||||
);
|
||||
|
||||
NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!");
|
||||
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
msg.m_isSent = true;
|
||||
|
||||
int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize
|
||||
if (len <= recipient.m_currentMTU)
|
||||
{
|
||||
Interlocked.Increment(ref msg.m_recyclingCount);
|
||||
return recipient.EnqueueMessage(msg, method, sequenceChannel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// message must be fragmented!
|
||||
if (recipient.m_status != NetConnectionStatus.Connected)
|
||||
return NetSendResult.FailedNotConnected;
|
||||
SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
|
||||
return NetSendResult.Queued; // could be different for each connection; Queued is "most true"
|
||||
}
|
||||
}
|
||||
|
||||
internal int GetMTU(IList<NetConnection> recipients)
|
||||
{
|
||||
int count = recipients.Count;
|
||||
NetException.Assert(count > 0);
|
||||
|
||||
int mtu = int.MaxValue;
|
||||
for(int i=0;i<count;i++)
|
||||
{
|
||||
var conn = recipients[i];
|
||||
int cmtu = conn.m_currentMTU;
|
||||
if (cmtu < mtu)
|
||||
mtu = cmtu;
|
||||
}
|
||||
return mtu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a list of connections
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipients">The list of recipients to send to</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
|
||||
public void SendMessage(NetOutgoingMessage msg, List<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (recipients.Count < 1)
|
||||
throw new NetException("recipients must contain at least one item");
|
||||
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
|
||||
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
|
||||
int mtu = GetMTU(recipients);
|
||||
|
||||
msg.m_isSent = true;
|
||||
|
||||
int len = msg.GetEncodedSize();
|
||||
if (len <= mtu)
|
||||
{
|
||||
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
|
||||
foreach (NetConnection conn in recipients)
|
||||
{
|
||||
if (conn == null)
|
||||
{
|
||||
Interlocked.Decrement(ref msg.m_recyclingCount);
|
||||
continue;
|
||||
}
|
||||
NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
|
||||
if (res != NetSendResult.Queued && res != NetSendResult.Sent)
|
||||
Interlocked.Decrement(ref msg.m_recyclingCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// message must be fragmented!
|
||||
SendFragmentedMessage(msg, recipients, method, sequenceChannel);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
IPAddress adr = NetUtility.Resolve(host);
|
||||
if (adr == null)
|
||||
throw new NetException("Failed to resolve " + host);
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
Interlocked.Increment(ref msg.m_recyclingCount);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(adr, port), msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
Interlocked.Increment(ref msg.m_recyclingCount);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> recipients)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (recipients.Count < 1)
|
||||
throw new NetException("recipients must contain at least one item");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
|
||||
foreach(IPEndPoint ep in recipients)
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(ep, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to this exact same netpeer (loopback)
|
||||
/// </summary>
|
||||
public void SendUnconnectedToSelf(NetOutgoingMessage msg)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false)
|
||||
return; // dropping unconnected message since it's not enabled for receiving
|
||||
|
||||
NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes);
|
||||
om.m_isFragment = false;
|
||||
om.m_receiveTime = NetTime.Now;
|
||||
om.m_senderConnection = null;
|
||||
om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint;
|
||||
om.m_bitLength = msg.LengthBits;
|
||||
|
||||
ReleaseMessage(om);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public partial class NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// Send a message to a specific connection
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipient">The recipient connection</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method)
|
||||
{
|
||||
return SendMessage(msg, recipient, method, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a specific connection
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipient">The recipient connection</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
|
||||
public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod)
|
||||
throw new ArgumentOutOfRangeException("sequenceChannel");
|
||||
|
||||
NetException.Assert(
|
||||
((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) ||
|
||||
((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)),
|
||||
"Delivery method " + method + " cannot use sequence channels other than 0!"
|
||||
);
|
||||
|
||||
NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!");
|
||||
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
msg.m_isSent = true;
|
||||
|
||||
int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize
|
||||
if (len <= recipient.m_currentMTU)
|
||||
{
|
||||
Interlocked.Increment(ref msg.m_recyclingCount);
|
||||
return recipient.EnqueueMessage(msg, method, sequenceChannel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// message must be fragmented!
|
||||
if (recipient.m_status != NetConnectionStatus.Connected)
|
||||
return NetSendResult.FailedNotConnected;
|
||||
SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel);
|
||||
return NetSendResult.Queued; // could be different for each connection; Queued is "most true"
|
||||
}
|
||||
}
|
||||
|
||||
internal int GetMTU(IList<NetConnection> recipients)
|
||||
{
|
||||
int count = recipients.Count;
|
||||
NetException.Assert(count > 0);
|
||||
|
||||
int mtu = int.MaxValue;
|
||||
for(int i=0;i<count;i++)
|
||||
{
|
||||
var conn = recipients[i];
|
||||
int cmtu = conn.m_currentMTU;
|
||||
if (cmtu < mtu)
|
||||
mtu = cmtu;
|
||||
}
|
||||
return mtu;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a list of connections
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="recipients">The list of recipients to send to</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
/// <param name="sequenceChannel">Sequence channel within the delivery method</param>
|
||||
public void SendMessage(NetOutgoingMessage msg, List<NetConnection> recipients, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (recipients.Count < 1)
|
||||
throw new NetException("recipients must contain at least one item");
|
||||
if (method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered)
|
||||
NetException.Assert(sequenceChannel == 0, "Delivery method " + method + " cannot use sequence channels other than 0!");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
|
||||
int mtu = GetMTU(recipients);
|
||||
|
||||
msg.m_isSent = true;
|
||||
|
||||
int len = msg.GetEncodedSize();
|
||||
if (len <= mtu)
|
||||
{
|
||||
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
|
||||
foreach (NetConnection conn in recipients)
|
||||
{
|
||||
if (conn == null)
|
||||
{
|
||||
Interlocked.Decrement(ref msg.m_recyclingCount);
|
||||
continue;
|
||||
}
|
||||
NetSendResult res = conn.EnqueueMessage(msg, method, sequenceChannel);
|
||||
if (res != NetSendResult.Queued && res != NetSendResult.Sent)
|
||||
Interlocked.Decrement(ref msg.m_recyclingCount);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// message must be fragmented!
|
||||
SendFragmentedMessage(msg, recipients, method, sequenceChannel);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
IPAddress adr = NetUtility.Resolve(host);
|
||||
if (adr == null)
|
||||
throw new NetException("Failed to resolve " + host);
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
Interlocked.Increment(ref msg.m_recyclingCount);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(new IPEndPoint(adr, port), msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
Interlocked.Increment(ref msg.m_recyclingCount);
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(recipient, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IList<IPEndPoint> recipients)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (recipients.Count < 1)
|
||||
throw new NetException("recipients must contain at least one item");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
if (msg.LengthBytes > m_configuration.MaximumTransmissionUnit)
|
||||
throw new NetException("Unconnected messages too long! Must be shorter than NetConfiguration.MaximumTransmissionUnit (currently " + m_configuration.MaximumTransmissionUnit + ")");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
Interlocked.Add(ref msg.m_recyclingCount, recipients.Count);
|
||||
foreach(IPEndPoint ep in recipients)
|
||||
m_unsentUnconnectedMessages.Enqueue(new NetTuple<IPEndPoint, NetOutgoingMessage>(ep, msg));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to this exact same netpeer (loopback)
|
||||
/// </summary>
|
||||
public void SendUnconnectedToSelf(NetOutgoingMessage msg)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (msg.m_isSent)
|
||||
throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently");
|
||||
|
||||
msg.m_messageType = NetMessageType.Unconnected;
|
||||
msg.m_isSent = true;
|
||||
|
||||
if (m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData) == false)
|
||||
return; // dropping unconnected message since it's not enabled for receiving
|
||||
|
||||
NetIncomingMessage om = CreateIncomingMessage(NetIncomingMessageType.UnconnectedData, msg.LengthBytes);
|
||||
om.m_isFragment = false;
|
||||
om.m_receiveTime = NetTime.Now;
|
||||
om.m_senderConnection = null;
|
||||
om.m_senderEndPoint = m_socket.LocalEndPoint as IPEndPoint;
|
||||
om.m_bitLength = msg.LengthBits;
|
||||
|
||||
ReleaseMessage(om);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,329 +1,329 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a local peer capable of holding zero, one or more connections to remote peers
|
||||
/// </summary>
|
||||
public partial class NetPeer
|
||||
{
|
||||
private static int s_initializedPeersCount;
|
||||
|
||||
private int m_listenPort;
|
||||
private object m_tag;
|
||||
|
||||
internal readonly List<NetConnection> m_connections;
|
||||
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
|
||||
|
||||
private string m_shutdownReason;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NetPeerStatus of the NetPeer
|
||||
/// </summary>
|
||||
public NetPeerStatus Status { get { return m_status; } }
|
||||
|
||||
/// <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; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique identifier for this NetPeer based on Mac address and ip/port. Note! Not available until Start() has been called!
|
||||
/// </summary>
|
||||
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number this NetPeer is listening and sending on, if Start() has been called
|
||||
/// </summary>
|
||||
public int Port { get { return m_listenPort; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns an UPnP object if enabled in the NetPeerConfiguration
|
||||
/// </summary>
|
||||
public NetUPnP UPnP { get { return m_upnp; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application defined object containing data about the peer
|
||||
/// </summary>
|
||||
public object Tag
|
||||
{
|
||||
get { return m_tag; }
|
||||
set { m_tag = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the list of connections
|
||||
/// </summary>
|
||||
public List<NetConnection> Connections
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (m_connections)
|
||||
return new List<NetConnection>(m_connections);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets 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 used to instanciate this NetPeer
|
||||
/// </summary>
|
||||
public NetPeerConfiguration Configuration { get { return m_configuration; } }
|
||||
|
||||
/// <summary>
|
||||
/// NetPeer constructor
|
||||
/// </summary>
|
||||
public NetPeer(NetPeerConfiguration config)
|
||||
{
|
||||
m_configuration = config;
|
||||
m_statistics = new NetPeerStatistics(this);
|
||||
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4);
|
||||
m_unsentUnconnectedMessages = new NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>>(2);
|
||||
m_connections = new List<NetConnection>();
|
||||
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds to socket and spawns the networking thread
|
||||
/// </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;
|
||||
|
||||
// fix network thread name
|
||||
if (m_configuration.NetworkThreadName == "Lidgren network thread")
|
||||
{
|
||||
int pc = Interlocked.Increment(ref s_initializedPeersCount);
|
||||
m_configuration.NetworkThreadName = "Lidgren network thread " + pc.ToString();
|
||||
}
|
||||
|
||||
InitializeNetwork();
|
||||
|
||||
// start network thread
|
||||
m_networkThread = new Thread(new ThreadStart(NetworkLoop));
|
||||
m_networkThread.Name = m_configuration.NetworkThreadName;
|
||||
m_networkThread.IsBackground = true;
|
||||
m_networkThread.Start();
|
||||
|
||||
// send upnp discovery
|
||||
if (m_upnp != null)
|
||||
m_upnp.Discover(this);
|
||||
|
||||
// allow some time for network thread to start up in case they call Connect() or UPnP calls immediately
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the connection, if any, for a certain remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection GetConnection(IPEndPoint ep)
|
||||
{
|
||||
NetConnection retval;
|
||||
|
||||
// this should not pose a threading problem, m_connectionLookup is never added to concurrently
|
||||
// and TryGetValue will not throw an exception on fail, only yield null, which is acceptable
|
||||
m_connectionLookup.TryGetValue(ep, out retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a pending message from any connection, blocking up to maxMillis if needed
|
||||
/// </summary>
|
||||
public NetIncomingMessage WaitMessage(int maxMillis)
|
||||
{
|
||||
var msg = ReadMessage();
|
||||
if (msg != null)
|
||||
return msg; // no need to wait; we already have a message to deliver
|
||||
if (m_messageReceivedEvent != null)
|
||||
m_messageReceivedEvent.WaitOne(maxMillis);
|
||||
return ReadMessage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a pending message from any connection, if any
|
||||
/// </summary>
|
||||
public NetIncomingMessage ReadMessage()
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_releasedIncomingMessages.TryDequeue(out retval))
|
||||
{
|
||||
if (retval.MessageType == NetIncomingMessageType.StatusChanged)
|
||||
{
|
||||
NetConnectionStatus status = (NetConnectionStatus)retval.PeekByte();
|
||||
retval.SenderConnection.m_visibleStatus = status;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a pending message from any connection, if any
|
||||
/// </summary>
|
||||
public int ReadMessages(IList<NetIncomingMessage> addTo)
|
||||
{
|
||||
int added = m_releasedIncomingMessages.TryDrain(addTo);
|
||||
if (added > 0)
|
||||
{
|
||||
for (int i = 0; i < added; i++)
|
||||
{
|
||||
var index = addTo.Count - added + i;
|
||||
var nim = addTo[index];
|
||||
if (nim.MessageType == NetIncomingMessageType.StatusChanged)
|
||||
{
|
||||
NetConnectionStatus status = (NetConnectionStatus)nim.PeekByte();
|
||||
nim.SenderConnection.m_visibleStatus = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
// send message immediately
|
||||
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
NetException.Assert(msg.m_isSent == false);
|
||||
|
||||
bool connReset;
|
||||
int len = msg.Encode(m_sendBuffer, 0, 0);
|
||||
SendPacket(len, recipient, 1, out connReset);
|
||||
}
|
||||
|
||||
/// <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(string host, int port, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage);
|
||||
}
|
||||
|
||||
/// <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 virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
if (remoteEndPoint == null)
|
||||
throw new ArgumentNullException("remoteEndPoint");
|
||||
|
||||
lock (m_connections)
|
||||
{
|
||||
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 hs;
|
||||
if (m_handshakes.TryGetValue(remoteEndPoint, out hs))
|
||||
{
|
||||
// already trying to connect to that endpoint; make another try
|
||||
switch (hs.m_status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// send another connect
|
||||
hs.m_connectRequested = true;
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// send another response
|
||||
hs.SendConnectResponse((float)NetTime.Now, false);
|
||||
break;
|
||||
default:
|
||||
// weird
|
||||
LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.m_status);
|
||||
break;
|
||||
}
|
||||
return hs;
|
||||
}
|
||||
|
||||
NetConnection conn = new NetConnection(this, remoteEndPoint);
|
||||
conn.m_status = NetConnectionStatus.InitiatedConnect;
|
||||
conn.m_localHailMessage = hailMessage;
|
||||
|
||||
// handle on network thread
|
||||
conn.m_connectRequested = true;
|
||||
conn.m_connectionInitiator = true;
|
||||
|
||||
m_handshakes.Add(remoteEndPoint, conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send raw bytes; only used for debugging
|
||||
/// </summary>
|
||||
#if DEBUG
|
||||
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
|
||||
#else
|
||||
internal void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
|
||||
#endif
|
||||
{
|
||||
// wrong thread - this miiiight crash with network thread... but what's a boy to do.
|
||||
Array.Copy(arr, offset, m_sendBuffer, 0, length);
|
||||
bool unused;
|
||||
SendPacket(length, destination, 1, out unused);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a local peer capable of holding zero, one or more connections to remote peers
|
||||
/// </summary>
|
||||
public partial class NetPeer
|
||||
{
|
||||
private static int s_initializedPeersCount;
|
||||
|
||||
private int m_listenPort;
|
||||
private object m_tag;
|
||||
|
||||
internal readonly List<NetConnection> m_connections;
|
||||
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
|
||||
|
||||
private string m_shutdownReason;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the NetPeerStatus of the NetPeer
|
||||
/// </summary>
|
||||
public NetPeerStatus Status { get { return m_status; } }
|
||||
|
||||
/// <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; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique identifier for this NetPeer based on Mac address and ip/port. Note! Not available until Start() has been called!
|
||||
/// </summary>
|
||||
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number this NetPeer is listening and sending on, if Start() has been called
|
||||
/// </summary>
|
||||
public int Port { get { return m_listenPort; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns an UPnP object if enabled in the NetPeerConfiguration
|
||||
/// </summary>
|
||||
public NetUPnP UPnP { get { return m_upnp; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the application defined object containing data about the peer
|
||||
/// </summary>
|
||||
public object Tag
|
||||
{
|
||||
get { return m_tag; }
|
||||
set { m_tag = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the list of connections
|
||||
/// </summary>
|
||||
public List<NetConnection> Connections
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (m_connections)
|
||||
return new List<NetConnection>(m_connections);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets 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 used to instanciate this NetPeer
|
||||
/// </summary>
|
||||
public NetPeerConfiguration Configuration { get { return m_configuration; } }
|
||||
|
||||
/// <summary>
|
||||
/// NetPeer constructor
|
||||
/// </summary>
|
||||
public NetPeer(NetPeerConfiguration config)
|
||||
{
|
||||
m_configuration = config;
|
||||
m_statistics = new NetPeerStatistics(this);
|
||||
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4);
|
||||
m_unsentUnconnectedMessages = new NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>>(2);
|
||||
m_connections = new List<NetConnection>();
|
||||
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
m_receivedFragmentGroups = new Dictionary<NetConnection, Dictionary<int, ReceivedFragmentGroup>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds to socket and spawns the networking thread
|
||||
/// </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;
|
||||
|
||||
// fix network thread name
|
||||
if (m_configuration.NetworkThreadName == "Lidgren network thread")
|
||||
{
|
||||
int pc = Interlocked.Increment(ref s_initializedPeersCount);
|
||||
m_configuration.NetworkThreadName = "Lidgren network thread " + pc.ToString();
|
||||
}
|
||||
|
||||
InitializeNetwork();
|
||||
|
||||
// start network thread
|
||||
m_networkThread = new Thread(new ThreadStart(NetworkLoop));
|
||||
m_networkThread.Name = m_configuration.NetworkThreadName;
|
||||
m_networkThread.IsBackground = true;
|
||||
m_networkThread.Start();
|
||||
|
||||
// send upnp discovery
|
||||
if (m_upnp != null)
|
||||
m_upnp.Discover(this);
|
||||
|
||||
// allow some time for network thread to start up in case they call Connect() or UPnP calls immediately
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the connection, if any, for a certain remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection GetConnection(IPEndPoint ep)
|
||||
{
|
||||
NetConnection retval;
|
||||
|
||||
// this should not pose a threading problem, m_connectionLookup is never added to concurrently
|
||||
// and TryGetValue will not throw an exception on fail, only yield null, which is acceptable
|
||||
m_connectionLookup.TryGetValue(ep, out retval);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a pending message from any connection, blocking up to maxMillis if needed
|
||||
/// </summary>
|
||||
public NetIncomingMessage WaitMessage(int maxMillis)
|
||||
{
|
||||
var msg = ReadMessage();
|
||||
if (msg != null)
|
||||
return msg; // no need to wait; we already have a message to deliver
|
||||
if (m_messageReceivedEvent != null)
|
||||
m_messageReceivedEvent.WaitOne(maxMillis);
|
||||
return ReadMessage();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a pending message from any connection, if any
|
||||
/// </summary>
|
||||
public NetIncomingMessage ReadMessage()
|
||||
{
|
||||
NetIncomingMessage retval;
|
||||
if (m_releasedIncomingMessages.TryDequeue(out retval))
|
||||
{
|
||||
if (retval.MessageType == NetIncomingMessageType.StatusChanged)
|
||||
{
|
||||
NetConnectionStatus status = (NetConnectionStatus)retval.PeekByte();
|
||||
retval.SenderConnection.m_visibleStatus = status;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a pending message from any connection, if any
|
||||
/// </summary>
|
||||
public int ReadMessages(IList<NetIncomingMessage> addTo)
|
||||
{
|
||||
int added = m_releasedIncomingMessages.TryDrain(addTo);
|
||||
if (added > 0)
|
||||
{
|
||||
for (int i = 0; i < added; i++)
|
||||
{
|
||||
var index = addTo.Count - added + i;
|
||||
var nim = addTo[index];
|
||||
if (nim.MessageType == NetIncomingMessageType.StatusChanged)
|
||||
{
|
||||
NetConnectionStatus status = (NetConnectionStatus)nim.PeekByte();
|
||||
nim.SenderConnection.m_visibleStatus = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
// send message immediately
|
||||
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
VerifyNetworkThread();
|
||||
NetException.Assert(msg.m_isSent == false);
|
||||
|
||||
bool connReset;
|
||||
int len = msg.Encode(m_sendBuffer, 0, 0);
|
||||
SendPacket(len, recipient, 1, out connReset);
|
||||
}
|
||||
|
||||
/// <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(string host, int port, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage);
|
||||
}
|
||||
|
||||
/// <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 virtual NetConnection Connect(IPEndPoint remoteEndPoint, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
if (remoteEndPoint == null)
|
||||
throw new ArgumentNullException("remoteEndPoint");
|
||||
|
||||
lock (m_connections)
|
||||
{
|
||||
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 hs;
|
||||
if (m_handshakes.TryGetValue(remoteEndPoint, out hs))
|
||||
{
|
||||
// already trying to connect to that endpoint; make another try
|
||||
switch (hs.m_status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// send another connect
|
||||
hs.m_connectRequested = true;
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// send another response
|
||||
hs.SendConnectResponse((float)NetTime.Now, false);
|
||||
break;
|
||||
default:
|
||||
// weird
|
||||
LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.m_status);
|
||||
break;
|
||||
}
|
||||
return hs;
|
||||
}
|
||||
|
||||
NetConnection conn = new NetConnection(this, remoteEndPoint);
|
||||
conn.m_status = NetConnectionStatus.InitiatedConnect;
|
||||
conn.m_localHailMessage = hailMessage;
|
||||
|
||||
// handle on network thread
|
||||
conn.m_connectRequested = true;
|
||||
conn.m_connectionInitiator = true;
|
||||
|
||||
m_handshakes.Add(remoteEndPoint, conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send raw bytes; only used for debugging
|
||||
/// </summary>
|
||||
#if DEBUG
|
||||
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
|
||||
#else
|
||||
internal void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
|
||||
#endif
|
||||
{
|
||||
// wrong thread - this miiiight crash with network thread... but what's a boy to do.
|
||||
Array.Copy(arr, offset, m_sendBuffer, 0, length);
|
||||
bool unused;
|
||||
SendPacket(length, destination, 1, out unused);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,471 +1,471 @@
|
||||
/* 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 modify the NetPeerConfiguration after it has been used to initialize a NetPeer";
|
||||
|
||||
private bool m_isLocked;
|
||||
private readonly string m_appIdentifier;
|
||||
private string m_networkThreadName;
|
||||
private IPAddress m_localAddress;
|
||||
private IPAddress m_broadcastAddress;
|
||||
internal bool m_acceptIncomingConnections;
|
||||
internal int m_maximumConnections;
|
||||
internal int m_defaultOutgoingMessageCapacity;
|
||||
internal float m_pingInterval;
|
||||
internal bool m_useMessageRecycling;
|
||||
internal float m_connectionTimeout;
|
||||
internal bool m_enableUPnP;
|
||||
internal bool m_autoFlushSendQueue;
|
||||
|
||||
internal NetIncomingMessageType m_disabledTypes;
|
||||
internal int m_port;
|
||||
internal int m_receiveBufferSize;
|
||||
internal int m_sendBufferSize;
|
||||
internal float m_resendHandshakeInterval;
|
||||
internal int m_maximumHandshakeAttempts;
|
||||
|
||||
// bad network simulation
|
||||
internal float m_loss;
|
||||
internal float m_duplicates;
|
||||
internal float m_minimumOneWayLatency;
|
||||
internal float m_randomOneWayLatency;
|
||||
|
||||
// MTU
|
||||
internal int m_maximumTransmissionUnit;
|
||||
internal bool m_autoExpandMTU;
|
||||
internal float m_expandMTUFrequency;
|
||||
internal int m_expandMTUFailAttempts;
|
||||
|
||||
/// <summary>
|
||||
/// NetPeerConfiguration constructor
|
||||
/// </summary>
|
||||
public NetPeerConfiguration(string appIdentifier)
|
||||
{
|
||||
if (string.IsNullOrEmpty(appIdentifier))
|
||||
throw new NetException("App identifier must be at least one character long");
|
||||
m_appIdentifier = appIdentifier.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
//
|
||||
// default values
|
||||
//
|
||||
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage | NetIncomingMessageType.ConnectionLatencyUpdated;
|
||||
m_networkThreadName = "Lidgren network thread";
|
||||
m_localAddress = IPAddress.Any;
|
||||
m_broadcastAddress = IPAddress.Broadcast;
|
||||
var ip = NetUtility.GetBroadcastAddress();
|
||||
if (ip != null)
|
||||
{
|
||||
m_broadcastAddress = ip;
|
||||
}
|
||||
m_port = 0;
|
||||
m_receiveBufferSize = 131071;
|
||||
m_sendBufferSize = 131071;
|
||||
m_acceptIncomingConnections = false;
|
||||
m_maximumConnections = 32;
|
||||
m_defaultOutgoingMessageCapacity = 16;
|
||||
m_pingInterval = 4.0f;
|
||||
m_connectionTimeout = 25.0f;
|
||||
m_useMessageRecycling = true;
|
||||
m_resendHandshakeInterval = 3.0f;
|
||||
m_maximumHandshakeAttempts = 5;
|
||||
m_autoFlushSendQueue = true;
|
||||
|
||||
// Maximum transmission unit
|
||||
// Ethernet can take 1500 bytes of payload, so lets stay below that.
|
||||
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
|
||||
// -20 bytes IP header
|
||||
// -8 bytes UDP header
|
||||
// -4 bytes to be on the safe side and align to 8-byte boundary
|
||||
// Total 1408 bytes
|
||||
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
|
||||
m_maximumTransmissionUnit = 1408;
|
||||
m_autoExpandMTU = false;
|
||||
m_expandMTUFrequency = 2.0f;
|
||||
m_expandMTUFailAttempts = 5;
|
||||
|
||||
m_loss = 0.0f;
|
||||
m_minimumOneWayLatency = 0.0f;
|
||||
m_randomOneWayLatency = 0.0f;
|
||||
m_duplicates = 0.0f;
|
||||
|
||||
m_isLocked = false;
|
||||
}
|
||||
|
||||
internal void Lock()
|
||||
{
|
||||
m_isLocked = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets 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 type)
|
||||
{
|
||||
m_disabledTypes &= (~type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables receiving of the specified type of message
|
||||
/// </summary>
|
||||
public void DisableMessageType(NetIncomingMessageType type)
|
||||
{
|
||||
m_disabledTypes |= type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables receiving of the specified type of message
|
||||
/// </summary>
|
||||
public void SetMessageTypeEnabled(NetIncomingMessageType type, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
m_disabledTypes &= (~type);
|
||||
else
|
||||
m_disabledTypes |= type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if receiving of the specified type of message is enabled
|
||||
/// </summary>
|
||||
public bool IsMessageTypeEnabled(NetIncomingMessageType type)
|
||||
{
|
||||
return !((m_disabledTypes & type) == type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public string NetworkThreadName
|
||||
{
|
||||
get { return m_networkThreadName; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException("NetworkThreadName may not be set after the NetPeer which uses the configuration has been started");
|
||||
m_networkThreadName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 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 the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int MaximumTransmissionUnit
|
||||
{
|
||||
get { return m_maximumTransmissionUnit; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
if (value < 1 || value >= ((ushort.MaxValue + 1) / 8))
|
||||
throw new NetException("MaximumTransmissionUnit must be between 1 and " + (((ushort.MaxValue + 1) / 8) - 1) + " bytes");
|
||||
m_maximumTransmissionUnit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 time between latency calculating pings
|
||||
/// </summary>
|
||||
public float PingInterval
|
||||
{
|
||||
get { return m_pingInterval; }
|
||||
set { m_pingInterval = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the library should recycling messages to avoid excessive garbage collection. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public bool UseMessageRecycling
|
||||
{
|
||||
get { return m_useMessageRecycling; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_useMessageRecycling = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong
|
||||
/// </summary>
|
||||
public float ConnectionTimeout
|
||||
{
|
||||
get { return m_connectionTimeout; }
|
||||
set
|
||||
{
|
||||
if (value < m_pingInterval)
|
||||
throw new NetException("Connection timeout cannot be lower than ping interval!");
|
||||
m_connectionTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables UPnP support; enabling port forwarding and getting external ip
|
||||
/// </summary>
|
||||
public bool EnableUPnP
|
||||
{
|
||||
get { return m_enableUPnP; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_enableUPnP = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables automatic flushing of the send queue. If disabled, you must manully call NetPeer.FlushSendQueue() to flush sent messages to network.
|
||||
/// </summary>
|
||||
public bool AutoFlushSendQueue
|
||||
{
|
||||
get { return m_autoFlushSendQueue; }
|
||||
set { m_autoFlushSendQueue = 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 broadcast address to use when broadcasting
|
||||
/// </summary>
|
||||
public IPAddress BroadcastAddress
|
||||
{
|
||||
get { return m_broadcastAddress; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_broadcastAddress = 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 if the NetPeer should accept incoming connections. This is automatically set to true in NetServer and false in NetClient.
|
||||
/// </summary>
|
||||
public bool AcceptIncomingConnections
|
||||
{
|
||||
get { return m_acceptIncomingConnections; }
|
||||
set { m_acceptIncomingConnections = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds between handshake attempts
|
||||
/// </summary>
|
||||
public float ResendHandshakeInterval
|
||||
{
|
||||
get { return m_resendHandshakeInterval; }
|
||||
set { m_resendHandshakeInterval = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of handshake attempts before failing to connect
|
||||
/// </summary>
|
||||
public int MaximumHandshakeAttempts
|
||||
{
|
||||
get { return m_maximumHandshakeAttempts; }
|
||||
set
|
||||
{
|
||||
if (value < 1)
|
||||
throw new NetException("MaximumHandshakeAttempts must be at least 1");
|
||||
m_maximumHandshakeAttempts = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the NetPeer should send large messages to try to expand the maximum transmission unit size
|
||||
/// </summary>
|
||||
public bool AutoExpandMTU
|
||||
{
|
||||
get { return m_autoExpandMTU; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_autoExpandMTU = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how often to send large messages to expand MTU if AutoExpandMTU is enabled
|
||||
/// </summary>
|
||||
public float ExpandMTUFrequency
|
||||
{
|
||||
get { return m_expandMTUFrequency; }
|
||||
set { m_expandMTUFrequency = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of failed expand mtu attempts to perform before setting final MTU
|
||||
/// </summary>
|
||||
public int ExpandMTUFailAttempts
|
||||
{
|
||||
get { return m_expandMTUFailAttempts; }
|
||||
set { m_expandMTUFailAttempts = value; }
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f
|
||||
/// </summary>
|
||||
public float SimulatedLoss
|
||||
{
|
||||
get { return m_loss; }
|
||||
set { m_loss = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the 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>
|
||||
/// Creates a memberwise shallow clone of this configuration
|
||||
/// </summary>
|
||||
public NetPeerConfiguration Clone()
|
||||
{
|
||||
NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration;
|
||||
retval.m_isLocked = false;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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 modify the NetPeerConfiguration after it has been used to initialize a NetPeer";
|
||||
|
||||
private bool m_isLocked;
|
||||
private readonly string m_appIdentifier;
|
||||
private string m_networkThreadName;
|
||||
private IPAddress m_localAddress;
|
||||
private IPAddress m_broadcastAddress;
|
||||
internal bool m_acceptIncomingConnections;
|
||||
internal int m_maximumConnections;
|
||||
internal int m_defaultOutgoingMessageCapacity;
|
||||
internal float m_pingInterval;
|
||||
internal bool m_useMessageRecycling;
|
||||
internal float m_connectionTimeout;
|
||||
internal bool m_enableUPnP;
|
||||
internal bool m_autoFlushSendQueue;
|
||||
|
||||
internal NetIncomingMessageType m_disabledTypes;
|
||||
internal int m_port;
|
||||
internal int m_receiveBufferSize;
|
||||
internal int m_sendBufferSize;
|
||||
internal float m_resendHandshakeInterval;
|
||||
internal int m_maximumHandshakeAttempts;
|
||||
|
||||
// bad network simulation
|
||||
internal float m_loss;
|
||||
internal float m_duplicates;
|
||||
internal float m_minimumOneWayLatency;
|
||||
internal float m_randomOneWayLatency;
|
||||
|
||||
// MTU
|
||||
internal int m_maximumTransmissionUnit;
|
||||
internal bool m_autoExpandMTU;
|
||||
internal float m_expandMTUFrequency;
|
||||
internal int m_expandMTUFailAttempts;
|
||||
|
||||
/// <summary>
|
||||
/// NetPeerConfiguration constructor
|
||||
/// </summary>
|
||||
public NetPeerConfiguration(string appIdentifier)
|
||||
{
|
||||
if (string.IsNullOrEmpty(appIdentifier))
|
||||
throw new NetException("App identifier must be at least one character long");
|
||||
m_appIdentifier = appIdentifier.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||
|
||||
//
|
||||
// default values
|
||||
//
|
||||
m_disabledTypes = NetIncomingMessageType.ConnectionApproval | NetIncomingMessageType.UnconnectedData | NetIncomingMessageType.VerboseDebugMessage | NetIncomingMessageType.ConnectionLatencyUpdated;
|
||||
m_networkThreadName = "Lidgren network thread";
|
||||
m_localAddress = IPAddress.Any;
|
||||
m_broadcastAddress = IPAddress.Broadcast;
|
||||
var ip = NetUtility.GetBroadcastAddress();
|
||||
if (ip != null)
|
||||
{
|
||||
m_broadcastAddress = ip;
|
||||
}
|
||||
m_port = 0;
|
||||
m_receiveBufferSize = 131071;
|
||||
m_sendBufferSize = 131071;
|
||||
m_acceptIncomingConnections = false;
|
||||
m_maximumConnections = 32;
|
||||
m_defaultOutgoingMessageCapacity = 16;
|
||||
m_pingInterval = 4.0f;
|
||||
m_connectionTimeout = 25.0f;
|
||||
m_useMessageRecycling = true;
|
||||
m_resendHandshakeInterval = 3.0f;
|
||||
m_maximumHandshakeAttempts = 5;
|
||||
m_autoFlushSendQueue = true;
|
||||
|
||||
// Maximum transmission unit
|
||||
// Ethernet can take 1500 bytes of payload, so lets stay below that.
|
||||
// The aim is for a max full packet to be 1440 bytes (30 x 48 bytes, lower than 1468)
|
||||
// -20 bytes IP header
|
||||
// -8 bytes UDP header
|
||||
// -4 bytes to be on the safe side and align to 8-byte boundary
|
||||
// Total 1408 bytes
|
||||
// Note that lidgren headers (5 bytes) are not included here; since it's part of the "mtu payload"
|
||||
m_maximumTransmissionUnit = 1408;
|
||||
m_autoExpandMTU = false;
|
||||
m_expandMTUFrequency = 2.0f;
|
||||
m_expandMTUFailAttempts = 5;
|
||||
|
||||
m_loss = 0.0f;
|
||||
m_minimumOneWayLatency = 0.0f;
|
||||
m_randomOneWayLatency = 0.0f;
|
||||
m_duplicates = 0.0f;
|
||||
|
||||
m_isLocked = false;
|
||||
}
|
||||
|
||||
internal void Lock()
|
||||
{
|
||||
m_isLocked = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets 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 type)
|
||||
{
|
||||
m_disabledTypes &= (~type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables receiving of the specified type of message
|
||||
/// </summary>
|
||||
public void DisableMessageType(NetIncomingMessageType type)
|
||||
{
|
||||
m_disabledTypes |= type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables receiving of the specified type of message
|
||||
/// </summary>
|
||||
public void SetMessageTypeEnabled(NetIncomingMessageType type, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
m_disabledTypes &= (~type);
|
||||
else
|
||||
m_disabledTypes |= type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets if receiving of the specified type of message is enabled
|
||||
/// </summary>
|
||||
public bool IsMessageTypeEnabled(NetIncomingMessageType type)
|
||||
{
|
||||
return !((m_disabledTypes & type) == type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the library network thread. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public string NetworkThreadName
|
||||
{
|
||||
get { return m_networkThreadName; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException("NetworkThreadName may not be set after the NetPeer which uses the configuration has been started");
|
||||
m_networkThreadName = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets 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 the maximum amount of bytes to send in a single packet, excluding ip, udp and lidgren headers. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public int MaximumTransmissionUnit
|
||||
{
|
||||
get { return m_maximumTransmissionUnit; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
if (value < 1 || value >= ((ushort.MaxValue + 1) / 8))
|
||||
throw new NetException("MaximumTransmissionUnit must be between 1 and " + (((ushort.MaxValue + 1) / 8) - 1) + " bytes");
|
||||
m_maximumTransmissionUnit = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 time between latency calculating pings
|
||||
/// </summary>
|
||||
public float PingInterval
|
||||
{
|
||||
get { return m_pingInterval; }
|
||||
set { m_pingInterval = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the library should recycling messages to avoid excessive garbage collection. Cannot be changed once NetPeer is initialized.
|
||||
/// </summary>
|
||||
public bool UseMessageRecycling
|
||||
{
|
||||
get { return m_useMessageRecycling; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_useMessageRecycling = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds timeout will be postponed on a successful ping/pong
|
||||
/// </summary>
|
||||
public float ConnectionTimeout
|
||||
{
|
||||
get { return m_connectionTimeout; }
|
||||
set
|
||||
{
|
||||
if (value < m_pingInterval)
|
||||
throw new NetException("Connection timeout cannot be lower than ping interval!");
|
||||
m_connectionTimeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables UPnP support; enabling port forwarding and getting external ip
|
||||
/// </summary>
|
||||
public bool EnableUPnP
|
||||
{
|
||||
get { return m_enableUPnP; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_enableUPnP = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables automatic flushing of the send queue. If disabled, you must manully call NetPeer.FlushSendQueue() to flush sent messages to network.
|
||||
/// </summary>
|
||||
public bool AutoFlushSendQueue
|
||||
{
|
||||
get { return m_autoFlushSendQueue; }
|
||||
set { m_autoFlushSendQueue = 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 broadcast address to use when broadcasting
|
||||
/// </summary>
|
||||
public IPAddress BroadcastAddress
|
||||
{
|
||||
get { return m_broadcastAddress; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_broadcastAddress = 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 if the NetPeer should accept incoming connections. This is automatically set to true in NetServer and false in NetClient.
|
||||
/// </summary>
|
||||
public bool AcceptIncomingConnections
|
||||
{
|
||||
get { return m_acceptIncomingConnections; }
|
||||
set { m_acceptIncomingConnections = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of seconds between handshake attempts
|
||||
/// </summary>
|
||||
public float ResendHandshakeInterval
|
||||
{
|
||||
get { return m_resendHandshakeInterval; }
|
||||
set { m_resendHandshakeInterval = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum number of handshake attempts before failing to connect
|
||||
/// </summary>
|
||||
public int MaximumHandshakeAttempts
|
||||
{
|
||||
get { return m_maximumHandshakeAttempts; }
|
||||
set
|
||||
{
|
||||
if (value < 1)
|
||||
throw new NetException("MaximumHandshakeAttempts must be at least 1");
|
||||
m_maximumHandshakeAttempts = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the NetPeer should send large messages to try to expand the maximum transmission unit size
|
||||
/// </summary>
|
||||
public bool AutoExpandMTU
|
||||
{
|
||||
get { return m_autoExpandMTU; }
|
||||
set
|
||||
{
|
||||
if (m_isLocked)
|
||||
throw new NetException(c_isLockedMessage);
|
||||
m_autoExpandMTU = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets how often to send large messages to expand MTU if AutoExpandMTU is enabled
|
||||
/// </summary>
|
||||
public float ExpandMTUFrequency
|
||||
{
|
||||
get { return m_expandMTUFrequency; }
|
||||
set { m_expandMTUFrequency = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the number of failed expand mtu attempts to perform before setting final MTU
|
||||
/// </summary>
|
||||
public int ExpandMTUFailAttempts
|
||||
{
|
||||
get { return m_expandMTUFailAttempts; }
|
||||
set { m_expandMTUFailAttempts = value; }
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Gets or sets the simulated amount of sent packets lost from 0.0f to 1.0f
|
||||
/// </summary>
|
||||
public float SimulatedLoss
|
||||
{
|
||||
get { return m_loss; }
|
||||
set { m_loss = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the 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>
|
||||
/// Creates a memberwise shallow clone of this configuration
|
||||
/// </summary>
|
||||
public NetPeerConfiguration Clone()
|
||||
{
|
||||
NetPeerConfiguration retval = this.MemberwiseClone() as NetPeerConfiguration;
|
||||
retval.m_isLocked = false;
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,161 +1,161 @@
|
||||
/* 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.
|
||||
|
||||
*/
|
||||
|
||||
// Uncomment the line below to get statistics in RELEASE builds
|
||||
#define USE_RELEASE_STATISTICS
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Statistics for a NetPeer instance
|
||||
/// </summary>
|
||||
public sealed class NetPeerStatistics
|
||||
{
|
||||
private readonly 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 StorageBytesAllocated { get { return m_bytesAllocated; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes in the recycled pool
|
||||
/// </summary>
|
||||
public int BytesInRecyclePool { get { return m_peer.m_storagePoolBytes; } }
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections");
|
||||
#if DEBUG || USE_RELEASE_STATISTICS
|
||||
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");
|
||||
#else
|
||||
bdr.AppendLine("Sent (n/a) bytes in (n/a) messages in (n/a) packets");
|
||||
bdr.AppendLine("Received (n/a) bytes in (n/a) messages in (n/a) packets");
|
||||
#endif
|
||||
bdr.AppendLine("Storage allocated " + m_bytesAllocated + " bytes");
|
||||
bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes");
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.
|
||||
|
||||
*/
|
||||
|
||||
// Uncomment the line below to get statistics in RELEASE builds
|
||||
#define USE_RELEASE_STATISTICS
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Statistics for a NetPeer instance
|
||||
/// </summary>
|
||||
public sealed class NetPeerStatistics
|
||||
{
|
||||
private readonly 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 StorageBytesAllocated { get { return m_bytesAllocated; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of bytes in the recycled pool
|
||||
/// </summary>
|
||||
public int BytesInRecyclePool { get { return m_peer.m_storagePoolBytes; } }
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketSent(int numBytes, int numMessages)
|
||||
{
|
||||
m_sentPackets++;
|
||||
m_sentBytes += numBytes;
|
||||
m_sentMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if USE_RELEASE_STATISTICS
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#else
|
||||
[Conditional("DEBUG")]
|
||||
internal void PacketReceived(int numBytes, int numMessages)
|
||||
{
|
||||
m_receivedPackets++;
|
||||
m_receivedBytes += numBytes;
|
||||
m_receivedMessages += numMessages;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
StringBuilder bdr = new StringBuilder();
|
||||
bdr.AppendLine(m_peer.ConnectionsCount.ToString() + " connections");
|
||||
#if DEBUG || USE_RELEASE_STATISTICS
|
||||
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");
|
||||
#else
|
||||
bdr.AppendLine("Sent (n/a) bytes in (n/a) messages in (n/a) packets");
|
||||
bdr.AppendLine("Received (n/a) bytes in (n/a) messages in (n/a) packets");
|
||||
#endif
|
||||
bdr.AppendLine("Storage allocated " + m_bytesAllocated + " bytes");
|
||||
bdr.AppendLine("Recycled pool " + m_peer.m_storagePoolBytes + " bytes");
|
||||
return bdr.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
/* 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 for a NetPeer instance
|
||||
/// </summary>
|
||||
public enum NetPeerStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// NetPeer is not running; socket is not bound
|
||||
/// </summary>
|
||||
NotRunning = 0,
|
||||
|
||||
/// <summary>
|
||||
/// NetPeer is in the process of starting up
|
||||
/// </summary>
|
||||
Starting = 1,
|
||||
|
||||
/// <summary>
|
||||
/// NetPeer is bound to socket and listening for packets
|
||||
/// </summary>
|
||||
Running = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Shutdown has been requested and will be executed shortly
|
||||
/// </summary>
|
||||
ShutdownRequested = 3,
|
||||
}
|
||||
}
|
||||
/* 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 for a NetPeer instance
|
||||
/// </summary>
|
||||
public enum NetPeerStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// NetPeer is not running; socket is not bound
|
||||
/// </summary>
|
||||
NotRunning = 0,
|
||||
|
||||
/// <summary>
|
||||
/// NetPeer is in the process of starting up
|
||||
/// </summary>
|
||||
Starting = 1,
|
||||
|
||||
/// <summary>
|
||||
/// NetPeer is bound to socket and listening for packets
|
||||
/// </summary>
|
||||
Running = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Shutdown has been requested and will be executed shortly
|
||||
/// </summary>
|
||||
ShutdownRequested = 3,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,283 +1,283 @@
|
||||
/* 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.Collections.Generic;
|
||||
|
||||
// @TODO: examine performance characteristics of using SpinLock when using .Net 4.0
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Thread safe (blocking) expanding queue with TryDequeue() and EnqueueFirst()
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Count={Count} Capacity={Capacity}")]
|
||||
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 readonly object m_lock;
|
||||
private int m_size;
|
||||
private int m_head;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in the queue
|
||||
/// </summary>
|
||||
public int Count { get { return m_size; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current capacity for the queue
|
||||
/// </summary>
|
||||
public int Capacity { get { return m_items.Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// NetQueue constructor
|
||||
/// </summary>
|
||||
public NetQueue(int initialCapacity)
|
||||
{
|
||||
m_lock = new object();
|
||||
m_items = new T[initialCapacity];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item last/tail of the queue
|
||||
/// </summary>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
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>
|
||||
/// Adds an item last/tail of the queue
|
||||
/// </summary>
|
||||
public void Enqueue(IEnumerable<T> items)
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (m_size == m_items.Length)
|
||||
SetCapacity(m_items.Length + 8); // @TODO move this out of loop
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
// must be called from within a lock(m_lock) !
|
||||
private void SetCapacity(int newCapacity)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
m_items = new T[newCapacity];
|
||||
m_head = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
T[] newItems = new T[newCapacity];
|
||||
|
||||
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 bool TryDequeue(out T item)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
item = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
item = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
item = m_items[m_head];
|
||||
m_items[m_head] = default(T);
|
||||
|
||||
m_head = (m_head + 1) % m_items.Length;
|
||||
m_size--;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from the head of the queue, or returns default(T) if empty
|
||||
/// </summary>
|
||||
public int TryDrain(IList<T> addTo)
|
||||
{
|
||||
if (m_size == 0)
|
||||
return 0;
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
int added = m_size;
|
||||
while (m_size > 0)
|
||||
{
|
||||
var item = m_items[m_head];
|
||||
addTo.Add(item);
|
||||
|
||||
m_items[m_head] = default(T);
|
||||
m_head = (m_head + 1) % m_items.Length;
|
||||
m_size--;
|
||||
}
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns default(T) if queue is empty
|
||||
/// </summary>
|
||||
public T TryPeek(int offset)
|
||||
{
|
||||
if (m_size == 0)
|
||||
return default(T);
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == 0)
|
||||
return default(T);
|
||||
return m_items[(m_head + offset) % m_items.Length];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an item is in the queue
|
||||
/// </summary>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
int ptr = m_head;
|
||||
for (int i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_items[ptr] == null)
|
||||
{
|
||||
if (item == null)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_items[ptr].Equals(item))
|
||||
return true;
|
||||
}
|
||||
ptr = (ptr + 1) % m_items.Length;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the queue items to a new array
|
||||
/// </summary>
|
||||
public T[] ToArray()
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
T[] retval = new T[m_size];
|
||||
int ptr = m_head;
|
||||
for (int i = 0; i < m_size; i++)
|
||||
{
|
||||
retval[i] = m_items[ptr++];
|
||||
if (ptr >= m_items.Length)
|
||||
ptr = 0;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all objects from the queue
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
for (int i = 0; i < m_items.Length; i++)
|
||||
m_items[i] = default(T);
|
||||
m_head = 0;
|
||||
m_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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.Collections.Generic;
|
||||
|
||||
// @TODO: examine performance characteristics of using SpinLock when using .Net 4.0
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Thread safe (blocking) expanding queue with TryDequeue() and EnqueueFirst()
|
||||
/// </summary>
|
||||
[DebuggerDisplay("Count={Count} Capacity={Capacity}")]
|
||||
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 readonly object m_lock;
|
||||
private int m_size;
|
||||
private int m_head;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in the queue
|
||||
/// </summary>
|
||||
public int Count { get { return m_size; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current capacity for the queue
|
||||
/// </summary>
|
||||
public int Capacity { get { return m_items.Length; } }
|
||||
|
||||
/// <summary>
|
||||
/// NetQueue constructor
|
||||
/// </summary>
|
||||
public NetQueue(int initialCapacity)
|
||||
{
|
||||
m_lock = new object();
|
||||
m_items = new T[initialCapacity];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item last/tail of the queue
|
||||
/// </summary>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
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>
|
||||
/// Adds an item last/tail of the queue
|
||||
/// </summary>
|
||||
public void Enqueue(IEnumerable<T> items)
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (m_size == m_items.Length)
|
||||
SetCapacity(m_items.Length + 8); // @TODO move this out of loop
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
// must be called from within a lock(m_lock) !
|
||||
private void SetCapacity(int newCapacity)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
m_items = new T[newCapacity];
|
||||
m_head = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
T[] newItems = new T[newCapacity];
|
||||
|
||||
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 bool TryDequeue(out T item)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
item = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
item = default(T);
|
||||
return false;
|
||||
}
|
||||
|
||||
item = m_items[m_head];
|
||||
m_items[m_head] = default(T);
|
||||
|
||||
m_head = (m_head + 1) % m_items.Length;
|
||||
m_size--;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an item from the head of the queue, or returns default(T) if empty
|
||||
/// </summary>
|
||||
public int TryDrain(IList<T> addTo)
|
||||
{
|
||||
if (m_size == 0)
|
||||
return 0;
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
int added = m_size;
|
||||
while (m_size > 0)
|
||||
{
|
||||
var item = m_items[m_head];
|
||||
addTo.Add(item);
|
||||
|
||||
m_items[m_head] = default(T);
|
||||
m_head = (m_head + 1) % m_items.Length;
|
||||
m_size--;
|
||||
}
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns default(T) if queue is empty
|
||||
/// </summary>
|
||||
public T TryPeek(int offset)
|
||||
{
|
||||
if (m_size == 0)
|
||||
return default(T);
|
||||
|
||||
lock (m_lock)
|
||||
{
|
||||
if (m_size == 0)
|
||||
return default(T);
|
||||
return m_items[(m_head + offset) % m_items.Length];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether an item is in the queue
|
||||
/// </summary>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
int ptr = m_head;
|
||||
for (int i = 0; i < m_size; i++)
|
||||
{
|
||||
if (m_items[ptr] == null)
|
||||
{
|
||||
if (item == null)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_items[ptr].Equals(item))
|
||||
return true;
|
||||
}
|
||||
ptr = (ptr + 1) % m_items.Length;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the queue items to a new array
|
||||
/// </summary>
|
||||
public T[] ToArray()
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
T[] retval = new T[m_size];
|
||||
int ptr = m_head;
|
||||
for (int i = 0; i < m_size; i++)
|
||||
{
|
||||
retval[i] = m_items[ptr++];
|
||||
if (ptr >= m_items.Length)
|
||||
ptr = 0;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all objects from the queue
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (m_lock)
|
||||
{
|
||||
for (int i = 0; i < m_items.Length; i++)
|
||||
m_items[i] = default(T);
|
||||
m_head = 0;
|
||||
m_size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
281
Lidgren.Network/NetRandom.Implementations.cs
Normal file
281
Lidgren.Network/NetRandom.Implementations.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Multiply With Carry random
|
||||
/// </summary>
|
||||
public class MWCRandom : NetRandom
|
||||
{
|
||||
/// <summary>
|
||||
/// Get global instance of MWCRandom
|
||||
/// </summary>
|
||||
public static new readonly MWCRandom Instance = new MWCRandom();
|
||||
|
||||
private uint m_w, m_z;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with randomized seed
|
||||
/// </summary>
|
||||
public MWCRandom()
|
||||
{
|
||||
Initialize(NetRandomSeed.GetUInt64());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Re)initialize this instance with provided 32 bit seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override void Initialize(uint seed)
|
||||
{
|
||||
m_w = seed;
|
||||
m_z = seed * 16777619;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Re)initialize this instance with provided 64 bit seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public void Initialize(ulong seed)
|
||||
{
|
||||
m_w = (uint)seed;
|
||||
m_z = (uint)(seed >> 32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override uint NextUInt32()
|
||||
{
|
||||
m_z = 36969 * (m_z & 65535) + (m_z >> 16);
|
||||
m_w = 18000 * (m_w & 65535) + (m_w >> 16);
|
||||
return ((m_z << 16) + m_w);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Xor Shift based random
|
||||
/// </summary>
|
||||
public sealed class XorShiftRandom : NetRandom
|
||||
{
|
||||
/// <summary>
|
||||
/// Get global instance of XorShiftRandom
|
||||
/// </summary>
|
||||
public static new readonly XorShiftRandom Instance = new XorShiftRandom();
|
||||
|
||||
private const uint c_x = 123456789;
|
||||
private const uint c_y = 362436069;
|
||||
private const uint c_z = 521288629;
|
||||
private const uint c_w = 88675123;
|
||||
|
||||
private uint m_x, m_y, m_z, m_w;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with randomized seed
|
||||
/// </summary>
|
||||
public XorShiftRandom()
|
||||
{
|
||||
Initialize(NetRandomSeed.GetUInt64());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with provided 64 bit seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public XorShiftRandom(ulong seed)
|
||||
{
|
||||
Initialize(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Re)initialize this instance with provided 32 bit seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override void Initialize(uint seed)
|
||||
{
|
||||
m_x = (uint)seed;
|
||||
m_y = c_y;
|
||||
m_z = c_z;
|
||||
m_w = c_w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Re)initialize this instance with provided 64 bit seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public void Initialize(ulong seed)
|
||||
{
|
||||
m_x = (uint)seed;
|
||||
m_y = c_y;
|
||||
m_z = (uint)(seed << 32);
|
||||
m_w = c_w;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override uint NextUInt32()
|
||||
{
|
||||
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>
|
||||
/// Mersenne Twister based random
|
||||
/// </summary>
|
||||
public sealed class MersenneTwisterRandom : NetRandom
|
||||
{
|
||||
/// <summary>
|
||||
/// Get global instance of MersenneTwisterRandom
|
||||
/// </summary>
|
||||
public static new readonly MersenneTwisterRandom Instance = new MersenneTwisterRandom();
|
||||
|
||||
private const int N = 624;
|
||||
private const int M = 397;
|
||||
private const uint MATRIX_A = 0x9908b0dfU;
|
||||
private const uint UPPER_MASK = 0x80000000U;
|
||||
private const uint LOWER_MASK = 0x7fffffffU;
|
||||
private const uint TEMPER1 = 0x9d2c5680U;
|
||||
private const uint TEMPER2 = 0xefc60000U;
|
||||
private const int TEMPER3 = 11;
|
||||
private const int TEMPER4 = 7;
|
||||
private const int TEMPER5 = 15;
|
||||
private const int TEMPER6 = 18;
|
||||
|
||||
private UInt32[] mt;
|
||||
private int mti;
|
||||
private UInt32[] mag01;
|
||||
|
||||
private const double c_realUnitInt = 1.0 / ((double)int.MaxValue + 1.0);
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with randomized seed
|
||||
/// </summary>
|
||||
public MersenneTwisterRandom()
|
||||
{
|
||||
Initialize(NetRandomSeed.GetUInt32());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with provided 32 bit seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public MersenneTwisterRandom(uint seed)
|
||||
{
|
||||
Initialize(seed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (Re)initialize this instance with provided 32 bit seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override void Initialize(uint seed)
|
||||
{
|
||||
mt = new UInt32[N];
|
||||
mti = N + 1;
|
||||
mag01 = new UInt32[] { 0x0U, MATRIX_A };
|
||||
mt[0] = seed;
|
||||
for (int i = 1; i < N; i++)
|
||||
mt[i] = (UInt32)(1812433253 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override uint NextUInt32()
|
||||
{
|
||||
UInt32 y;
|
||||
if (mti >= N)
|
||||
{
|
||||
GenRandAll();
|
||||
mti = 0;
|
||||
}
|
||||
y = mt[mti++];
|
||||
y ^= (y >> TEMPER3);
|
||||
y ^= (y << TEMPER4) & TEMPER1;
|
||||
y ^= (y << TEMPER5) & TEMPER2;
|
||||
y ^= (y >> TEMPER6);
|
||||
return y;
|
||||
}
|
||||
|
||||
private void GenRandAll()
|
||||
{
|
||||
int kk = 1;
|
||||
UInt32 y;
|
||||
UInt32 p;
|
||||
y = mt[0] & UPPER_MASK;
|
||||
do
|
||||
{
|
||||
p = mt[kk];
|
||||
mt[kk - 1] = mt[kk + (M - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
|
||||
y = p & UPPER_MASK;
|
||||
} while (++kk < N - M + 1);
|
||||
do
|
||||
{
|
||||
p = mt[kk];
|
||||
mt[kk - 1] = mt[kk + (M - N - 1)] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
|
||||
y = p & UPPER_MASK;
|
||||
} while (++kk < N);
|
||||
p = mt[0];
|
||||
mt[N - 1] = mt[M - 1] ^ ((y | (p & LOWER_MASK)) >> 1) ^ mag01[p & 1];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// RNGCryptoServiceProvider based random; very slow but cryptographically safe
|
||||
/// </summary>
|
||||
public class CryptoRandom : NetRandom
|
||||
{
|
||||
/// <summary>
|
||||
/// Global instance of CryptoRandom
|
||||
/// </summary>
|
||||
public static new readonly CryptoRandom Instance = new CryptoRandom();
|
||||
|
||||
private RandomNumberGenerator m_rnd = new RNGCryptoServiceProvider();
|
||||
|
||||
/// <summary>
|
||||
/// Seed in CryptoRandom does not create deterministic sequences
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override void Initialize(uint seed)
|
||||
{
|
||||
byte[] tmp = new byte[seed % 16];
|
||||
m_rnd.GetBytes(tmp); // just prime it
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random value from UInt32.MinValue to UInt32.MaxValue, inclusively
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public override uint NextUInt32()
|
||||
{
|
||||
var bytes = new byte[4];
|
||||
m_rnd.GetBytes(bytes);
|
||||
return (uint)bytes[0] | (((uint)bytes[1]) << 8) | (((uint)bytes[2]) << 16) | (((uint)bytes[3]) << 24);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill the specified buffer with random values
|
||||
/// </summary>
|
||||
public override void NextBytes(byte[] buffer)
|
||||
{
|
||||
m_rnd.GetBytes(buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills all bytes from offset to offset + length in buffer with random values
|
||||
/// </summary>
|
||||
public override void NextBytes(byte[] buffer, int offset, int length)
|
||||
{
|
||||
var bytes = new byte[length];
|
||||
m_rnd.GetBytes(bytes);
|
||||
Array.Copy(bytes, 0, buffer, offset, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,373 +1,373 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// A fast random number generator for .NET
|
||||
/// Colin Green, January 2005
|
||||
/// </summary>
|
||||
/// September 4th 2005
|
||||
/// Added NextBytesUnsafe() - commented out by default.
|
||||
/// Fixed bug in Reinitialise() - y,z and w variables were not being reset.
|
||||
///
|
||||
/// 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 8x faster, depending on which methods are called.
|
||||
///
|
||||
/// 3) Direct replacement for System.Random. This class implements all of the methods that System.Random
|
||||
/// does plus some additional methods. The like named methods are functionally equivalent.
|
||||
///
|
||||
/// 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.
|
||||
public class NetRandom
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a global NetRandom instance
|
||||
/// </summary>
|
||||
public static readonly NetRandom Instance = new NetRandom();
|
||||
|
||||
// The +1 ensures NextDouble doesn't generate 1.0
|
||||
const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
|
||||
const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
|
||||
const uint Y = 842502087, Z = 3579807591, W = 273326509;
|
||||
|
||||
private static int s_extraSeed = 42;
|
||||
|
||||
uint x, y, z, w;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance using time dependent seed.
|
||||
/// </summary>
|
||||
public NetRandom()
|
||||
{
|
||||
// Initialise using the system tick count.
|
||||
Reinitialise(GetSeed(this));
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Create a semi-random seed based on an object
|
||||
/// </summary>
|
||||
public int GetSeed(object forObject)
|
||||
{
|
||||
// mix some semi-random properties
|
||||
int seed = (int)Environment.TickCount;
|
||||
seed ^= forObject.GetHashCode();
|
||||
//seed ^= (int)(Stopwatch.GetTimestamp());
|
||||
//seed ^= (int)(Environment.WorkingSet); // will return 0 on mono
|
||||
|
||||
int extraSeed = System.Threading.Interlocked.Increment(ref s_extraSeed);
|
||||
|
||||
return seed + extraSeed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Reinitialisation]
|
||||
|
||||
/// <summary>
|
||||
/// Reinitialises using an int value as a seed.
|
||||
/// </summary>
|
||||
/// <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
|
||||
x = (uint)seed;
|
||||
y = Y;
|
||||
z = Z;
|
||||
w = W;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [System.Random functionally equivalent methods]
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to int.MaxValue-1.
|
||||
/// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next().
|
||||
/// This does slightly eat into some of the performance gain over System.Random, but not much.
|
||||
/// For better performance see:
|
||||
///
|
||||
/// Call NextInt() for an int over the range 0 to int.MaxValue.
|
||||
///
|
||||
/// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range
|
||||
/// including negative values.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int Next()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
// Handle the special case where the value int.MaxValue is generated. This is outside of
|
||||
// the range of permitted values, so we therefore call Next() to try again.
|
||||
uint rtn = w & 0x7FFFFFFF;
|
||||
if (rtn == 0x7FFFFFFF)
|
||||
return Next();
|
||||
return (int)rtn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
|
||||
/// </summary>
|
||||
/// <param name="upperBound"></param>
|
||||
/// <returns></returns>
|
||||
public int Next(int upperBound)
|
||||
{
|
||||
if (upperBound < 0)
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
|
||||
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
|
||||
/// upperBound must be >= lowerBound. lowerBound may be negative.
|
||||
/// </summary>
|
||||
/// <param name="lowerBound"></param>
|
||||
/// <param name="upperBound"></param>
|
||||
/// <returns></returns>
|
||||
public int Next(int lowerBound, int upperBound)
|
||||
{
|
||||
if (lowerBound > upperBound)
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
|
||||
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
int range = upperBound - lowerBound;
|
||||
if (range < 0)
|
||||
{ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
|
||||
// We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
|
||||
return lowerBound + (int)((REAL_UNIT_UINT * (double)(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
|
||||
}
|
||||
|
||||
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
|
||||
// a little more performance.
|
||||
return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double. Values returned are from 0.0 up to but not including 1.0.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public double NextDouble()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
// 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.
|
||||
//
|
||||
// Also note that the loss of one bit of precision is equivalent to what occurs within
|
||||
// System.Random.
|
||||
return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random single. Values returned are from 0.0 up to but not including 1.0.
|
||||
/// </summary>
|
||||
public float NextSingle()
|
||||
{
|
||||
return (float)NextDouble();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the provided byte array with random bytes.
|
||||
/// This method is functionally equivalent to System.Random.NextBytes().
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
public void NextBytes(byte[] buffer)
|
||||
{
|
||||
// Fill up the bulk of the buffer in chunks of 4 bytes at a time.
|
||||
uint x = this.x, y = this.y, z = this.z, w = this.w;
|
||||
int i = 0;
|
||||
uint t;
|
||||
for (int bound = buffer.Length - 3; i < bound; )
|
||||
{
|
||||
// Generate 4 bytes.
|
||||
// Increased performance is achieved by generating 4 random bytes per loop.
|
||||
// Also note that no mask needs to be applied to zero out the higher order bytes before
|
||||
// casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out.
|
||||
t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte)w;
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
buffer[i++] = (byte)(w >> 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;
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i] = (byte)(w >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.x = x; this.y = y; this.z = z; this.w = w;
|
||||
}
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// A version of NextBytes that uses a pointer to set 4 bytes of the byte buffer in one operation
|
||||
// /// thus providing a nice speedup. The loop is also partially unrolled to allow out-of-order-execution,
|
||||
// /// this results in about a x2 speedup on an AMD Athlon. Thus performance may vary wildly on different CPUs
|
||||
// /// depending on the number of execution units available.
|
||||
// ///
|
||||
// /// Another significant speedup is obtained by setting the 4 bytes by indexing pDWord (e.g. pDWord[i++]=w)
|
||||
// /// instead of adjusting it dereferencing it (e.g. *pDWord++=w).
|
||||
// ///
|
||||
// /// Note that this routine requires the unsafe compilation flag to be specified and so is commented out by default.
|
||||
// /// </summary>
|
||||
// /// <param name="buffer"></param>
|
||||
// public unsafe void NextBytesUnsafe(byte[] buffer)
|
||||
// {
|
||||
// if(buffer.Length % 8 != 0)
|
||||
// throw new ArgumentException("Buffer length must be divisible by 8", "buffer");
|
||||
//
|
||||
// uint x=this.x, y=this.y, z=this.z, w=this.w;
|
||||
//
|
||||
// fixed(byte* pByte0 = buffer)
|
||||
// {
|
||||
// uint* pDWord = (uint*)pByte0;
|
||||
// for(int i=0, len=buffer.Length>>2; i < len; i+=2)
|
||||
// {
|
||||
// uint t=(x^(x<<11));
|
||||
// x=y; y=z; z=w;
|
||||
// pDWord[i] = w = (w^(w>>19))^(t^(t>>8));
|
||||
//
|
||||
// t=(x^(x<<11));
|
||||
// x=y; y=z; z=w;
|
||||
// pDWord[i+1] = w = (w^(w>>19))^(t^(t>>8));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.x=x; this.y=y; this.z=z; this.w=w;
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Methods not present on System.Random]
|
||||
|
||||
/// <summary>
|
||||
/// Generates a uint. Values returned are over the full range of a uint,
|
||||
/// uint.MinValue to uint.MaxValue, inclusive.
|
||||
///
|
||||
/// This is the fastest method for generating a single random number because the underlying
|
||||
/// random number generator algorithm generates 32 random bits that can be cast directly to
|
||||
/// a uint.
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to int.MaxValue, inclusive.
|
||||
/// This method differs from Next() only in that the range is 0 to int.MaxValue
|
||||
/// and not 0 to int.MaxValue-1.
|
||||
///
|
||||
/// The slight difference in range means this method is slightly faster than Next()
|
||||
/// but is not functionally equivalent to System.Random.Next().
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int NextInt()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
|
||||
}
|
||||
|
||||
|
||||
// Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
|
||||
// with bitBufferIdx.
|
||||
uint bitBuffer;
|
||||
uint bitMask = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a single random bit.
|
||||
/// This method's performance is improved by generating 32 bits in one operation and storing them
|
||||
/// ready for future calls.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool NextBool()
|
||||
{
|
||||
if (bitMask == 1)
|
||||
{
|
||||
// Generate 32 more bits.
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
// Reset the bitMask that tells us which bit to read next.
|
||||
bitMask = 0x80000000;
|
||||
return (bitBuffer & bitMask) == 0;
|
||||
}
|
||||
|
||||
return (bitBuffer & (bitMask >>= 1)) == 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// A fast random number generator for .NET
|
||||
/// Colin Green, January 2005
|
||||
/// </summary>
|
||||
/// September 4th 2005
|
||||
/// Added NextBytesUnsafe() - commented out by default.
|
||||
/// Fixed bug in Reinitialise() - y,z and w variables were not being reset.
|
||||
///
|
||||
/// 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 8x faster, depending on which methods are called.
|
||||
///
|
||||
/// 3) Direct replacement for System.Random. This class implements all of the methods that System.Random
|
||||
/// does plus some additional methods. The like named methods are functionally equivalent.
|
||||
///
|
||||
/// 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.
|
||||
public class NetRandom
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a global NetRandom instance
|
||||
/// </summary>
|
||||
public static readonly NetRandom Instance = new NetRandom();
|
||||
|
||||
// The +1 ensures NextDouble doesn't generate 1.0
|
||||
const double REAL_UNIT_INT = 1.0 / ((double)int.MaxValue + 1.0);
|
||||
const double REAL_UNIT_UINT = 1.0 / ((double)uint.MaxValue + 1.0);
|
||||
const uint Y = 842502087, Z = 3579807591, W = 273326509;
|
||||
|
||||
private static int s_extraSeed = 42;
|
||||
|
||||
uint x, y, z, w;
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance using time dependent seed.
|
||||
/// </summary>
|
||||
public NetRandom()
|
||||
{
|
||||
// Initialise using the system tick count.
|
||||
Reinitialise(GetSeed(this));
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Create a semi-random seed based on an object
|
||||
/// </summary>
|
||||
public int GetSeed(object forObject)
|
||||
{
|
||||
// mix some semi-random properties
|
||||
int seed = (int)Environment.TickCount;
|
||||
seed ^= forObject.GetHashCode();
|
||||
//seed ^= (int)(Stopwatch.GetTimestamp());
|
||||
//seed ^= (int)(Environment.WorkingSet); // will return 0 on mono
|
||||
|
||||
int extraSeed = System.Threading.Interlocked.Increment(ref s_extraSeed);
|
||||
|
||||
return seed + extraSeed;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Reinitialisation]
|
||||
|
||||
/// <summary>
|
||||
/// Reinitialises using an int value as a seed.
|
||||
/// </summary>
|
||||
/// <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
|
||||
x = (uint)seed;
|
||||
y = Y;
|
||||
z = Z;
|
||||
w = W;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [System.Random functionally equivalent methods]
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to int.MaxValue-1.
|
||||
/// MaxValue is not generated in order to remain functionally equivalent to System.Random.Next().
|
||||
/// This does slightly eat into some of the performance gain over System.Random, but not much.
|
||||
/// For better performance see:
|
||||
///
|
||||
/// Call NextInt() for an int over the range 0 to int.MaxValue.
|
||||
///
|
||||
/// Call NextUInt() and cast the result to an int to generate an int over the full Int32 value range
|
||||
/// including negative values.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int Next()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
// Handle the special case where the value int.MaxValue is generated. This is outside of
|
||||
// the range of permitted values, so we therefore call Next() to try again.
|
||||
uint rtn = w & 0x7FFFFFFF;
|
||||
if (rtn == 0x7FFFFFFF)
|
||||
return Next();
|
||||
return (int)rtn;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to upperBound-1, and not including upperBound.
|
||||
/// </summary>
|
||||
/// <param name="upperBound"></param>
|
||||
/// <returns></returns>
|
||||
public int Next(int upperBound)
|
||||
{
|
||||
if (upperBound < 0)
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=0");
|
||||
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
return (int)((REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * upperBound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range lowerBound to upperBound-1, and not including upperBound.
|
||||
/// upperBound must be >= lowerBound. lowerBound may be negative.
|
||||
/// </summary>
|
||||
/// <param name="lowerBound"></param>
|
||||
/// <param name="upperBound"></param>
|
||||
/// <returns></returns>
|
||||
public int Next(int lowerBound, int upperBound)
|
||||
{
|
||||
if (lowerBound > upperBound)
|
||||
throw new ArgumentOutOfRangeException("upperBound", upperBound, "upperBound must be >=lowerBound");
|
||||
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// The explicit int cast before the first multiplication gives better performance.
|
||||
// See comments in NextDouble.
|
||||
int range = upperBound - lowerBound;
|
||||
if (range < 0)
|
||||
{ // If range is <0 then an overflow has occured and must resort to using long integer arithmetic instead (slower).
|
||||
// We also must use all 32 bits of precision, instead of the normal 31, which again is slower.
|
||||
return lowerBound + (int)((REAL_UNIT_UINT * (double)(w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))) * (double)((long)upperBound - (long)lowerBound));
|
||||
}
|
||||
|
||||
// 31 bits of precision will suffice if range<=int.MaxValue. This allows us to cast to an int and gain
|
||||
// a little more performance.
|
||||
return lowerBound + (int)((REAL_UNIT_INT * (double)(int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))))) * (double)range);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random double. Values returned are from 0.0 up to but not including 1.0.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public double NextDouble()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
|
||||
// Here we can gain a 2x speed improvement by generating a value that can be cast to
|
||||
// an int instead of the more easily available uint. If we then explicitly cast to an
|
||||
// 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.
|
||||
//
|
||||
// Also note that the loss of one bit of precision is equivalent to what occurs within
|
||||
// System.Random.
|
||||
return (REAL_UNIT_INT * (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random single. Values returned are from 0.0 up to but not including 1.0.
|
||||
/// </summary>
|
||||
public float NextSingle()
|
||||
{
|
||||
return (float)NextDouble();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the provided byte array with random bytes.
|
||||
/// This method is functionally equivalent to System.Random.NextBytes().
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
public void NextBytes(byte[] buffer)
|
||||
{
|
||||
// Fill up the bulk of the buffer in chunks of 4 bytes at a time.
|
||||
uint x = this.x, y = this.y, z = this.z, w = this.w;
|
||||
int i = 0;
|
||||
uint t;
|
||||
for (int bound = buffer.Length - 3; i < bound; )
|
||||
{
|
||||
// Generate 4 bytes.
|
||||
// Increased performance is achieved by generating 4 random bytes per loop.
|
||||
// Also note that no mask needs to be applied to zero out the higher order bytes before
|
||||
// casting because the cast ignores thos bytes. Thanks to Stefan Troschütz for pointing this out.
|
||||
t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
buffer[i++] = (byte)w;
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
buffer[i++] = (byte)(w >> 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;
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)(w >> 8);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i++] = (byte)(w >> 16);
|
||||
if (i < buffer.Length)
|
||||
{
|
||||
buffer[i] = (byte)(w >> 24);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.x = x; this.y = y; this.z = z; this.w = w;
|
||||
}
|
||||
|
||||
|
||||
// /// <summary>
|
||||
// /// A version of NextBytes that uses a pointer to set 4 bytes of the byte buffer in one operation
|
||||
// /// thus providing a nice speedup. The loop is also partially unrolled to allow out-of-order-execution,
|
||||
// /// this results in about a x2 speedup on an AMD Athlon. Thus performance may vary wildly on different CPUs
|
||||
// /// depending on the number of execution units available.
|
||||
// ///
|
||||
// /// Another significant speedup is obtained by setting the 4 bytes by indexing pDWord (e.g. pDWord[i++]=w)
|
||||
// /// instead of adjusting it dereferencing it (e.g. *pDWord++=w).
|
||||
// ///
|
||||
// /// Note that this routine requires the unsafe compilation flag to be specified and so is commented out by default.
|
||||
// /// </summary>
|
||||
// /// <param name="buffer"></param>
|
||||
// public unsafe void NextBytesUnsafe(byte[] buffer)
|
||||
// {
|
||||
// if(buffer.Length % 8 != 0)
|
||||
// throw new ArgumentException("Buffer length must be divisible by 8", "buffer");
|
||||
//
|
||||
// uint x=this.x, y=this.y, z=this.z, w=this.w;
|
||||
//
|
||||
// fixed(byte* pByte0 = buffer)
|
||||
// {
|
||||
// uint* pDWord = (uint*)pByte0;
|
||||
// for(int i=0, len=buffer.Length>>2; i < len; i+=2)
|
||||
// {
|
||||
// uint t=(x^(x<<11));
|
||||
// x=y; y=z; z=w;
|
||||
// pDWord[i] = w = (w^(w>>19))^(t^(t>>8));
|
||||
//
|
||||
// t=(x^(x<<11));
|
||||
// x=y; y=z; z=w;
|
||||
// pDWord[i+1] = w = (w^(w>>19))^(t^(t>>8));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// this.x=x; this.y=y; this.z=z; this.w=w;
|
||||
// }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods [Methods not present on System.Random]
|
||||
|
||||
/// <summary>
|
||||
/// Generates a uint. Values returned are over the full range of a uint,
|
||||
/// uint.MinValue to uint.MaxValue, inclusive.
|
||||
///
|
||||
/// This is the fastest method for generating a single random number because the underlying
|
||||
/// random number generator algorithm generates 32 random bits that can be cast directly to
|
||||
/// a uint.
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
return (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random int over the range 0 to int.MaxValue, inclusive.
|
||||
/// This method differs from Next() only in that the range is 0 to int.MaxValue
|
||||
/// and not 0 to int.MaxValue-1.
|
||||
///
|
||||
/// The slight difference in range means this method is slightly faster than Next()
|
||||
/// but is not functionally equivalent to System.Random.Next().
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int NextInt()
|
||||
{
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
return (int)(0x7FFFFFFF & (w = (w ^ (w >> 19)) ^ (t ^ (t >> 8))));
|
||||
}
|
||||
|
||||
|
||||
// Buffer 32 bits in bitBuffer, return 1 at a time, keep track of how many have been returned
|
||||
// with bitBufferIdx.
|
||||
uint bitBuffer;
|
||||
uint bitMask = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a single random bit.
|
||||
/// This method's performance is improved by generating 32 bits in one operation and storing them
|
||||
/// ready for future calls.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool NextBool()
|
||||
{
|
||||
if (bitMask == 1)
|
||||
{
|
||||
// Generate 32 more bits.
|
||||
uint t = (x ^ (x << 11));
|
||||
x = y; y = z; z = w;
|
||||
bitBuffer = w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
|
||||
|
||||
// Reset the bitMask that tells us which bit to read next.
|
||||
bitMask = 0x80000000;
|
||||
return (bitBuffer & bitMask) == 0;
|
||||
}
|
||||
|
||||
return (bitBuffer & (bitMask >>= 1)) == 0;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
45
Lidgren.Network/NetRandomSeed.cs
Normal file
45
Lidgren.Network/NetRandomSeed.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for generating random seeds
|
||||
/// </summary>
|
||||
public static class NetRandomSeed
|
||||
{
|
||||
private static int m_seedIncrement = -1640531527;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a 32 bit random seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public static uint GetUInt32()
|
||||
{
|
||||
ulong seed = GetUInt64();
|
||||
uint low = (uint)seed;
|
||||
uint high = (uint)(seed >> 32);
|
||||
return low ^ high;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a 64 bit random seed
|
||||
/// </summary>
|
||||
[CLSCompliant(false)]
|
||||
public static ulong GetUInt64()
|
||||
{
|
||||
var guidBytes = Guid.NewGuid().ToByteArray();
|
||||
ulong seed =
|
||||
((ulong)guidBytes[0] << (8 * 0)) |
|
||||
((ulong)guidBytes[1] << (8 * 1)) |
|
||||
((ulong)guidBytes[2] << (8 * 2)) |
|
||||
((ulong)guidBytes[3] << (8 * 3)) |
|
||||
((ulong)guidBytes[4] << (8 * 4)) |
|
||||
((ulong)guidBytes[5] << (8 * 5)) |
|
||||
((ulong)guidBytes[6] << (8 * 6)) |
|
||||
((ulong)guidBytes[7] << (8 * 7));
|
||||
|
||||
return seed ^ NetUtility.GetPlatformSeed(m_seedIncrement);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class NetReceiverChannelBase
|
||||
{
|
||||
internal NetPeer m_peer;
|
||||
internal NetConnection m_connection;
|
||||
|
||||
public NetReceiverChannelBase(NetConnection connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_peer = connection.m_peer;
|
||||
}
|
||||
|
||||
internal abstract void ReceiveMessage(NetIncomingMessage msg);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class NetReceiverChannelBase
|
||||
{
|
||||
internal NetPeer m_peer;
|
||||
internal NetConnection m_connection;
|
||||
|
||||
public NetReceiverChannelBase(NetConnection connection)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_peer = connection.m_peer;
|
||||
}
|
||||
|
||||
internal abstract void ReceiveMessage(NetIncomingMessage msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableOrderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private NetBitVector m_earlyReceived;
|
||||
internal NetIncomingMessage[] m_withheldMessages;
|
||||
|
||||
public NetReliableOrderedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
m_withheldMessages = new NetIncomingMessage[windowSize];
|
||||
m_earlyReceived = new NetBitVector(windowSize);
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
//m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
// release withheld messages
|
||||
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
while (m_earlyReceived[nextSeqNr % m_windowSize])
|
||||
{
|
||||
message = m_withheldMessages[nextSeqNr % m_windowSize];
|
||||
NetException.Assert(message != null);
|
||||
|
||||
// remove it from withheld messages
|
||||
m_withheldMessages[nextSeqNr % m_windowSize] = null;
|
||||
|
||||
m_peer.LogVerbose("Releasing withheld message #" + message);
|
||||
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
AdvanceWindow();
|
||||
nextSeqNr++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
|
||||
// duplicate
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
|
||||
m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
|
||||
m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableOrderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private NetBitVector m_earlyReceived;
|
||||
internal NetIncomingMessage[] m_withheldMessages;
|
||||
|
||||
public NetReliableOrderedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
m_withheldMessages = new NetIncomingMessage[windowSize];
|
||||
m_earlyReceived = new NetBitVector(windowSize);
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
//m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
// release withheld messages
|
||||
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
while (m_earlyReceived[nextSeqNr % m_windowSize])
|
||||
{
|
||||
message = m_withheldMessages[nextSeqNr % m_windowSize];
|
||||
NetException.Assert(message != null);
|
||||
|
||||
// remove it from withheld messages
|
||||
m_withheldMessages[nextSeqNr % m_windowSize] = null;
|
||||
|
||||
m_peer.LogVerbose("Releasing withheld message #" + message);
|
||||
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
AdvanceWindow();
|
||||
nextSeqNr++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
|
||||
// duplicate
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
|
||||
m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
|
||||
m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,258 +1,258 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Sender part of Selective repeat ARQ for a particular NetChannel
|
||||
/// </summary>
|
||||
internal sealed class NetReliableSenderChannel : NetSenderChannelBase
|
||||
{
|
||||
private NetConnection m_connection;
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private int m_sendStart;
|
||||
|
||||
private NetBitVector m_receivedAcks;
|
||||
internal NetStoredReliableMessage[] m_storedMessages;
|
||||
|
||||
internal float m_resendDelay;
|
||||
|
||||
internal override int WindowSize { get { return m_windowSize; } }
|
||||
|
||||
internal NetReliableSenderChannel(NetConnection connection, int windowSize)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_windowSize = windowSize;
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
|
||||
m_storedMessages = new NetStoredReliableMessage[m_windowSize];
|
||||
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
|
||||
m_resendDelay = m_connection.GetResendDelay();
|
||||
}
|
||||
|
||||
internal override int GetAllowedSends()
|
||||
{
|
||||
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
NetException.Assert(retval >= 0 && retval <= m_windowSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
m_receivedAcks.Clear();
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
m_storedMessages[i].Reset();
|
||||
m_queuedSends.Clear();
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
}
|
||||
|
||||
internal override NetSendResult Enqueue(NetOutgoingMessage message)
|
||||
{
|
||||
m_queuedSends.Enqueue(message);
|
||||
|
||||
int queueLen = m_queuedSends.Count;
|
||||
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
if (queueLen <= left)
|
||||
return NetSendResult.Sent;
|
||||
return NetSendResult.Queued;
|
||||
}
|
||||
|
||||
// call this regularely
|
||||
internal override void SendQueuedMessages(float now)
|
||||
{
|
||||
//
|
||||
// resends
|
||||
//
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
{
|
||||
NetOutgoingMessage om = m_storedMessages[i].Message;
|
||||
if (om == null)
|
||||
continue;
|
||||
|
||||
float t = m_storedMessages[i].LastSent;
|
||||
if (t > 0 && (now - t) > m_resendDelay)
|
||||
{
|
||||
// deduce sequence number
|
||||
int startSlot = m_windowStart % m_windowSize;
|
||||
int seqNr = m_windowStart;
|
||||
while (startSlot != i)
|
||||
{
|
||||
startSlot--;
|
||||
if (startSlot < 0)
|
||||
startSlot = m_windowSize - 1;
|
||||
seqNr--;
|
||||
}
|
||||
|
||||
//m_connection.m_peer.LogVerbose("Resending due to delay #" + seqNr + " " + om.ToString());
|
||||
m_connection.m_statistics.MessageResent(MessageResendReason.Delay);
|
||||
|
||||
m_connection.QueueSendMessage(om, seqNr);
|
||||
|
||||
m_storedMessages[i].LastSent = now;
|
||||
m_storedMessages[i].NumSent++;
|
||||
}
|
||||
}
|
||||
|
||||
int num = GetAllowedSends();
|
||||
if (num < 1)
|
||||
return;
|
||||
|
||||
// queued sends
|
||||
while (m_queuedSends.Count > 0 && num > 0)
|
||||
{
|
||||
NetOutgoingMessage om;
|
||||
if (m_queuedSends.TryDequeue(out om))
|
||||
ExecuteSend(now, om);
|
||||
num--;
|
||||
NetException.Assert(num == GetAllowedSends());
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteSend(float now, NetOutgoingMessage message)
|
||||
{
|
||||
int seqNr = m_sendStart;
|
||||
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
m_connection.QueueSendMessage(message, seqNr);
|
||||
|
||||
int storeIndex = seqNr % m_windowSize;
|
||||
NetException.Assert(m_storedMessages[storeIndex].Message == null);
|
||||
|
||||
m_storedMessages[storeIndex].NumSent++;
|
||||
m_storedMessages[storeIndex].Message = message;
|
||||
m_storedMessages[storeIndex].LastSent = now;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void DestoreMessage(int storeIndex)
|
||||
{
|
||||
NetOutgoingMessage storedMessage = m_storedMessages[storeIndex].Message;
|
||||
#if DEBUG
|
||||
if (storedMessage == null)
|
||||
throw new NetException("m_storedMessages[" + storeIndex + "].Message is null; sent " + m_storedMessages[storeIndex].NumSent + " times, last time " + (NetTime.Now - m_storedMessages[storeIndex].LastSent) + " seconds ago");
|
||||
#else
|
||||
if (storedMessage != null)
|
||||
{
|
||||
#endif
|
||||
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
|
||||
if (storedMessage.m_recyclingCount <= 0)
|
||||
m_connection.m_peer.Recycle(storedMessage);
|
||||
|
||||
#if !DEBUG
|
||||
}
|
||||
#endif
|
||||
m_storedMessages[storeIndex] = new NetStoredReliableMessage();
|
||||
}
|
||||
|
||||
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
|
||||
// seqNr is the actual nr received
|
||||
internal override void ReceiveAcknowledge(float now, int seqNr)
|
||||
{
|
||||
// late (dupe), on time or early ack?
|
||||
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
|
||||
return; // late/duplicate ack
|
||||
}
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
|
||||
|
||||
// ack arrived right on time
|
||||
NetException.Assert(seqNr == m_windowStart);
|
||||
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
DestoreMessage(m_windowStart % m_windowSize);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
// advance window if we already have early acks
|
||||
while (m_receivedAcks.Get(m_windowStart))
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "...");
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
DestoreMessage(m_windowStart % m_windowSize);
|
||||
|
||||
NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
//m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// early ack... (if it has been sent!)
|
||||
//
|
||||
// If it has been sent either the m_windowStart message was lost
|
||||
// ... or the ack for that message was lost
|
||||
//
|
||||
|
||||
//m_connection.m_peer.LogDebug("Received early ack for #" + seqNr);
|
||||
|
||||
int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart);
|
||||
if (sendRelate <= 0)
|
||||
{
|
||||
// yes, we've sent this message - it's an early (but valid) ack
|
||||
if (m_receivedAcks[seqNr])
|
||||
{
|
||||
// we've already destored/been acked for this message
|
||||
}
|
||||
else
|
||||
{
|
||||
m_receivedAcks[seqNr] = true;
|
||||
}
|
||||
}
|
||||
else if (sendRelate > 0)
|
||||
{
|
||||
// uh... we haven't sent this message yet? Weird, dupe or error...
|
||||
NetException.Assert(false, "Got ack for message not yet sent?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, lets resend all missing acks
|
||||
int rnr = seqNr;
|
||||
do
|
||||
{
|
||||
rnr--;
|
||||
if (rnr < 0)
|
||||
rnr = NetConstants.NumSequenceNumbers - 1;
|
||||
|
||||
if (m_receivedAcks[rnr])
|
||||
{
|
||||
// m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)");
|
||||
}
|
||||
else
|
||||
{
|
||||
int slot = rnr % m_windowSize;
|
||||
NetException.Assert(m_storedMessages[slot].Message != null);
|
||||
if (m_storedMessages[slot].NumSent == 1)
|
||||
{
|
||||
// just sent once; resend immediately since we found gap in ack sequence
|
||||
NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
|
||||
//m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")");
|
||||
|
||||
if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f))
|
||||
{
|
||||
// already resent recently
|
||||
}
|
||||
else
|
||||
{
|
||||
m_storedMessages[slot].LastSent = now;
|
||||
m_storedMessages[slot].NumSent++;
|
||||
m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence);
|
||||
m_connection.QueueSendMessage(rmsg, rnr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (rnr != m_windowStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Sender part of Selective repeat ARQ for a particular NetChannel
|
||||
/// </summary>
|
||||
internal sealed class NetReliableSenderChannel : NetSenderChannelBase
|
||||
{
|
||||
private NetConnection m_connection;
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private int m_sendStart;
|
||||
|
||||
private NetBitVector m_receivedAcks;
|
||||
internal NetStoredReliableMessage[] m_storedMessages;
|
||||
|
||||
internal float m_resendDelay;
|
||||
|
||||
internal override int WindowSize { get { return m_windowSize; } }
|
||||
|
||||
internal NetReliableSenderChannel(NetConnection connection, int windowSize)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_windowSize = windowSize;
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
|
||||
m_storedMessages = new NetStoredReliableMessage[m_windowSize];
|
||||
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
|
||||
m_resendDelay = m_connection.GetResendDelay();
|
||||
}
|
||||
|
||||
internal override int GetAllowedSends()
|
||||
{
|
||||
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
NetException.Assert(retval >= 0 && retval <= m_windowSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
m_receivedAcks.Clear();
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
m_storedMessages[i].Reset();
|
||||
m_queuedSends.Clear();
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
}
|
||||
|
||||
internal override NetSendResult Enqueue(NetOutgoingMessage message)
|
||||
{
|
||||
m_queuedSends.Enqueue(message);
|
||||
|
||||
int queueLen = m_queuedSends.Count;
|
||||
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
if (queueLen <= left)
|
||||
return NetSendResult.Sent;
|
||||
return NetSendResult.Queued;
|
||||
}
|
||||
|
||||
// call this regularely
|
||||
internal override void SendQueuedMessages(float now)
|
||||
{
|
||||
//
|
||||
// resends
|
||||
//
|
||||
for (int i = 0; i < m_storedMessages.Length; i++)
|
||||
{
|
||||
NetOutgoingMessage om = m_storedMessages[i].Message;
|
||||
if (om == null)
|
||||
continue;
|
||||
|
||||
float t = m_storedMessages[i].LastSent;
|
||||
if (t > 0 && (now - t) > m_resendDelay)
|
||||
{
|
||||
// deduce sequence number
|
||||
int startSlot = m_windowStart % m_windowSize;
|
||||
int seqNr = m_windowStart;
|
||||
while (startSlot != i)
|
||||
{
|
||||
startSlot--;
|
||||
if (startSlot < 0)
|
||||
startSlot = m_windowSize - 1;
|
||||
seqNr--;
|
||||
}
|
||||
|
||||
//m_connection.m_peer.LogVerbose("Resending due to delay #" + seqNr + " " + om.ToString());
|
||||
m_connection.m_statistics.MessageResent(MessageResendReason.Delay);
|
||||
|
||||
m_connection.QueueSendMessage(om, seqNr);
|
||||
|
||||
m_storedMessages[i].LastSent = now;
|
||||
m_storedMessages[i].NumSent++;
|
||||
}
|
||||
}
|
||||
|
||||
int num = GetAllowedSends();
|
||||
if (num < 1)
|
||||
return;
|
||||
|
||||
// queued sends
|
||||
while (m_queuedSends.Count > 0 && num > 0)
|
||||
{
|
||||
NetOutgoingMessage om;
|
||||
if (m_queuedSends.TryDequeue(out om))
|
||||
ExecuteSend(now, om);
|
||||
num--;
|
||||
NetException.Assert(num == GetAllowedSends());
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteSend(float now, NetOutgoingMessage message)
|
||||
{
|
||||
int seqNr = m_sendStart;
|
||||
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
m_connection.QueueSendMessage(message, seqNr);
|
||||
|
||||
int storeIndex = seqNr % m_windowSize;
|
||||
NetException.Assert(m_storedMessages[storeIndex].Message == null);
|
||||
|
||||
m_storedMessages[storeIndex].NumSent++;
|
||||
m_storedMessages[storeIndex].Message = message;
|
||||
m_storedMessages[storeIndex].LastSent = now;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void DestoreMessage(int storeIndex)
|
||||
{
|
||||
NetOutgoingMessage storedMessage = m_storedMessages[storeIndex].Message;
|
||||
#if DEBUG
|
||||
if (storedMessage == null)
|
||||
throw new NetException("m_storedMessages[" + storeIndex + "].Message is null; sent " + m_storedMessages[storeIndex].NumSent + " times, last time " + (NetTime.Now - m_storedMessages[storeIndex].LastSent) + " seconds ago");
|
||||
#else
|
||||
if (storedMessage != null)
|
||||
{
|
||||
#endif
|
||||
Interlocked.Decrement(ref storedMessage.m_recyclingCount);
|
||||
if (storedMessage.m_recyclingCount <= 0)
|
||||
m_connection.m_peer.Recycle(storedMessage);
|
||||
|
||||
#if !DEBUG
|
||||
}
|
||||
#endif
|
||||
m_storedMessages[storeIndex] = new NetStoredReliableMessage();
|
||||
}
|
||||
|
||||
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
|
||||
// seqNr is the actual nr received
|
||||
internal override void ReceiveAcknowledge(float now, int seqNr)
|
||||
{
|
||||
// late (dupe), on time or early ack?
|
||||
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
|
||||
return; // late/duplicate ack
|
||||
}
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
|
||||
|
||||
// ack arrived right on time
|
||||
NetException.Assert(seqNr == m_windowStart);
|
||||
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
DestoreMessage(m_windowStart % m_windowSize);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
// advance window if we already have early acks
|
||||
while (m_receivedAcks.Get(m_windowStart))
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "...");
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
DestoreMessage(m_windowStart % m_windowSize);
|
||||
|
||||
NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
//m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// early ack... (if it has been sent!)
|
||||
//
|
||||
// If it has been sent either the m_windowStart message was lost
|
||||
// ... or the ack for that message was lost
|
||||
//
|
||||
|
||||
//m_connection.m_peer.LogDebug("Received early ack for #" + seqNr);
|
||||
|
||||
int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart);
|
||||
if (sendRelate <= 0)
|
||||
{
|
||||
// yes, we've sent this message - it's an early (but valid) ack
|
||||
if (m_receivedAcks[seqNr])
|
||||
{
|
||||
// we've already destored/been acked for this message
|
||||
}
|
||||
else
|
||||
{
|
||||
m_receivedAcks[seqNr] = true;
|
||||
}
|
||||
}
|
||||
else if (sendRelate > 0)
|
||||
{
|
||||
// uh... we haven't sent this message yet? Weird, dupe or error...
|
||||
NetException.Assert(false, "Got ack for message not yet sent?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ok, lets resend all missing acks
|
||||
int rnr = seqNr;
|
||||
do
|
||||
{
|
||||
rnr--;
|
||||
if (rnr < 0)
|
||||
rnr = NetConstants.NumSequenceNumbers - 1;
|
||||
|
||||
if (m_receivedAcks[rnr])
|
||||
{
|
||||
// m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)");
|
||||
}
|
||||
else
|
||||
{
|
||||
int slot = rnr % m_windowSize;
|
||||
NetException.Assert(m_storedMessages[slot].Message != null);
|
||||
if (m_storedMessages[slot].NumSent == 1)
|
||||
{
|
||||
// just sent once; resend immediately since we found gap in ack sequence
|
||||
NetOutgoingMessage rmsg = m_storedMessages[slot].Message;
|
||||
//m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")");
|
||||
|
||||
if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f))
|
||||
{
|
||||
// already resent recently
|
||||
}
|
||||
else
|
||||
{
|
||||
m_storedMessages[slot].LastSent = now;
|
||||
m_storedMessages[slot].NumSent++;
|
||||
m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence);
|
||||
m_connection.QueueSendMessage(rmsg, rnr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (rnr != m_windowStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableSequencedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
|
||||
public NetReliableSequencedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int nr = message.m_sequenceNumber;
|
||||
|
||||
int relate = NetUtility.RelativeSequenceNumber(nr, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, nr);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE");
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
// ok
|
||||
m_windowStart = (m_windowStart + relate) % NetConstants.NumSequenceNumbers;
|
||||
m_peer.ReleaseMessage(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableSequencedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
|
||||
public NetReliableSequencedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int nr = message.m_sequenceNumber;
|
||||
|
||||
int relate = NetUtility.RelativeSequenceNumber(nr, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, nr);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING LATE or DUPE");
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
// ok
|
||||
m_windowStart = (m_windowStart + relate) % NetConstants.NumSequenceNumbers;
|
||||
m_peer.ReleaseMessage(message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableUnorderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private NetBitVector m_earlyReceived;
|
||||
|
||||
public NetReliableUnorderedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
m_earlyReceived = new NetBitVector(windowSize);
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
//m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
// release withheld messages
|
||||
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
while (m_earlyReceived[nextSeqNr % m_windowSize])
|
||||
{
|
||||
//message = m_withheldMessages[nextSeqNr % m_windowSize];
|
||||
//NetException.Assert(message != null);
|
||||
|
||||
// remove it from withheld messages
|
||||
//m_withheldMessages[nextSeqNr % m_windowSize] = null;
|
||||
|
||||
//m_peer.LogVerbose("Releasing withheld message #" + message);
|
||||
|
||||
//m_peer.ReleaseMessage(message);
|
||||
|
||||
AdvanceWindow();
|
||||
nextSeqNr++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
// duplicate
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
|
||||
//m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
|
||||
//m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
|
||||
|
||||
m_peer.ReleaseMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetReliableUnorderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private NetBitVector m_earlyReceived;
|
||||
|
||||
public NetReliableUnorderedReceiver(NetConnection connection, int windowSize)
|
||||
: base(connection)
|
||||
{
|
||||
m_windowSize = windowSize;
|
||||
m_earlyReceived = new NetBitVector(windowSize);
|
||||
}
|
||||
|
||||
private void AdvanceWindow()
|
||||
{
|
||||
m_earlyReceived.Set(m_windowStart % m_windowSize, false);
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage message)
|
||||
{
|
||||
int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart);
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber);
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
// Log("Received message #" + message.SequenceNumber + " right on time");
|
||||
|
||||
//
|
||||
// excellent, right on time
|
||||
//
|
||||
//m_peer.LogVerbose("Received RIGHT-ON-TIME " + message);
|
||||
|
||||
AdvanceWindow();
|
||||
m_peer.ReleaseMessage(message);
|
||||
|
||||
// release withheld messages
|
||||
int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
while (m_earlyReceived[nextSeqNr % m_windowSize])
|
||||
{
|
||||
//message = m_withheldMessages[nextSeqNr % m_windowSize];
|
||||
//NetException.Assert(message != null);
|
||||
|
||||
// remove it from withheld messages
|
||||
//m_withheldMessages[nextSeqNr % m_windowSize] = null;
|
||||
|
||||
//m_peer.LogVerbose("Releasing withheld message #" + message);
|
||||
|
||||
//m_peer.ReleaseMessage(message);
|
||||
|
||||
AdvanceWindow();
|
||||
nextSeqNr++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
// duplicate
|
||||
m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE");
|
||||
return;
|
||||
}
|
||||
|
||||
// relate > 0 = early message
|
||||
if (relate > m_windowSize)
|
||||
{
|
||||
// too early message!
|
||||
m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart);
|
||||
return;
|
||||
}
|
||||
|
||||
m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true);
|
||||
//m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart);
|
||||
//m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message;
|
||||
|
||||
m_peer.ReleaseMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,204 +1,204 @@
|
||||
#define USE_SHA256
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper methods for implementing SRP authentication
|
||||
/// </summary>
|
||||
public static class NetSRP
|
||||
{
|
||||
private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16);
|
||||
private static readonly NetBigInteger g = NetBigInteger.Two;
|
||||
private static readonly NetBigInteger k = ComputeMultiplier();
|
||||
|
||||
private static HashAlgorithm GetHashAlgorithm()
|
||||
{
|
||||
#if USE_SHA256
|
||||
// this does not seem to work as of yet
|
||||
return SHA256.Create();
|
||||
#else
|
||||
return SHA1.Create();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute multiplier (k)
|
||||
/// </summary>
|
||||
private static NetBigInteger ComputeMultiplier()
|
||||
{
|
||||
string one = NetUtility.ToHexString(N.ToByteArrayUnsigned());
|
||||
string two = NetUtility.ToHexString(g.ToByteArrayUnsigned());
|
||||
|
||||
string ccstr = one + two.PadLeft(one.Length, '0');
|
||||
byte[] cc = NetUtility.ToByteArray(ccstr);
|
||||
|
||||
var sha = GetHashAlgorithm();
|
||||
var ccHashed = sha.ComputeHash(cc);
|
||||
|
||||
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create 16 bytes of random salt
|
||||
/// </summary>
|
||||
public static byte[] CreateRandomSalt()
|
||||
{
|
||||
byte[] retval = new byte[16];
|
||||
NetRandom.Instance.NextBytes(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create 32 bytes of random ephemeral value
|
||||
/// </summary>
|
||||
public static byte[] CreateRandomEphemeral()
|
||||
{
|
||||
byte[] retval = new byte[32];
|
||||
NetRandom.Instance.NextBytes(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computer private key (x)
|
||||
/// </summary>
|
||||
public static byte[] ComputePrivateKey(string username, string password, byte[] salt)
|
||||
{
|
||||
var sha = GetHashAlgorithm();
|
||||
|
||||
byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password);
|
||||
byte[] innerHash = sha.ComputeHash(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);
|
||||
|
||||
// x ie. H(salt || H(username || ":" || password))
|
||||
return new NetBigInteger(NetUtility.ToHexString(sha.ComputeHash(total)), 16).ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a verifier that the server can later use to authenticate users later on (v)
|
||||
/// </summary>
|
||||
public static byte[] ComputeServerVerifier(byte[] privateKey)
|
||||
{
|
||||
NetBigInteger x = new NetBigInteger(NetUtility.ToHexString(privateKey), 16);
|
||||
|
||||
// Verifier (v) = g^x (mod N)
|
||||
var serverVerifier = g.ModPow(x, N);
|
||||
|
||||
return serverVerifier.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SHA hash data
|
||||
/// </summary>
|
||||
public static byte[] Hash(byte[] data)
|
||||
{
|
||||
var sha = GetHashAlgorithm();
|
||||
return sha.ComputeHash(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute client public ephemeral value (A)
|
||||
/// </summary>
|
||||
public static byte[] ComputeClientEphemeral(byte[] clientPrivateEphemeral) // a
|
||||
{
|
||||
// A= g^a (mod N)
|
||||
NetBigInteger a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
|
||||
NetBigInteger retval = g.ModPow(a, N);
|
||||
|
||||
return retval.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute server ephemeral value (B)
|
||||
/// </summary>
|
||||
public static byte[] ComputeServerEphemeral(byte[] serverPrivateEphemeral, byte[] verifier) // b
|
||||
{
|
||||
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
|
||||
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
|
||||
|
||||
// B = kv + g^b (mod N)
|
||||
var bb = g.ModPow(b, N);
|
||||
var kv = v.Multiply(k);
|
||||
var B = (kv.Add(bb)).Mod(N);
|
||||
|
||||
return B.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute intermediate value (u)
|
||||
/// </summary>
|
||||
public static byte[] ComputeU(byte[] clientPublicEphemeral, byte[] serverPublicEphemeral)
|
||||
{
|
||||
// u = SHA-1(A || B)
|
||||
string one = NetUtility.ToHexString(clientPublicEphemeral);
|
||||
string two = NetUtility.ToHexString(serverPublicEphemeral);
|
||||
|
||||
int len = 66; // Math.Max(one.Length, two.Length);
|
||||
string ccstr = one.PadLeft(len, '0') + two.PadLeft(len, '0');
|
||||
|
||||
byte[] cc = NetUtility.ToByteArray(ccstr);
|
||||
|
||||
var sha = GetHashAlgorithm();
|
||||
var ccHashed = sha.ComputeHash(cc);
|
||||
|
||||
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the server session value
|
||||
/// </summary>
|
||||
public static byte[] ComputeServerSessionValue(byte[] clientPublicEphemeral, byte[] verifier, byte[] udata, byte[] serverPrivateEphemeral)
|
||||
{
|
||||
// S = (Av^u) ^ b (mod N)
|
||||
var A = new NetBigInteger(NetUtility.ToHexString(clientPublicEphemeral), 16);
|
||||
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
|
||||
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
|
||||
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
|
||||
|
||||
NetBigInteger retval = v.ModPow(u, N).Multiply(A).Mod(N).ModPow(b, N).Mod(N);
|
||||
|
||||
return retval.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the client session value
|
||||
/// </summary>
|
||||
public static byte[] ComputeClientSessionValue(byte[] serverPublicEphemeral, byte[] xdata, byte[] udata, byte[] clientPrivateEphemeral)
|
||||
{
|
||||
// (B - kg^x) ^ (a + ux) (mod N)
|
||||
var B = new NetBigInteger(NetUtility.ToHexString(serverPublicEphemeral), 16);
|
||||
var x = new NetBigInteger(NetUtility.ToHexString(xdata), 16);
|
||||
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
|
||||
var a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
|
||||
|
||||
var bx = g.ModPow(x, N);
|
||||
var btmp = B.Add(N.Multiply(k)).Subtract(bx.Multiply(k)).Mod(N);
|
||||
return btmp.ModPow(x.Multiply(u).Add(a), N).ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create XTEA symmetrical encryption object from sessionValue
|
||||
/// </summary>
|
||||
public static NetXtea CreateEncryption(byte[] sessionValue)
|
||||
{
|
||||
var sha = GetHashAlgorithm();
|
||||
var hash = sha.ComputeHash(sessionValue);
|
||||
|
||||
var key = new byte[16];
|
||||
for(int i=0;i<16;i++)
|
||||
{
|
||||
key[i] = hash[i];
|
||||
for (int j = 1; j < hash.Length / 16; j++)
|
||||
key[i] ^= hash[i + (j * 16)];
|
||||
}
|
||||
|
||||
return new NetXtea(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
#define USE_SHA256
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper methods for implementing SRP authentication
|
||||
/// </summary>
|
||||
public static class NetSRP
|
||||
{
|
||||
private static readonly NetBigInteger N = new NetBigInteger("0115b8b692e0e045692cf280b436735c77a5a9e8a9e7ed56c965f87db5b2a2ece3", 16);
|
||||
private static readonly NetBigInteger g = NetBigInteger.Two;
|
||||
private static readonly NetBigInteger k = ComputeMultiplier();
|
||||
|
||||
private static HashAlgorithm GetHashAlgorithm()
|
||||
{
|
||||
#if USE_SHA256
|
||||
// this does not seem to work as of yet
|
||||
return SHA256.Create();
|
||||
#else
|
||||
return SHA1.Create();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute multiplier (k)
|
||||
/// </summary>
|
||||
private static NetBigInteger ComputeMultiplier()
|
||||
{
|
||||
string one = NetUtility.ToHexString(N.ToByteArrayUnsigned());
|
||||
string two = NetUtility.ToHexString(g.ToByteArrayUnsigned());
|
||||
|
||||
string ccstr = one + two.PadLeft(one.Length, '0');
|
||||
byte[] cc = NetUtility.ToByteArray(ccstr);
|
||||
|
||||
var sha = GetHashAlgorithm();
|
||||
var ccHashed = sha.ComputeHash(cc);
|
||||
|
||||
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create 16 bytes of random salt
|
||||
/// </summary>
|
||||
public static byte[] CreateRandomSalt()
|
||||
{
|
||||
byte[] retval = new byte[16];
|
||||
NetRandom.Instance.NextBytes(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create 32 bytes of random ephemeral value
|
||||
/// </summary>
|
||||
public static byte[] CreateRandomEphemeral()
|
||||
{
|
||||
byte[] retval = new byte[32];
|
||||
NetRandom.Instance.NextBytes(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computer private key (x)
|
||||
/// </summary>
|
||||
public static byte[] ComputePrivateKey(string username, string password, byte[] salt)
|
||||
{
|
||||
var sha = GetHashAlgorithm();
|
||||
|
||||
byte[] tmp = Encoding.UTF8.GetBytes(username + ":" + password);
|
||||
byte[] innerHash = sha.ComputeHash(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);
|
||||
|
||||
// x ie. H(salt || H(username || ":" || password))
|
||||
return new NetBigInteger(NetUtility.ToHexString(sha.ComputeHash(total)), 16).ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a verifier that the server can later use to authenticate users later on (v)
|
||||
/// </summary>
|
||||
public static byte[] ComputeServerVerifier(byte[] privateKey)
|
||||
{
|
||||
NetBigInteger x = new NetBigInteger(NetUtility.ToHexString(privateKey), 16);
|
||||
|
||||
// Verifier (v) = g^x (mod N)
|
||||
var serverVerifier = g.ModPow(x, N);
|
||||
|
||||
return serverVerifier.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SHA hash data
|
||||
/// </summary>
|
||||
public static byte[] Hash(byte[] data)
|
||||
{
|
||||
var sha = GetHashAlgorithm();
|
||||
return sha.ComputeHash(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute client public ephemeral value (A)
|
||||
/// </summary>
|
||||
public static byte[] ComputeClientEphemeral(byte[] clientPrivateEphemeral) // a
|
||||
{
|
||||
// A= g^a (mod N)
|
||||
NetBigInteger a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
|
||||
NetBigInteger retval = g.ModPow(a, N);
|
||||
|
||||
return retval.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute server ephemeral value (B)
|
||||
/// </summary>
|
||||
public static byte[] ComputeServerEphemeral(byte[] serverPrivateEphemeral, byte[] verifier) // b
|
||||
{
|
||||
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
|
||||
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
|
||||
|
||||
// B = kv + g^b (mod N)
|
||||
var bb = g.ModPow(b, N);
|
||||
var kv = v.Multiply(k);
|
||||
var B = (kv.Add(bb)).Mod(N);
|
||||
|
||||
return B.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute intermediate value (u)
|
||||
/// </summary>
|
||||
public static byte[] ComputeU(byte[] clientPublicEphemeral, byte[] serverPublicEphemeral)
|
||||
{
|
||||
// u = SHA-1(A || B)
|
||||
string one = NetUtility.ToHexString(clientPublicEphemeral);
|
||||
string two = NetUtility.ToHexString(serverPublicEphemeral);
|
||||
|
||||
int len = 66; // Math.Max(one.Length, two.Length);
|
||||
string ccstr = one.PadLeft(len, '0') + two.PadLeft(len, '0');
|
||||
|
||||
byte[] cc = NetUtility.ToByteArray(ccstr);
|
||||
|
||||
var sha = GetHashAlgorithm();
|
||||
var ccHashed = sha.ComputeHash(cc);
|
||||
|
||||
return new NetBigInteger(NetUtility.ToHexString(ccHashed), 16).ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the server session value
|
||||
/// </summary>
|
||||
public static byte[] ComputeServerSessionValue(byte[] clientPublicEphemeral, byte[] verifier, byte[] udata, byte[] serverPrivateEphemeral)
|
||||
{
|
||||
// S = (Av^u) ^ b (mod N)
|
||||
var A = new NetBigInteger(NetUtility.ToHexString(clientPublicEphemeral), 16);
|
||||
var v = new NetBigInteger(NetUtility.ToHexString(verifier), 16);
|
||||
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
|
||||
var b = new NetBigInteger(NetUtility.ToHexString(serverPrivateEphemeral), 16);
|
||||
|
||||
NetBigInteger retval = v.ModPow(u, N).Multiply(A).Mod(N).ModPow(b, N).Mod(N);
|
||||
|
||||
return retval.ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the client session value
|
||||
/// </summary>
|
||||
public static byte[] ComputeClientSessionValue(byte[] serverPublicEphemeral, byte[] xdata, byte[] udata, byte[] clientPrivateEphemeral)
|
||||
{
|
||||
// (B - kg^x) ^ (a + ux) (mod N)
|
||||
var B = new NetBigInteger(NetUtility.ToHexString(serverPublicEphemeral), 16);
|
||||
var x = new NetBigInteger(NetUtility.ToHexString(xdata), 16);
|
||||
var u = new NetBigInteger(NetUtility.ToHexString(udata), 16);
|
||||
var a = new NetBigInteger(NetUtility.ToHexString(clientPrivateEphemeral), 16);
|
||||
|
||||
var bx = g.ModPow(x, N);
|
||||
var btmp = B.Add(N.Multiply(k)).Subtract(bx.Multiply(k)).Mod(N);
|
||||
return btmp.ModPow(x.Multiply(u).Add(a), N).ToByteArrayUnsigned();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create XTEA symmetrical encryption object from sessionValue
|
||||
/// </summary>
|
||||
public static NetXtea CreateEncryption(byte[] sessionValue)
|
||||
{
|
||||
var sha = GetHashAlgorithm();
|
||||
var hash = sha.ComputeHash(sessionValue);
|
||||
|
||||
var key = new byte[16];
|
||||
for(int i=0;i<16;i++)
|
||||
{
|
||||
key[i] = hash[i];
|
||||
for (int j = 1; j < hash.Length / 16; j++)
|
||||
key[i] ^= hash[i + (j * 16)];
|
||||
}
|
||||
|
||||
return new NetXtea(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of a SendMessage call
|
||||
/// </summary>
|
||||
public enum NetSendResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Message failed to enqueue because there is no connection
|
||||
/// </summary>
|
||||
FailedNotConnected = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Message was immediately sent
|
||||
/// </summary>
|
||||
Sent = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Message was queued for delivery
|
||||
/// </summary>
|
||||
Queued = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Message was dropped immediately since too many message were queued
|
||||
/// </summary>
|
||||
Dropped = 3
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Result of a SendMessage call
|
||||
/// </summary>
|
||||
public enum NetSendResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Message failed to enqueue because there is no connection
|
||||
/// </summary>
|
||||
FailedNotConnected = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Message was immediately sent
|
||||
/// </summary>
|
||||
Sent = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Message was queued for delivery
|
||||
/// </summary>
|
||||
Queued = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Message was dropped immediately since too many message were queued
|
||||
/// </summary>
|
||||
Dropped = 3
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class NetSenderChannelBase
|
||||
{
|
||||
// access this directly to queue things in this channel
|
||||
internal NetQueue<NetOutgoingMessage> m_queuedSends;
|
||||
|
||||
internal abstract int WindowSize { get; }
|
||||
|
||||
internal abstract int GetAllowedSends();
|
||||
|
||||
internal abstract NetSendResult Enqueue(NetOutgoingMessage message);
|
||||
internal abstract void SendQueuedMessages(float now);
|
||||
internal abstract void Reset();
|
||||
internal abstract void ReceiveAcknowledge(float now, int sequenceNumber);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class NetSenderChannelBase
|
||||
{
|
||||
// access this directly to queue things in this channel
|
||||
internal NetQueue<NetOutgoingMessage> m_queuedSends;
|
||||
|
||||
internal abstract int WindowSize { get; }
|
||||
|
||||
internal abstract int GetAllowedSends();
|
||||
|
||||
internal abstract NetSendResult Enqueue(NetOutgoingMessage message);
|
||||
internal abstract void SendQueuedMessages(float now);
|
||||
internal abstract void Reset();
|
||||
internal abstract void ReceiveAcknowledge(float now, int sequenceNumber);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Specialized version of NetPeer used for "server" peers
|
||||
/// </summary>
|
||||
public class NetServer : NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// NetServer constructor
|
||||
/// </summary>
|
||||
public NetServer(NetPeerConfiguration config)
|
||||
: base(config)
|
||||
{
|
||||
config.AcceptIncomingConnections = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to all connections
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
public void SendToAll(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
{
|
||||
var all = this.Connections;
|
||||
if (all.Count <= 0)
|
||||
return;
|
||||
|
||||
SendMessage(msg, all, method, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to all connections except one
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
/// <param name="except">Don't send to this particular connection</param>
|
||||
/// <param name="sequenceChannel">Which sequence channel to use for the message</param>
|
||||
public void SendToAll(NetOutgoingMessage msg, NetConnection except, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
var all = this.Connections;
|
||||
if (all.Count <= 0)
|
||||
return;
|
||||
|
||||
if (except == null)
|
||||
{
|
||||
SendMessage(msg, all, method, sequenceChannel);
|
||||
return;
|
||||
}
|
||||
|
||||
List<NetConnection> recipients = new List<NetConnection>(all.Count - 1);
|
||||
foreach (var conn in all)
|
||||
if (conn != except)
|
||||
recipients.Add(conn);
|
||||
|
||||
if (recipients.Count > 0)
|
||||
SendMessage(msg, recipients, method, sequenceChannel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetServer " + ConnectionsCount + " connections]";
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Specialized version of NetPeer used for "server" peers
|
||||
/// </summary>
|
||||
public class NetServer : NetPeer
|
||||
{
|
||||
/// <summary>
|
||||
/// NetServer constructor
|
||||
/// </summary>
|
||||
public NetServer(NetPeerConfiguration config)
|
||||
: base(config)
|
||||
{
|
||||
config.AcceptIncomingConnections = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to all connections
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
public void SendToAll(NetOutgoingMessage msg, NetDeliveryMethod method)
|
||||
{
|
||||
var all = this.Connections;
|
||||
if (all.Count <= 0)
|
||||
return;
|
||||
|
||||
SendMessage(msg, all, method, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to all connections except one
|
||||
/// </summary>
|
||||
/// <param name="msg">The message to send</param>
|
||||
/// <param name="method">How to deliver the message</param>
|
||||
/// <param name="except">Don't send to this particular connection</param>
|
||||
/// <param name="sequenceChannel">Which sequence channel to use for the message</param>
|
||||
public void SendToAll(NetOutgoingMessage msg, NetConnection except, NetDeliveryMethod method, int sequenceChannel)
|
||||
{
|
||||
var all = this.Connections;
|
||||
if (all.Count <= 0)
|
||||
return;
|
||||
|
||||
if (except == null)
|
||||
{
|
||||
SendMessage(msg, all, method, sequenceChannel);
|
||||
return;
|
||||
}
|
||||
|
||||
List<NetConnection> recipients = new List<NetConnection>(all.Count - 1);
|
||||
foreach (var conn in all)
|
||||
if (conn != except)
|
||||
recipients.Add(conn);
|
||||
|
||||
if (recipients.Count > 0)
|
||||
SendMessage(msg, recipients, method, sequenceChannel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string that represents this object
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return "[NetServer " + ConnectionsCount + " connections]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal struct NetStoredReliableMessage
|
||||
{
|
||||
public int NumSent;
|
||||
public float LastSent;
|
||||
public NetOutgoingMessage Message;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
NumSent = 0;
|
||||
LastSent = 0;
|
||||
Message = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal struct NetStoredReliableMessage
|
||||
{
|
||||
public int NumSent;
|
||||
public float LastSent;
|
||||
public NetOutgoingMessage Message;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
NumSent = 0;
|
||||
LastSent = 0;
|
||||
Message = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
/* 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 readonly long s_timeInitialized = Stopwatch.GetTimestamp();
|
||||
private static readonly 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
|
||||
private static readonly uint s_timeInitialized = (uint)Environment.TickCount;
|
||||
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds)
|
||||
/// </summary>
|
||||
public static string ToReadable(double seconds)
|
||||
{
|
||||
if (seconds > 60)
|
||||
return TimeSpan.FromSeconds(seconds).ToString();
|
||||
return (seconds * 1000.0).ToString("N2") + " ms";
|
||||
}
|
||||
}
|
||||
/* 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 readonly long s_timeInitialized = Stopwatch.GetTimestamp();
|
||||
private static readonly 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
|
||||
private static readonly uint s_timeInitialized = (uint)Environment.TickCount;
|
||||
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Given seconds it will output a human friendly readable string (milliseconds if less than 60 seconds)
|
||||
/// </summary>
|
||||
public static string ToReadable(double seconds)
|
||||
{
|
||||
if (seconds > 60)
|
||||
return TimeSpan.FromSeconds(seconds).ToString();
|
||||
return (seconds * 1000.0).ToString("N2") + " ms";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
// replace with BCL 4.0 Tuple<> when appropriate
|
||||
internal struct NetTuple<A, B>
|
||||
{
|
||||
public A Item1;
|
||||
public B Item2;
|
||||
|
||||
public NetTuple(A item1, B item2)
|
||||
{
|
||||
Item1 = item1;
|
||||
Item2 = item2;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
// replace with BCL 4.0 Tuple<> when appropriate
|
||||
internal struct NetTuple<A, B>
|
||||
{
|
||||
public A Item1;
|
||||
public B Item2;
|
||||
|
||||
public NetTuple(A item1, B item2)
|
||||
{
|
||||
Item1 = item1;
|
||||
Item2 = item2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,194 +1,194 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// UPnP support class
|
||||
/// </summary>
|
||||
public class NetUPnP
|
||||
{
|
||||
private const int c_discoveryTimeOutMillis = 1000;
|
||||
|
||||
private string m_serviceUrl;
|
||||
private NetPeer m_peer;
|
||||
private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// NetUPnP constructor
|
||||
/// </summary>
|
||||
public NetUPnP(NetPeer peer)
|
||||
{
|
||||
m_peer = peer;
|
||||
}
|
||||
|
||||
internal void Discover(NetPeer peer)
|
||||
{
|
||||
string str =
|
||||
"M-SEARCH * HTTP/1.1\r\n" +
|
||||
"HOST: 239.255.255.250:1900\r\n" +
|
||||
"ST:upnp:rootdevice\r\n" +
|
||||
"MAN:\"ssdp:discover\"\r\n" +
|
||||
"MX:3\r\n\r\n";
|
||||
|
||||
byte[] arr = System.Text.Encoding.UTF8.GetBytes(str);
|
||||
|
||||
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
peer.RawSend(arr, 0, arr.Length, new IPEndPoint(IPAddress.Broadcast, 1900));
|
||||
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
|
||||
// allow some extra time for router to respond
|
||||
// System.Threading.Thread.Sleep(50);
|
||||
}
|
||||
|
||||
internal void ExtractServiceUrl(string resp)
|
||||
{
|
||||
#if !DEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
XmlDocument desc = new XmlDocument();
|
||||
desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream());
|
||||
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
|
||||
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
|
||||
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
|
||||
if (!typen.Value.Contains("InternetGatewayDevice"))
|
||||
return;
|
||||
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
|
||||
if (node == null)
|
||||
return;
|
||||
m_serviceUrl = CombineUrls(resp, node.Value);
|
||||
m_peer.LogDebug("UPnP service ready");
|
||||
m_discoveryComplete.Set();
|
||||
#if !DEBUG
|
||||
}
|
||||
catch { return; }
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string CombineUrls(string gatewayURL, string subURL)
|
||||
{
|
||||
// Is Control URL an absolute URL?
|
||||
if ((subURL.Contains("http:")) || (subURL.Contains(".")))
|
||||
return subURL;
|
||||
|
||||
gatewayURL = gatewayURL.Replace("http://", ""); // strip any protocol
|
||||
int n = gatewayURL.IndexOf("/");
|
||||
if (n != -1)
|
||||
gatewayURL = gatewayURL.Substring(0, n); // Use first portion of URL
|
||||
return "http://" + gatewayURL + subURL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a forwarding rule to the router using UPnP
|
||||
/// </summary>
|
||||
public bool ForwardPort(int port, string description)
|
||||
{
|
||||
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
|
||||
return false;
|
||||
|
||||
IPAddress mask;
|
||||
var client = NetUtility.GetMyAddress(out mask);
|
||||
if (client == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
|
||||
"<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + port.ToString() + "</NewExternalPort>" +
|
||||
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
|
||||
"<NewInternalPort>" + port.ToString() + "</NewInternalPort>" +
|
||||
"<NewInternalClient>" + client.ToString() + "</NewInternalClient>" +
|
||||
"<NewEnabled>1</NewEnabled>" +
|
||||
"<NewPortMappingDescription>" + description + "</NewPortMappingDescription>" +
|
||||
"<NewLeaseDuration>0</NewLeaseDuration>" +
|
||||
"</u:AddPortMapping>",
|
||||
"AddPortMapping");
|
||||
|
||||
m_peer.LogDebug("Sent UPnP port forward request");
|
||||
System.Threading.Thread.Sleep(50);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_peer.LogWarning("UPnP port forward failed: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a forwarding rule from the router using UPnP
|
||||
/// </summary>
|
||||
public bool DeleteForwardingRule(int port)
|
||||
{
|
||||
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
|
||||
"<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
|
||||
"<NewRemoteHost>" +
|
||||
"</NewRemoteHost>" +
|
||||
"<NewExternalPort>" + port + "</NewExternalPort>" +
|
||||
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
|
||||
"</u:DeletePortMapping>", "DeletePortMapping");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_peer.LogWarning("UPnP delete forwarding rule failed: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the extern ip using UPnP
|
||||
/// </summary>
|
||||
public IPAddress GetExternalIP()
|
||||
{
|
||||
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
|
||||
return null;
|
||||
try
|
||||
{
|
||||
XmlDocument xdoc = SOAPRequest(m_serviceUrl, "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
|
||||
"</u:GetExternalIPAddress>", "GetExternalIPAddress");
|
||||
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
|
||||
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
|
||||
string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value;
|
||||
return IPAddress.Parse(IP);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_peer.LogWarning("Failed to get external IP: " + ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private XmlDocument SOAPRequest(string url, string soap, string function)
|
||||
{
|
||||
string req = "<?xml version=\"1.0\"?>" +
|
||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
|
||||
"<s:Body>" +
|
||||
soap +
|
||||
"</s:Body>" +
|
||||
"</s:Envelope>";
|
||||
WebRequest r = HttpWebRequest.Create(url);
|
||||
r.Method = "POST";
|
||||
byte[] b = System.Text.Encoding.UTF8.GetBytes(req);
|
||||
r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\"");
|
||||
r.ContentType = "text/xml; charset=\"utf-8\"";
|
||||
r.ContentLength = b.Length;
|
||||
r.GetRequestStream().Write(b, 0, b.Length);
|
||||
XmlDocument resp = new XmlDocument();
|
||||
WebResponse wres = r.GetResponse();
|
||||
Stream ress = wres.GetResponseStream();
|
||||
resp.Load(ress);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// UPnP support class
|
||||
/// </summary>
|
||||
public class NetUPnP
|
||||
{
|
||||
private const int c_discoveryTimeOutMillis = 1000;
|
||||
|
||||
private string m_serviceUrl;
|
||||
private NetPeer m_peer;
|
||||
private ManualResetEvent m_discoveryComplete = new ManualResetEvent(false);
|
||||
|
||||
/// <summary>
|
||||
/// NetUPnP constructor
|
||||
/// </summary>
|
||||
public NetUPnP(NetPeer peer)
|
||||
{
|
||||
m_peer = peer;
|
||||
}
|
||||
|
||||
internal void Discover(NetPeer peer)
|
||||
{
|
||||
string str =
|
||||
"M-SEARCH * HTTP/1.1\r\n" +
|
||||
"HOST: 239.255.255.250:1900\r\n" +
|
||||
"ST:upnp:rootdevice\r\n" +
|
||||
"MAN:\"ssdp:discover\"\r\n" +
|
||||
"MX:3\r\n\r\n";
|
||||
|
||||
byte[] arr = System.Text.Encoding.UTF8.GetBytes(str);
|
||||
|
||||
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
|
||||
peer.RawSend(arr, 0, arr.Length, new IPEndPoint(IPAddress.Broadcast, 1900));
|
||||
peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false);
|
||||
|
||||
// allow some extra time for router to respond
|
||||
// System.Threading.Thread.Sleep(50);
|
||||
}
|
||||
|
||||
internal void ExtractServiceUrl(string resp)
|
||||
{
|
||||
#if !DEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
XmlDocument desc = new XmlDocument();
|
||||
desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream());
|
||||
XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable);
|
||||
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
|
||||
XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr);
|
||||
if (!typen.Value.Contains("InternetGatewayDevice"))
|
||||
return;
|
||||
XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr);
|
||||
if (node == null)
|
||||
return;
|
||||
m_serviceUrl = CombineUrls(resp, node.Value);
|
||||
m_peer.LogDebug("UPnP service ready");
|
||||
m_discoveryComplete.Set();
|
||||
#if !DEBUG
|
||||
}
|
||||
catch { return; }
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string CombineUrls(string gatewayURL, string subURL)
|
||||
{
|
||||
// Is Control URL an absolute URL?
|
||||
if ((subURL.Contains("http:")) || (subURL.Contains(".")))
|
||||
return subURL;
|
||||
|
||||
gatewayURL = gatewayURL.Replace("http://", ""); // strip any protocol
|
||||
int n = gatewayURL.IndexOf("/");
|
||||
if (n != -1)
|
||||
gatewayURL = gatewayURL.Substring(0, n); // Use first portion of URL
|
||||
return "http://" + gatewayURL + subURL;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a forwarding rule to the router using UPnP
|
||||
/// </summary>
|
||||
public bool ForwardPort(int port, string description)
|
||||
{
|
||||
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
|
||||
return false;
|
||||
|
||||
IPAddress mask;
|
||||
var client = NetUtility.GetMyAddress(out mask);
|
||||
if (client == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
|
||||
"<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
|
||||
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + port.ToString() + "</NewExternalPort>" +
|
||||
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
|
||||
"<NewInternalPort>" + port.ToString() + "</NewInternalPort>" +
|
||||
"<NewInternalClient>" + client.ToString() + "</NewInternalClient>" +
|
||||
"<NewEnabled>1</NewEnabled>" +
|
||||
"<NewPortMappingDescription>" + description + "</NewPortMappingDescription>" +
|
||||
"<NewLeaseDuration>0</NewLeaseDuration>" +
|
||||
"</u:AddPortMapping>",
|
||||
"AddPortMapping");
|
||||
|
||||
m_peer.LogDebug("Sent UPnP port forward request");
|
||||
System.Threading.Thread.Sleep(50);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_peer.LogWarning("UPnP port forward failed: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete a forwarding rule from the router using UPnP
|
||||
/// </summary>
|
||||
public bool DeleteForwardingRule(int port)
|
||||
{
|
||||
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
|
||||
return false;
|
||||
try
|
||||
{
|
||||
XmlDocument xdoc = SOAPRequest(m_serviceUrl,
|
||||
"<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
|
||||
"<NewRemoteHost>" +
|
||||
"</NewRemoteHost>" +
|
||||
"<NewExternalPort>" + port + "</NewExternalPort>" +
|
||||
"<NewProtocol>" + ProtocolType.Udp.ToString().ToUpper() + "</NewProtocol>" +
|
||||
"</u:DeletePortMapping>", "DeletePortMapping");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_peer.LogWarning("UPnP delete forwarding rule failed: " + ex.Message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the extern ip using UPnP
|
||||
/// </summary>
|
||||
public IPAddress GetExternalIP()
|
||||
{
|
||||
if (m_serviceUrl == null && !m_discoveryComplete.WaitOne(c_discoveryTimeOutMillis))
|
||||
return null;
|
||||
try
|
||||
{
|
||||
XmlDocument xdoc = SOAPRequest(m_serviceUrl, "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">" +
|
||||
"</u:GetExternalIPAddress>", "GetExternalIPAddress");
|
||||
XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable);
|
||||
nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0");
|
||||
string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value;
|
||||
return IPAddress.Parse(IP);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_peer.LogWarning("Failed to get external IP: " + ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private XmlDocument SOAPRequest(string url, string soap, string function)
|
||||
{
|
||||
string req = "<?xml version=\"1.0\"?>" +
|
||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
|
||||
"<s:Body>" +
|
||||
soap +
|
||||
"</s:Body>" +
|
||||
"</s:Envelope>";
|
||||
WebRequest r = HttpWebRequest.Create(url);
|
||||
r.Method = "POST";
|
||||
byte[] b = System.Text.Encoding.UTF8.GetBytes(req);
|
||||
r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\"");
|
||||
r.ContentType = "text/xml; charset=\"utf-8\"";
|
||||
r.ContentLength = b.Length;
|
||||
r.GetRequestStream().Write(b, 0, b.Length);
|
||||
XmlDocument resp = new XmlDocument();
|
||||
WebResponse wres = r.GetResponse();
|
||||
Stream ress = wres.GetResponseStream();
|
||||
resp.Load(ress);
|
||||
return resp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,125 +1,125 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Sender part of Selective repeat ARQ for a particular NetChannel
|
||||
/// </summary>
|
||||
internal sealed class NetUnreliableSenderChannel : NetSenderChannelBase
|
||||
{
|
||||
private NetConnection m_connection;
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private int m_sendStart;
|
||||
|
||||
private NetBitVector m_receivedAcks;
|
||||
|
||||
internal override int WindowSize { get { return m_windowSize; } }
|
||||
|
||||
internal NetUnreliableSenderChannel(NetConnection connection, int windowSize)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_windowSize = windowSize;
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
|
||||
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
|
||||
}
|
||||
|
||||
internal override int GetAllowedSends()
|
||||
{
|
||||
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize;
|
||||
NetException.Assert(retval >= 0 && retval <= m_windowSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
m_receivedAcks.Clear();
|
||||
m_queuedSends.Clear();
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
}
|
||||
|
||||
internal override NetSendResult Enqueue(NetOutgoingMessage message)
|
||||
{
|
||||
int queueLen = m_queuedSends.Count + 1;
|
||||
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
if (queueLen > left)
|
||||
return NetSendResult.Dropped;
|
||||
|
||||
m_queuedSends.Enqueue(message);
|
||||
return NetSendResult.Sent;
|
||||
}
|
||||
|
||||
// call this regularely
|
||||
internal override void SendQueuedMessages(float now)
|
||||
{
|
||||
int num = GetAllowedSends();
|
||||
if (num < 1)
|
||||
return;
|
||||
|
||||
// queued sends
|
||||
while (m_queuedSends.Count > 0 && num > 0)
|
||||
{
|
||||
NetOutgoingMessage om;
|
||||
if (m_queuedSends.TryDequeue(out om))
|
||||
ExecuteSend(now, om);
|
||||
num--;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteSend(float now, NetOutgoingMessage message)
|
||||
{
|
||||
m_connection.m_peer.VerifyNetworkThread();
|
||||
|
||||
int seqNr = m_sendStart;
|
||||
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
m_connection.QueueSendMessage(message, seqNr);
|
||||
|
||||
Interlocked.Decrement(ref message.m_recyclingCount);
|
||||
if (message.m_recyclingCount <= 0)
|
||||
m_connection.m_peer.Recycle(message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
|
||||
// seqNr is the actual nr received
|
||||
internal override void ReceiveAcknowledge(float now, int seqNr)
|
||||
{
|
||||
// late (dupe), on time or early ack?
|
||||
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
|
||||
return; // late/duplicate ack
|
||||
}
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
|
||||
|
||||
// ack arrived right on time
|
||||
NetException.Assert(seqNr == m_windowStart);
|
||||
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Advance window to this position
|
||||
m_receivedAcks[seqNr] = true;
|
||||
|
||||
while (m_windowStart != seqNr)
|
||||
{
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
/// <summary>
|
||||
/// Sender part of Selective repeat ARQ for a particular NetChannel
|
||||
/// </summary>
|
||||
internal sealed class NetUnreliableSenderChannel : NetSenderChannelBase
|
||||
{
|
||||
private NetConnection m_connection;
|
||||
private int m_windowStart;
|
||||
private int m_windowSize;
|
||||
private int m_sendStart;
|
||||
|
||||
private NetBitVector m_receivedAcks;
|
||||
|
||||
internal override int WindowSize { get { return m_windowSize; } }
|
||||
|
||||
internal NetUnreliableSenderChannel(NetConnection connection, int windowSize)
|
||||
{
|
||||
m_connection = connection;
|
||||
m_windowSize = windowSize;
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
m_receivedAcks = new NetBitVector(NetConstants.NumSequenceNumbers);
|
||||
m_queuedSends = new NetQueue<NetOutgoingMessage>(8);
|
||||
}
|
||||
|
||||
internal override int GetAllowedSends()
|
||||
{
|
||||
int retval = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % m_windowSize;
|
||||
NetException.Assert(retval >= 0 && retval <= m_windowSize);
|
||||
return retval;
|
||||
}
|
||||
|
||||
internal override void Reset()
|
||||
{
|
||||
m_receivedAcks.Clear();
|
||||
m_queuedSends.Clear();
|
||||
m_windowStart = 0;
|
||||
m_sendStart = 0;
|
||||
}
|
||||
|
||||
internal override NetSendResult Enqueue(NetOutgoingMessage message)
|
||||
{
|
||||
int queueLen = m_queuedSends.Count + 1;
|
||||
int left = m_windowSize - ((m_sendStart + NetConstants.NumSequenceNumbers) - m_windowStart) % NetConstants.NumSequenceNumbers;
|
||||
if (queueLen > left)
|
||||
return NetSendResult.Dropped;
|
||||
|
||||
m_queuedSends.Enqueue(message);
|
||||
return NetSendResult.Sent;
|
||||
}
|
||||
|
||||
// call this regularely
|
||||
internal override void SendQueuedMessages(float now)
|
||||
{
|
||||
int num = GetAllowedSends();
|
||||
if (num < 1)
|
||||
return;
|
||||
|
||||
// queued sends
|
||||
while (m_queuedSends.Count > 0 && num > 0)
|
||||
{
|
||||
NetOutgoingMessage om;
|
||||
if (m_queuedSends.TryDequeue(out om))
|
||||
ExecuteSend(now, om);
|
||||
num--;
|
||||
}
|
||||
}
|
||||
|
||||
private void ExecuteSend(float now, NetOutgoingMessage message)
|
||||
{
|
||||
m_connection.m_peer.VerifyNetworkThread();
|
||||
|
||||
int seqNr = m_sendStart;
|
||||
m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
m_connection.QueueSendMessage(message, seqNr);
|
||||
|
||||
Interlocked.Decrement(ref message.m_recyclingCount);
|
||||
if (message.m_recyclingCount <= 0)
|
||||
m_connection.m_peer.Recycle(message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly
|
||||
// seqNr is the actual nr received
|
||||
internal override void ReceiveAcknowledge(float now, int seqNr)
|
||||
{
|
||||
// late (dupe), on time or early ack?
|
||||
int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart);
|
||||
|
||||
if (relate < 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr);
|
||||
return; // late/duplicate ack
|
||||
}
|
||||
|
||||
if (relate == 0)
|
||||
{
|
||||
//m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr);
|
||||
|
||||
// ack arrived right on time
|
||||
NetException.Assert(seqNr == m_windowStart);
|
||||
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Advance window to this position
|
||||
m_receivedAcks[seqNr] = true;
|
||||
|
||||
while (m_windowStart != seqNr)
|
||||
{
|
||||
m_receivedAcks[m_windowStart] = false;
|
||||
m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_lastReceivedSequenceNumber;
|
||||
|
||||
public NetUnreliableSequencedReceiver(NetConnection connection)
|
||||
: base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage msg)
|
||||
{
|
||||
int nr = msg.m_sequenceNumber;
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(msg.m_receivedMessageType, nr);
|
||||
|
||||
int relate = NetUtility.RelativeSequenceNumber(nr, m_lastReceivedSequenceNumber);
|
||||
if (relate < 0)
|
||||
return; // drop if late
|
||||
|
||||
m_lastReceivedSequenceNumber = nr;
|
||||
m_peer.ReleaseMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetUnreliableSequencedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
private int m_lastReceivedSequenceNumber;
|
||||
|
||||
public NetUnreliableSequencedReceiver(NetConnection connection)
|
||||
: base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage msg)
|
||||
{
|
||||
int nr = msg.m_sequenceNumber;
|
||||
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(msg.m_receivedMessageType, nr);
|
||||
|
||||
int relate = NetUtility.RelativeSequenceNumber(nr, m_lastReceivedSequenceNumber);
|
||||
if (relate < 0)
|
||||
return; // drop if late
|
||||
|
||||
m_lastReceivedSequenceNumber = nr;
|
||||
m_peer.ReleaseMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetUnreliableUnorderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
public NetUnreliableUnorderedReceiver(NetConnection connection)
|
||||
: base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage msg)
|
||||
{
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(msg.m_receivedMessageType, msg.m_sequenceNumber);
|
||||
|
||||
m_peer.ReleaseMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal sealed class NetUnreliableUnorderedReceiver : NetReceiverChannelBase
|
||||
{
|
||||
public NetUnreliableUnorderedReceiver(NetConnection connection)
|
||||
: base(connection)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReceiveMessage(NetIncomingMessage msg)
|
||||
{
|
||||
// ack no matter what
|
||||
m_connection.QueueAck(msg.m_receivedMessageType, msg.m_sequenceNumber);
|
||||
|
||||
m_peer.ReleaseMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
87
Lidgren.Network/Platform/PlatformAndroid.cs
Normal file
87
Lidgren.Network/Platform/PlatformAndroid.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
#if __ANDROID__
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public static partial class NetUtility
|
||||
{
|
||||
private static byte[] s_randomMacBytes;
|
||||
|
||||
static NetUtility()
|
||||
{
|
||||
s_randomMacBytes = new byte[8];
|
||||
MWCRandom.Instance.NextBytes(s_randomMacBytes);
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static ulong GetPlatformSeed(int seedInc)
|
||||
{
|
||||
ulong seed = (ulong)Environment.TickCount + (ulong)seedInc;
|
||||
return seed ^ ((ulong)(new object().GetHashCode()) << 32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets my local IPv4 address (not necessarily external) and subnet mask
|
||||
/// </summary>
|
||||
public static IPAddress GetMyAddress(out IPAddress mask)
|
||||
{
|
||||
mask = null;
|
||||
try
|
||||
{
|
||||
Android.Net.Wifi.WifiManager wifi = (Android.Net.Wifi.WifiManager)Android.App.Application.Context.GetSystemService(Android.App.Activity.WifiService);
|
||||
if (!wifi.IsWifiEnabled)
|
||||
return null;
|
||||
|
||||
var dhcp = wifi.DhcpInfo;
|
||||
int addr = dhcp.IpAddress;
|
||||
byte[] quads = new byte[4];
|
||||
for (int k = 0; k < 4; k++)
|
||||
quads[k] = (byte)((addr >> k * 8) & 0xFF);
|
||||
return new IPAddress(quads);
|
||||
}
|
||||
catch // Catch Access Denied errors
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] GetMacAddressBytes()
|
||||
{
|
||||
return s_randomMacBytes;
|
||||
}
|
||||
|
||||
public static void Sleep(int milliseconds)
|
||||
{
|
||||
System.Threading.Thread.Sleep(milliseconds);
|
||||
}
|
||||
|
||||
public static IPAddress GetBroadcastAddress()
|
||||
{
|
||||
return IPAddress.Broadcast;
|
||||
}
|
||||
|
||||
public static IPAddress CreateAddressFromBytes(byte[] bytes)
|
||||
{
|
||||
return new IPAddress(bytes);
|
||||
}
|
||||
|
||||
private static readonly SHA1 s_sha = SHA1.Create();
|
||||
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
|
||||
{
|
||||
return s_sha.ComputeHash(bytes, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class NetTime
|
||||
{
|
||||
private static readonly long s_timeInitialized = Environment.TickCount;
|
||||
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
86
Lidgren.Network/Platform/PlatformConstrained.cs
Normal file
86
Lidgren.Network/Platform/PlatformConstrained.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
#if __CONSTRAINED__ || UNITY_STANDALONE_LINUX
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public static partial class NetUtility
|
||||
{
|
||||
private static byte[] s_randomMacBytes;
|
||||
|
||||
static NetUtility()
|
||||
{
|
||||
s_randomMacBytes = new byte[8];
|
||||
MWCRandom.Instance.NextBytes(s_randomMacBytes);
|
||||
}
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static ulong GetPlatformSeed(int seedInc)
|
||||
{
|
||||
ulong seed = (ulong)Environment.TickCount + (ulong)seedInc;
|
||||
return seed ^ ((ulong)(new object().GetHashCode()) << 32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets my local IPv4 address (not necessarily external) and subnet mask
|
||||
/// </summary>
|
||||
public static IPAddress GetMyAddress(out IPAddress mask)
|
||||
{
|
||||
mask = null;
|
||||
#if UNITY_ANDROID || UNITY_STANDALONE_OSX || UNITY_STANDALONE_WIN || UNITY_STANDALONE_LINUX || UNITY_IOS
|
||||
try
|
||||
{
|
||||
if (!(UnityEngine.Application.internetReachability == UnityEngine.NetworkReachability.NotReachable))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return IPAddress.Parse(UnityEngine.Network.player.externalIP);
|
||||
}
|
||||
catch // Catch Access Denied errors
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
return null;
|
||||
}
|
||||
|
||||
public static byte[] GetMacAddressBytes()
|
||||
{
|
||||
return s_randomMacBytes;
|
||||
}
|
||||
|
||||
public static IPAddress GetBroadcastAddress()
|
||||
{
|
||||
return IPAddress.Broadcast;
|
||||
}
|
||||
|
||||
public static void Sleep(int milliseconds)
|
||||
{
|
||||
System.Threading.Thread.Sleep(milliseconds);
|
||||
}
|
||||
|
||||
public static IPAddress CreateAddressFromBytes(byte[] bytes)
|
||||
{
|
||||
return new IPAddress(bytes);
|
||||
}
|
||||
|
||||
private static readonly SHA1 s_sha = SHA1.Create();
|
||||
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
|
||||
{
|
||||
return s_sha.ComputeHash(bytes, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class NetTime
|
||||
{
|
||||
private static readonly long s_timeInitialized = Environment.TickCount;
|
||||
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
156
Lidgren.Network/Platform/PlatformWin32.cs
Normal file
156
Lidgren.Network/Platform/PlatformWin32.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
#if !__ANDROID__ && !__CONSTRAINED__ && !WINDOWS_RUNTIME && !UNITY_STANDALONE_LINUX
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public static partial class NetUtility
|
||||
{
|
||||
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
|
||||
private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;
|
||||
|
||||
[CLSCompliant(false)]
|
||||
public static ulong GetPlatformSeed(int seedInc)
|
||||
{
|
||||
ulong seed = (ulong)System.Diagnostics.Stopwatch.GetTimestamp();
|
||||
return seed ^ ((ulong)Environment.WorkingSet + (ulong)seedInc);
|
||||
}
|
||||
|
||||
public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }
|
||||
|
||||
private static NetworkInterface GetNetworkInterface()
|
||||
{
|
||||
var computerProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||
if (computerProperties == null)
|
||||
return null;
|
||||
|
||||
var 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;
|
||||
|
||||
// make sure this adapter has any ipv4 addresses
|
||||
IPInterfaceProperties properties = adapter.GetIPProperties();
|
||||
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
|
||||
{
|
||||
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
// Yes it does, return this network interface.
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If available, returns the bytes of the physical (MAC) address for the first usable network interface
|
||||
/// </summary>
|
||||
public static byte[] GetMacAddressBytes()
|
||||
{
|
||||
var ni = GetNetworkInterface();
|
||||
if (ni == null)
|
||||
return null;
|
||||
return ni.GetPhysicalAddress().GetAddressBytes();
|
||||
}
|
||||
|
||||
public static IPAddress GetBroadcastAddress()
|
||||
{
|
||||
var ni = GetNetworkInterface();
|
||||
if (ni == null)
|
||||
return null;
|
||||
|
||||
var properties = ni.GetIPProperties();
|
||||
foreach (UnicastIPAddressInformation unicastAddress in properties.UnicastAddresses)
|
||||
{
|
||||
if (unicastAddress != null && unicastAddress.Address != null && unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork)
|
||||
{
|
||||
var mask = unicastAddress.IPv4Mask;
|
||||
byte[] ipAdressBytes = unicastAddress.Address.GetAddressBytes();
|
||||
byte[] subnetMaskBytes = mask.GetAddressBytes();
|
||||
|
||||
if (ipAdressBytes.Length != subnetMaskBytes.Length)
|
||||
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
|
||||
|
||||
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
|
||||
for (int i = 0; i < broadcastAddress.Length; i++)
|
||||
{
|
||||
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
|
||||
}
|
||||
return new IPAddress(broadcastAddress);
|
||||
}
|
||||
}
|
||||
return IPAddress.Broadcast;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets my local IPv4 address (not necessarily external) and subnet mask
|
||||
/// </summary>
|
||||
public static IPAddress GetMyAddress(out IPAddress mask)
|
||||
{
|
||||
var 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;
|
||||
}
|
||||
|
||||
public static void Sleep(int milliseconds)
|
||||
{
|
||||
System.Threading.Thread.Sleep(milliseconds);
|
||||
}
|
||||
|
||||
public static IPAddress CreateAddressFromBytes(byte[] bytes)
|
||||
{
|
||||
return new IPAddress(bytes);
|
||||
}
|
||||
|
||||
private static readonly SHA256 s_sha = SHA256.Create();
|
||||
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
|
||||
{
|
||||
return s_sha.ComputeHash(bytes, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class NetTime
|
||||
{
|
||||
private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
|
||||
private static readonly 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; } }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
102
Lidgren.Network/Platform/PlatformWinRT.cs
Normal file
102
Lidgren.Network/Platform/PlatformWinRT.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
#if WINDOWS_RUNTIME
|
||||
//
|
||||
//
|
||||
//
|
||||
// Completely broken right now
|
||||
//
|
||||
//
|
||||
//
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
public class NetAddress
|
||||
{
|
||||
public static readonly HostName Any = null;
|
||||
}
|
||||
|
||||
public class NetEndPoint
|
||||
{
|
||||
public NetEndPoint(HostName hostname, int port) { HostName = hostname; Port = port; }
|
||||
public NetEndPoint(HostName hostname, string port) { HostName = hostname; Port = int.Parse(port); }
|
||||
public NetEndPoint(string hostname, int port) { HostName = (hostname == null) ? null : new HostName(hostname); Port = port; }
|
||||
public HostName HostName;
|
||||
public int Port;
|
||||
public override string ToString() { return string.Format("{0}:{1}", HostName, Port); }
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HostName.RawName.GetHashCode() + Port;
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var ep = obj as NetEndPoint;
|
||||
if (ep == null) return false;
|
||||
if (Port != ep.Port) return false;
|
||||
return HostName.RawName.Equals(ep.HostName.RawName);
|
||||
}
|
||||
};
|
||||
|
||||
public static partial class NetUtility
|
||||
{
|
||||
[CLSCompliant(false)]
|
||||
public static ulong GetPlatformSeed(int seedInc)
|
||||
{
|
||||
ulong seed = (ulong)Environment.TickCount + (ulong)seedInc;
|
||||
return seed ^ ((ulong)(new object().GetHashCode()) << 32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the physical (MAC) address for the first usable network interface
|
||||
/// </summary>
|
||||
public static PhysicalAddress GetMacAddress()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static IPAddress GetBroadcastAddress()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets my local IPv4 address (not necessarily external) and subnet mask
|
||||
/// </summary>
|
||||
public static IPAddress GetMyAddress(out IPAddress mask)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public static void Sleep(int milliseconds)
|
||||
{
|
||||
Task.Delay(50).Wait();
|
||||
}
|
||||
|
||||
public static NetAddress CreateAddressFromBytes(byte[] bytes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private static readonly SHA1CryptoServiceProvider s_sha = new SHA1CryptoServiceProvider();
|
||||
public static byte[] ComputeSHAHash(byte[] bytes, int offset, int count)
|
||||
{
|
||||
return s_sha.ComputeHash(bytes, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
public static partial class NetTime
|
||||
{
|
||||
private static readonly long s_timeInitialized = Environment.TickCount;
|
||||
|
||||
/// <summary>
|
||||
/// Get number of seconds since the application started
|
||||
/// </summary>
|
||||
public static double Now { get { return (double)((uint)Environment.TickCount - s_timeInitialized) / 1000.0; } }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,37 +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("")]
|
||||
[assembly: AssemblyProduct("Lidgren.Network")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[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("78f2a988-46e1-4fc4-b7b4-dd2cbe46da6a")]
|
||||
|
||||
// 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("2012.1.7.0")]
|
||||
[assembly: AssemblyFileVersion("2012.1.7.0")]
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Lidgren.Network")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Lidgren.Network")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2012")]
|
||||
[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("78f2a988-46e1-4fc4-b7b4-dd2cbe46da6a")]
|
||||
|
||||
// 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("2012.1.7.0")]
|
||||
[assembly: AssemblyFileVersion("2012.1.7.0")]
|
||||
[assembly: System.CLSCompliant(true)]
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class SenderChannelBase
|
||||
{
|
||||
internal abstract NetSendResult Send(float now, NetOutgoingMessage message);
|
||||
internal abstract void SendQueuedMessages(float now);
|
||||
internal abstract void Reset();
|
||||
}
|
||||
}
|
||||
using System;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
internal abstract class SenderChannelBase
|
||||
{
|
||||
internal abstract NetSendResult Send(float now, NetOutgoingMessage message);
|
||||
internal abstract void SendQueuedMessages(float now);
|
||||
internal abstract void Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GasPropagationTest", "GasPropagationTest\GasPropagationTest.csproj", "{A935EE41-4BC3-4D04-9E82-04503092F6D3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Debug|x86.ActiveCfg = Release|x86
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Debug|x86.Build.0 = Release|x86
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Release|x86.ActiveCfg = Release|x86
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GasPropagationTest", "GasPropagationTest\GasPropagationTest.csproj", "{A935EE41-4BC3-4D04-9E82-04503092F6D3}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Debug|x86.ActiveCfg = Release|x86
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Debug|x86.Build.0 = Release|x86
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Release|x86.ActiveCfg = Release|x86
|
||||
{A935EE41-4BC3-4D04-9E82-04503092F6D3}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<Application x:Class="GasPropagationTest.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
<Application x:Class="GasPropagationTest.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace GasPropagationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
|
||||
namespace GasPropagationTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user