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