diff --git a/.gitmodules b/.gitmodules
index 7f6219f..b2db22d 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
[submodule "SteamKit"]
path = SteamKit
url = git@github.com:tuokri/SteamKit.git
-[submodule "NetCoreServer"]
- path = NetCoreServer
- url = https://github.com/chronoxor/NetCoreServer.git
+[submodule "SuperSocket"]
+ path = SuperSocket
+ url = git@github.com:kerryjiang/SuperSocket.git
diff --git a/.idea/.idea.SteamDSStub/.idea/indexLayout.xml b/.idea/.idea.SteamDSStub/.idea/indexLayout.xml
index 7b08163..764ea63 100644
--- a/.idea/.idea.SteamDSStub/.idea/indexLayout.xml
+++ b/.idea/.idea.SteamDSStub/.idea/indexLayout.xml
@@ -1,7 +1,9 @@
-
+
+ SuperSocket
+
diff --git a/.idea/.idea.SteamDSStub/.idea/vcs.xml b/.idea/.idea.SteamDSStub/.idea/vcs.xml
index 94a25f7..09b2430 100644
--- a/.idea/.idea.SteamDSStub/.idea/vcs.xml
+++ b/.idea/.idea.SteamDSStub/.idea/vcs.xml
@@ -2,5 +2,6 @@
+
\ No newline at end of file
diff --git a/A2SServer/A2SServer.cs b/A2SServer/A2SServer.cs
index 950f949..c433ee9 100644
--- a/A2SServer/A2SServer.cs
+++ b/A2SServer/A2SServer.cs
@@ -1,11 +1,91 @@
-using System.Net;
+using System.Buffers;
+using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
-using NetCoreServer;
+using SuperSocket.ProtoBase;
namespace A2SServer;
+public static class Constants
+{
+ public const int A2SPrefix = -1; // 0xFFFFFFFF.
+ public const string A2SQueryStr = "Source Engine Query\0";
+ public const byte A2SInfoRequestHeader = 0x54;
+ public const byte A2SPlayerRequestHeader = 0x55;
+ public const byte A2SRulesRequestHeader = 0x56;
+ public const byte A2SChallengeResponseHeader = 0x41;
+ public const byte A2SInfoResponseHeader = 0x49;
+ public const byte A2SPlayerResponseHeader = 0x44;
+ public const byte A2SRulesResponseHeader = 0x45;
+}
+
+public class A2SRequestPackage
+{
+ public byte Header { get; set; }
+ public int Challenge { get; set; }
+}
+
+public class A2SPackageDecoder : IPackageDecoder
+{
+ private static bool CheckInfoHeader(ref SequenceReader reader)
+ {
+ var queryStr = reader.ReadString(Constants.A2SQueryStr.Length);
+ if (queryStr is not Constants.A2SQueryStr)
+ {
+ throw new ProtocolException("invalid query string");
+ }
+
+ return true;
+ }
+
+ public A2SRequestPackage Decode(ref ReadOnlySequence buffer, object context)
+ {
+ // A2S requests should never exceed 29 bytes.
+ if (buffer.Length is < 5 or > 30)
+ {
+ throw new ProtocolException($"invalid length: {buffer.Length}");
+ }
+
+ var reader = new SequenceReader(buffer);
+ if (!reader.TryReadLittleEndian(out int prefix))
+ {
+ throw new ProtocolException("failed to read prefix");
+ }
+
+ if (prefix != Constants.A2SPrefix)
+ {
+ throw new ProtocolException($"invalid prefix: 0x{prefix:x}");
+ }
+
+ var package = new A2SRequestPackage();
+
+ reader.TryRead(out byte header);
+ package.Header = header;
+
+ var validHeader = header switch
+ {
+ Constants.A2SInfoRequestHeader => true,
+ Constants.A2SPlayerRequestHeader => CheckInfoHeader(ref reader),
+ Constants.A2SRulesRequestHeader => true,
+ _ => false
+ };
+ if (!validHeader)
+ {
+ throw new ProtocolException($"invalid header: 0x{header:x}");
+ }
+
+ if (!reader.TryReadLittleEndian(out int challenge))
+ {
+ throw new ProtocolException("failed to read challenge");
+ }
+
+ package.Challenge = challenge;
+
+ return package;
+ }
+}
+
public class Info
{
public byte Protocol = 0;
@@ -30,251 +110,90 @@ public class Info
public long GameId = 0;
}
-public class A2SServer : UdpServer
+// TODO: better name?
+public static class Utils
{
- private readonly byte[] _prefix = [0xff, 0xff, 0xff, 0xff];
- private const string QueryStr = "Source Engine Query\0";
- private readonly object netLock = new object();
-
- public Info Info { get; set; } = new();
- public Dictionary Rules { get; set; } = new() { ["\0"] = "\0" };
- // public List> Players { get; set; } = new();
-
- private readonly int _challenge = 0;
-
- public A2SServer(IPAddress address, int port) : base(address, port)
- {
- _challenge = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
- }
-
- protected override void OnStarted()
+ public static byte[] MakeInfoResponsePacket(ref Info info)
{
- ReceiveAsync();
- }
-
- protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size)
- {
- // TODO: shit workaround, migrate to SuperSocket.
- Monitor.Enter(netLock);
-
- Console.WriteLine($"OnReceived: {endpoint}: {size}");
-
- // A2S requests should never exceed 29 bytes.
- if (size is < 5 or > 30)
+ var basicInfo = new byte[]
{
- Console.WriteLine($"bad size on request from {endpoint}");
- OnSent(endpoint, 0);
- return;
- }
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ Constants.A2SInfoResponseHeader,
+ info.Protocol,
+ };
+
+ var bytes = basicInfo.Concat(Encoding.ASCII.GetBytes(info.ServerName + '\0'))
+ .Concat(Encoding.ASCII.GetBytes(info.Map + '\0'))
+ .Concat(Encoding.ASCII.GetBytes(info.GameDir + '\0'))
+ .Concat(Encoding.ASCII.GetBytes(info.GameName + '\0'))
+ .Concat(BitConverter.GetBytes(info.AppId));
- var prefix = buffer.Take(4);
- if (!prefix.SequenceEqual(_prefix))
+ var moreInfo = new[]
{
- Console.WriteLine($"bad prefix on request from {endpoint}");
- OnSent(endpoint, 0);
- return;
- }
+ info.Players,
+ info.MaxPlayers,
+ info.NumBots,
+ info.ServerType,
+ info.OperatingSystem,
+ info.PasswordProtected,
+ info.Secure,
+ };
- var ok = false;
- var sendSize = 0;
- var sendBuffer = Array.Empty();
+ bytes = bytes.Concat(moreInfo).Concat(Encoding.ASCII.GetBytes(info.Version + '\0'));
- var header = buffer[4];
- // Console.WriteLine($"header: {header}");
+ const byte edf = 0x80 | 0x10 | 0x20 | 0x01;
+ bytes = bytes.Concat(new[] { edf });
- try
- {
- ok = header switch
- {
- 0x54 => HandleInfoRequest(ref buffer, size, out sendBuffer, out sendSize),
- 0x55 => HandlePlayerRequest(ref buffer, size, out sendBuffer, out sendSize),
- 0x56 => HandleRulesRequest(ref buffer, size, out sendBuffer, out sendSize),
- _ => false
- };
- }
- catch (Exception e)
- {
- Console.WriteLine($"error handling request from: {endpoint}: {e}");
- ok = false;
- }
+ bytes = bytes.Concat(BitConverter.GetBytes(info.Port))
+ .Concat(BitConverter.GetBytes(info.SteamId))
+ .Concat(Encoding.ASCII.GetBytes(info.Keywords + '\0'))
+ .Concat(BitConverter.GetBytes(info.GameId));
- if (ok)
- {
- Console.WriteLine($"responding to 0x{header:X} request from {endpoint}: {sendSize}");
- if (!SendAsync(endpoint, sendBuffer, 0, sendSize))
- {
- Console.WriteLine($"failed to respond to 0x{header:x} request from {endpoint}");
- }
- }
- else
- {
- OnSent(endpoint, 0);
- }
+ return bytes.ToArray();
}
- private bool HandleInfoRequest(ref byte[] buffer, long bufferSize, out byte[] sendBuffer,
- out int sendSize)
+ public static byte[] MakePlayerResponsePacket()
{
- if (bufferSize >= 24)
+ var data = new byte[]
{
- var payload = new ArraySegment(buffer, 5, 20);
- var ascii = Encoding.ASCII.GetString(payload);
- if (ascii.SequenceEqual(QueryStr))
- {
- // Console.WriteLine("ok...");
-
- var basicInfo = new byte[]
- {
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0x49,
- Info.Protocol,
- };
-
- var bytes = basicInfo.Concat(Encoding.ASCII.GetBytes(Info.ServerName + '\0'))
- .Concat(Encoding.ASCII.GetBytes(Info.Map + '\0'))
- .Concat(Encoding.ASCII.GetBytes(Info.GameDir + '\0'))
- .Concat(Encoding.ASCII.GetBytes(Info.GameName + '\0'))
- .Concat(BitConverter.GetBytes(Info.AppId));
-
- var moreInfo = new[]
- {
- Info.Players,
- Info.MaxPlayers,
- Info.NumBots,
- Info.ServerType,
- Info.OperatingSystem,
- Info.PasswordProtected,
- Info.Secure,
- };
-
- bytes = bytes.Concat(moreInfo).Concat(Encoding.ASCII.GetBytes(Info.Version + '\0'));
-
- const byte edf = 0x80 | 0x10 | 0x20 | 0x01;
- bytes = bytes.Concat(new[] { edf });
-
- bytes = bytes.Concat(BitConverter.GetBytes(Info.Port))
- .Concat(BitConverter.GetBytes(Info.SteamId))
- .Concat(Encoding.ASCII.GetBytes(Info.Keywords + '\0'))
- .Concat(BitConverter.GetBytes(Info.GameId));
-
- sendBuffer = bytes.ToArray();
- sendSize = sendBuffer.Length;
- return true;
- }
- }
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ Constants.A2SPlayerResponseHeader,
+ 0x00,
+ };
- sendBuffer = Array.Empty();
- sendSize = 0;
- return false;
+ return data;
}
- private bool HandlePlayerRequest(ref byte[] buffer, long bufferSize, out byte[] sendBuffer,
- out int sendSize)
+ public static byte[] MakeRulesResponsePacket(ref Dictionary rules)
{
- // Console.WriteLine($"HandlePlayerRequest: {bufferSize}");
-
- if (bufferSize == 9)
+ var data = new byte[]
{
- var challengeOk = CheckChallenge(ref buffer);
-
- // Console.WriteLine($"challenge: {challenge:X} challengeOk: {challengeOk}");
-
- IEnumerable allData;
-
- if (!challengeOk)
- {
- allData = MakeChallengePacket();
- }
- else
- {
- var data = new byte[]
- {
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0x44,
- 0x00,
- };
-
- allData = data;
- }
-
- sendBuffer = allData.ToArray();
- sendSize = sendBuffer.Length;
- return true;
- }
+ 0xff,
+ 0xff,
+ 0xff,
+ 0xff,
+ Constants.A2SRulesResponseHeader,
+ };
- sendBuffer = Array.Empty();
- sendSize = 0;
- return false;
- }
+ var rulesLen = (short)rules.Count;
- private bool HandleRulesRequest(ref byte[] buffer, long bufferSize, out byte[] sendBuffer,
- out int sendSize)
- {
- if (bufferSize == 9)
+ List rulesData = [];
+ foreach (var kv in rules)
{
- var challengeOk = CheckChallenge(ref buffer);
-
- // Console.WriteLine($"challenge: {challenge:X} challengeOk: {challengeOk}");
-
- IEnumerable allData;
-
- if (!challengeOk)
- {
- allData = MakeChallengePacket();
- }
- else
- {
- var data = new byte[]
- {
- 0xff,
- 0xff,
- 0xff,
- 0xff,
- 0x45,
- };
-
- var rulesLen = (short)Rules.Count;
-
- List rulesData = [];
- foreach (var kv in Rules)
- {
- rulesData.AddRange(Encoding.ASCII.GetBytes(kv.Key + '\0'));
- rulesData.AddRange(Encoding.ASCII.GetBytes(kv.Value + '\0'));
- }
-
- allData = data.Concat(BitConverter.GetBytes(rulesLen)).Concat(rulesData);
- }
-
- sendBuffer = allData.ToArray();
- sendSize = sendBuffer.Length;
- return true;
+ rulesData.AddRange(Encoding.ASCII.GetBytes(kv.Key + '\0'));
+ rulesData.AddRange(Encoding.ASCII.GetBytes(kv.Value + '\0'));
}
- sendBuffer = Array.Empty();
- sendSize = 0;
- return false;
- }
-
- protected override void OnSent(EndPoint endpoint, long sent)
- {
- Monitor.Exit(netLock);
- ReceiveAsync();
- }
-
- protected override void OnError(SocketError error)
- {
- Monitor.Exit(netLock);
- Console.WriteLine($"A2SServer OnError: {error}");
- ReceiveAsync();
+ return data.Concat(BitConverter.GetBytes(rulesLen)).Concat(rulesData).ToArray();
}
- private byte[] MakeChallengePacket()
+ public static byte[] MakeChallengeResponsePacket(int challenge)
{
var data = new byte[]
{
@@ -282,23 +201,35 @@ private byte[] MakeChallengePacket()
0xff,
0xff,
0xff,
- 0x41,
+ Constants.A2SChallengeResponseHeader,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00
};
- var allData = data.Concat(BitConverter.GetBytes(_challenge));
- return allData.ToArray();
+ var challengeBytes = BitConverter.GetBytes(challenge);
+ data[5] = challengeBytes[0];
+ data[6] = challengeBytes[1];
+ data[7] = challengeBytes[2];
+ data[8] = challengeBytes[3];
+
+ return data;
}
+}
- private bool CheckChallenge(ref byte[] buffer)
+public class A2SPipelineFilter : PipelineFilterBase
+{
+ public override A2SRequestPackage Filter(ref SequenceReader reader)
{
- var payload = new ArraySegment(buffer, 5, 4);
- var challenge = BitConverter.ToInt32(payload);
-
- if (challenge is -1 or 0)
+ var pack = reader.Sequence;
+ try
{
- return false;
+ return DecodePackage(ref pack);
+ }
+ finally
+ {
+ reader.Advance(reader.Length);
}
-
- return challenge == _challenge;
}
}
diff --git a/A2SServer/A2SServer.csproj b/A2SServer/A2SServer.csproj
index fd1b220..c67775c 100644
--- a/A2SServer/A2SServer.csproj
+++ b/A2SServer/A2SServer.csproj
@@ -8,11 +8,16 @@
-
+
-
+
+
+
+
+
+
diff --git a/A2SServer/Program.cs b/A2SServer/Program.cs
index ccc3042..ec18e3a 100644
--- a/A2SServer/Program.cs
+++ b/A2SServer/Program.cs
@@ -1,11 +1,13 @@
-using System.Net.Sockets;
+using A2SServer;
+using Microsoft.Extensions.Hosting;
+using SuperSocket.ProtoBase;
+using SuperSocket;
+using System.Net.Sockets;
using System.Net;
-using System.Runtime.InteropServices;
+using System.Security.Cryptography;
using Tomlyn.Model;
using Tomlyn;
-PosixSignalRegistration.Create(PosixSignal.SIGINT, HandleSignal);
-PosixSignalRegistration.Create(PosixSignal.SIGTERM, HandleSignal);
var fileName = args[0];
Console.WriteLine($"reading config from '{fileName}'");
@@ -44,49 +46,70 @@
Console.WriteLine($"resolved '{host}' to '{addr}'");
-var isRunning = true;
-
-var server = new A2SServer.A2SServer(addr, queryPort);
-server.OptionReuseAddress = true;
-server.OptionReceiveBufferLimit = 100;
-server.OptionSendBufferLimit = 3000;
-
-server.Info.Protocol = protocol;
-server.Info.ServerName = serverName;
-server.Info.Map = map;
-server.Info.GameDir = gameDir;
-server.Info.GameName = gameName;
-server.Info.AppId = 0;
-server.Info.Players = players;
-server.Info.MaxPlayers = maxPlayers;
-server.Info.NumBots = numBots;
-server.Info.ServerType = serverType;
-server.Info.OperatingSystem = os;
-server.Info.PasswordProtected = passwordProtected;
-server.Info.Secure = secure;
-server.Info.Version = version;
-server.Info.Port = gamePort;
-server.Info.SteamId = steamId;
-server.Info.Keywords = keywords;
-server.Info.GameId = appId;
-
-server.Rules = rules;
-
-server.Start();
-
-while (isRunning)
+var info = new Info
{
- Thread.Sleep(100);
-}
+ Protocol = protocol,
+ ServerName = serverName,
+ Map = map,
+ GameDir = gameDir,
+ GameName = gameName,
+ AppId = 0,
+ Players = players,
+ MaxPlayers = maxPlayers,
+ NumBots = numBots,
+ ServerType = serverType,
+ OperatingSystem = os,
+ PasswordProtected = passwordProtected,
+ Secure = secure,
+ Version = version,
+ Port = gamePort,
+ SteamId = steamId,
+ Keywords = keywords,
+ GameId = appId
+};
+
+var challenge = RandomNumberGenerator.GetInt32(int.MinValue, int.MaxValue);
+
+Console.WriteLine($"generated challenge: 0x{challenge:x}");
+
+var socketHost = SuperSocketHostBuilder
+ .Create()
+ .UseUdp()
+ .UsePackageDecoder()
+ .UsePackageHandler(async (s, p) =>
+ {
+ if (p.Challenge != challenge)
+ {
+ // Send challenge response.
+ await s.SendAsync(Utils.MakeChallengeResponsePacket(challenge));
+ return;
+ }
+
+ var response = p.Header switch
+ {
+ Constants.A2SInfoRequestHeader => Utils.MakeInfoResponsePacket(ref info),
+ Constants.A2SRulesRequestHeader => Utils.MakeRulesResponsePacket(ref rules),
+ Constants.A2SPlayerRequestHeader => Utils.MakePlayerResponsePacket(),
+ _ => throw new ProtocolException($"invalid header: 0x{p.Header:x}")
+ };
+
+ Console.WriteLine($"responding to 0x{p.Header:x} request from {s.Channel.RemoteEndPoint}");
+ await s.SendAsync(response);
+ }).ConfigureSuperSocket(options =>
+ {
+ options.Name = "A2SServer";
+ options.Listeners =
+ [
+ new ListenOptions
+ {
+ Ip = addr.ToString(),
+ Port = queryPort
+ }
+ ];
+ }).Build();
+
+await socketHost.RunAsync();
Console.WriteLine("stopping");
-server.Stop();
-
return 0;
-
-void HandleSignal(PosixSignalContext context)
-{
- Console.WriteLine($"got signal: {context.Signal}");
- isRunning = false;
-}
diff --git a/NetCoreServer b/NetCoreServer
deleted file mode 160000
index a55eed2..0000000
--- a/NetCoreServer
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit a55eed227500395b08520a74dcbea48029e581a3
diff --git a/SteamDSStub.sln b/SteamDSStub.sln
index e91dcc4..d967e6e 100644
--- a/SteamDSStub.sln
+++ b/SteamDSStub.sln
@@ -5,11 +5,21 @@ VisualStudioVersion = 17.6.33801.468
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DedicatedServer", "DedicatedServer\DedicatedServer.csproj", "{416D047F-A853-42CD-A277-240E5B3E4FF0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SteamKit2", "SteamKit\SteamKit2\SteamKit2\SteamKit2.csproj", "{4B2B0365-DE37-4B65-B614-3E4E7C05147D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamKit2", "SteamKit\SteamKit2\SteamKit2\SteamKit2.csproj", "{4B2B0365-DE37-4B65-B614-3E4E7C05147D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "A2SServer", "A2SServer\A2SServer.csproj", "{7E86C7A0-9378-4B09-8E58-56E19A6E8841}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "A2SServer", "A2SServer\A2SServer.csproj", "{7E86C7A0-9378-4B09-8E58-56E19A6E8841}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetCoreServer", "NetCoreServer\source\NetCoreServer\NetCoreServer.csproj", "{66332867-9824-46DC-9766-482FE4D2EC65}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuperSocket.Server", "SuperSocket\src\SuperSocket.Server\SuperSocket.Server.csproj", "{520EC6BC-EF24-41CF-BBAE-B6C04A66AE4C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuperSocket.ProtoBase", "SuperSocket\src\SuperSocket.ProtoBase\SuperSocket.ProtoBase.csproj", "{9E0C6E02-54EF-4D75-BA29-57456054A422}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuperSocket.Primitives", "SuperSocket\src\SuperSocket.Primitives\SuperSocket.Primitives.csproj", "{68E6D92C-013F-4683-A376-74C61D675491}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuperSocket.Channel", "SuperSocket\src\SuperSocket.Channel\SuperSocket.Channel.csproj", "{F7083615-4366-427D-B9BA-E6ADBC273B96}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuperSocket.Udp", "SuperSocket\src\SuperSocket.Udp\SuperSocket.Udp.csproj", "{EE8E425B-F3CE-4AFD-91D7-56EA95BAFF6F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SuperSocket.SessionContainer", "SuperSocket\src\SuperSocket.SessionContainer\SuperSocket.SessionContainer.csproj", "{0F92E291-DFB2-4626-A27D-E78EF09495AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -29,12 +39,35 @@ Global
{7E86C7A0-9378-4B09-8E58-56E19A6E8841}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7E86C7A0-9378-4B09-8E58-56E19A6E8841}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7E86C7A0-9378-4B09-8E58-56E19A6E8841}.Release|Any CPU.Build.0 = Release|Any CPU
- {66332867-9824-46DC-9766-482FE4D2EC65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {66332867-9824-46DC-9766-482FE4D2EC65}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {66332867-9824-46DC-9766-482FE4D2EC65}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {66332867-9824-46DC-9766-482FE4D2EC65}.Release|Any CPU.Build.0 = Release|Any CPU
+ {520EC6BC-EF24-41CF-BBAE-B6C04A66AE4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {520EC6BC-EF24-41CF-BBAE-B6C04A66AE4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {520EC6BC-EF24-41CF-BBAE-B6C04A66AE4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {520EC6BC-EF24-41CF-BBAE-B6C04A66AE4C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9E0C6E02-54EF-4D75-BA29-57456054A422}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9E0C6E02-54EF-4D75-BA29-57456054A422}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9E0C6E02-54EF-4D75-BA29-57456054A422}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9E0C6E02-54EF-4D75-BA29-57456054A422}.Release|Any CPU.Build.0 = Release|Any CPU
+ {68E6D92C-013F-4683-A376-74C61D675491}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {68E6D92C-013F-4683-A376-74C61D675491}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {68E6D92C-013F-4683-A376-74C61D675491}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {68E6D92C-013F-4683-A376-74C61D675491}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7083615-4366-427D-B9BA-E6ADBC273B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7083615-4366-427D-B9BA-E6ADBC273B96}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7083615-4366-427D-B9BA-E6ADBC273B96}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7083615-4366-427D-B9BA-E6ADBC273B96}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EE8E425B-F3CE-4AFD-91D7-56EA95BAFF6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EE8E425B-F3CE-4AFD-91D7-56EA95BAFF6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EE8E425B-F3CE-4AFD-91D7-56EA95BAFF6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EE8E425B-F3CE-4AFD-91D7-56EA95BAFF6F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0F92E291-DFB2-4626-A27D-E78EF09495AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0F92E291-DFB2-4626-A27D-E78EF09495AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0F92E291-DFB2-4626-A27D-E78EF09495AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0F92E291-DFB2-4626-A27D-E78EF09495AF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {42E1EAB2-670E-4AF4-BB14-2699206FE6A0}
+ EndGlobalSection
EndGlobal
diff --git a/SuperSocket b/SuperSocket
new file mode 160000
index 0000000..76ad533
--- /dev/null
+++ b/SuperSocket
@@ -0,0 +1 @@
+Subproject commit 76ad533edaa506ad1cc843693b2af87dbc8d3004