diff --git a/src/Syroot.OnlineWorms.Server/Client.cs b/src/Syroot.OnlineWorms.Server/Client.cs
new file mode 100644
index 0000000..8eef7d7
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Client.cs
@@ -0,0 +1,55 @@
+using System.Net.Sockets;
+using Syroot.Worms.OnlineWorms.Server.Net;
+
+namespace Syroot.Worms.OnlineWorms.Server
+{
+ ///
+ /// Represents a connection with an Online Worms client which replies to received packets appropriately.
+ ///
+ internal class Client : PacketHandler
+ {
+ // ---- FIELDS -------------------------------------------------------------------------------------------------
+
+ private readonly Server _server;
+
+ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+
+ ///
+ /// Initializes a new instance of the class accepted by the given
+ /// through the provided .
+ ///
+ /// The representing the connection to the client.
+ /// The instance with which this client communicates.
+ public Client(TcpClient tcpClient, Server server)
+ : base(tcpClient)
+ {
+ _server = server;
+ }
+
+ // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
+
+ private Packet HandleConnect(ConnectQueryPacket connectPacket)
+ {
+ return new ConnectReplyPacket
+ {
+ Unknown = "Online Worms Private Server",
+ Version = "114"
+ };
+ }
+
+ //private Packet HandleLogin(LoginQueryPacket loginPacket)
+ //{
+ // return new LoginReplyPacket
+ // {
+ // LoginResult = LoginResult.Success
+ // };
+ //}
+
+#if DEBUG
+ private Packet HandleRaw(RawQueryPacket rawPacket)
+ {
+ return null;
+ }
+#endif
+ }
+}
\ No newline at end of file
diff --git a/src/Syroot.OnlineWorms.Server/Core/MathFuncs.cs b/src/Syroot.OnlineWorms.Server/Core/MathFuncs.cs
new file mode 100644
index 0000000..72ac934
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Core/MathFuncs.cs
@@ -0,0 +1,26 @@
+namespace Syroot.Worms.OnlineWorms.Server.Core
+{
+ ///
+ /// Represents common mathematical operations.
+ ///
+ internal static class MathFuncs
+ {
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ ///
+ /// Returns the nearest power of two bigger than the given .
+ ///
+ /// The value to get the nearest, bigger power of two for.
+ /// The nearest, bigger power of two.
+ internal static int NextPowerOfTwo(int value)
+ {
+ value--;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ return ++value;
+ }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/GameConnection.cs b/src/Syroot.OnlineWorms.Server/Net/GameConnection.cs
new file mode 100644
index 0000000..414841a
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/GameConnection.cs
@@ -0,0 +1,128 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents a stream of instances send between a game client and server.
+ ///
+ internal class GameConnection
+ {
+ // A complete packet consists of the following:
+ // - ushort id
+ // - ushort dataSize
+ // - byte[dataSize] data
+
+ // ---- CONSTANTS ----------------------------------------------------------------------------------------------
+
+ private const int _maxDataSize = 2048;
+
+ // ---- FIELDS -------------------------------------------------------------------------------------------------
+
+ private readonly Stream _tcpStream;
+ private readonly byte[] _receiveBuffer = new byte[_maxDataSize];
+ private readonly byte[] _sendDataBuffer = new byte[_maxDataSize];
+ private readonly BinaryStream _writeStream;
+
+ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+
+ ///
+ /// Initializes a new instance of the class, receiving and sending packets from and
+ /// to the given .
+ ///
+ /// The to communicate with.
+ internal GameConnection(TcpClient tcpClient)
+ {
+ _tcpStream = tcpClient.GetStream();
+ _writeStream = new BinaryStream(new MemoryStream(_sendDataBuffer, 0, _maxDataSize, true),
+ encoding: Encoding.GetEncoding(949), stringCoding: StringCoding.Int16CharCount);
+ }
+
+ // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
+
+ ///
+ /// Returns a read from the stream. If the packet could not be read,
+ /// is returned.
+ ///
+ /// The read instance or if the packet could not be read.
+ ///
+ internal Packet ReceivePacket()
+ {
+ // Receive the raw packet data.
+ ushort id;
+ ushort dataSize;
+ try
+ {
+ id = _tcpStream.ReadUInt16();
+ dataSize = _tcpStream.ReadUInt16();
+ ReadComplete(_receiveBuffer, 0, dataSize);
+ }
+ catch (IOException) { return null; } // The underlying socket closed.
+ catch (ObjectDisposedException) { return null; } // The underlying stream closed.
+
+ // Deserialize and return the packet.
+ Packet packet = PacketFactory.Create(id);
+ using (BinaryStream readStream = new BinaryStream(new MemoryStream(_receiveBuffer, 0, dataSize, false),
+ encoding: Encoding.GetEncoding(949), stringCoding: StringCoding.Int16CharCount))
+ {
+ packet.Deserialize(readStream);
+ }
+ return packet;
+ }
+
+ ///
+ /// Sends the given and returns if the packet was sent
+ /// successfully.
+ ///
+ /// The to send.
+ /// when the packet was sent.
+ internal bool SendPacket(Packet packet)
+ {
+ // Serialize the raw packet data.
+ _writeStream.Position = 0;
+ packet.Serialize(_writeStream);
+ ushort dataSize = (ushort)_writeStream.Position;
+
+ // Send the data and return success.
+ try
+ {
+ _tcpStream.WriteUInt16(PacketFactory.GetID(packet));
+ _tcpStream.WriteUInt16(dataSize);
+ _tcpStream.Write(_sendDataBuffer, 0, dataSize);
+ return true;
+ }
+ catch (IOException) { return false; } // A network error appeared, and communication should end.
+ catch (ObjectDisposedException) { return false; } // The underlying stream was most apparently closed.
+ }
+
+ // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
+
+ ///
+ /// Reads number of bytes to the given , starting at the
+ /// specified , and returns the number of bytes read. Since this method ensures that
+ /// the requested number of bytes is always read, it always returns , and otherwise
+ /// throws an if the required bytes could not be read.
+ ///
+ /// The byte array to write the read data to.
+ /// The offset into at which to start writing data.
+ /// The number of bytes to read into .
+ /// The cannot store the requested number of
+ /// bytes.
+ /// The requested number of bytes could not be read.
+ private void ReadComplete(byte[] buffer, int offset, int count)
+ {
+ int totalRead = 0;
+ while (totalRead < count)
+ {
+ // Read returns 0 only when the underlying socket is closed, otherwise it blocks.
+ int read = _tcpStream.Read(buffer, offset + totalRead, count - totalRead);
+ if (read == 0)
+ throw new IOException("The underlying stream has closed, 0 bytes were retrieved.");
+ totalRead += read;
+ }
+ }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/PacketAttribute.cs b/src/Syroot.OnlineWorms.Server/Net/PacketAttribute.cs
new file mode 100644
index 0000000..04c423c
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/PacketAttribute.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ internal class PacketAttribute : Attribute
+ {
+ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+
+ public PacketAttribute(ushort id)
+ {
+ ID = id;
+ }
+
+ // ---- PROPERTIES ---------------------------------------------------------------------------------------------
+
+ public ushort ID { get; }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/PacketFactory.cs b/src/Syroot.OnlineWorms.Server/Net/PacketFactory.cs
new file mode 100644
index 0000000..9afd7e1
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/PacketFactory.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents a factory creating instances by mapping their ID to types to instantiate.
+ ///
+ internal static class PacketFactory
+ {
+ // ---- FIELDS -------------------------------------------------------------------------------------------------
+
+ private static readonly Dictionary _packetTypeMap = new Dictionary();
+
+ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+
+ static PacketFactory()
+ {
+ // Cache the ID-to-packet-class map.
+ foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
+ {
+ foreach (PacketAttribute packetAttribute in type.GetCustomAttributes())
+ {
+ if (_packetTypeMap.ContainsKey(packetAttribute.ID))
+ throw new InvalidOperationException($"Packet {packetAttribute.ID} mapped to multiple classes.");
+ _packetTypeMap.Add(packetAttribute.ID, type);
+ }
+ }
+ }
+
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ ///
+ /// Creates a new instance mapped to the given .
+ ///
+ /// The ID of the packet class to instantiate.
+ /// The created instance.
+ /// No class was mapped to the given packet ID.
+ internal static Packet Create(ushort id)
+ {
+#if DEBUG
+ return _packetTypeMap.TryGetValue(id, out Type type)
+ ? (Packet)Activator.CreateInstance(type, true)
+ : new RawQueryPacket(id);
+#else
+ return (Packet)Activator.CreateInstance(_packetTypeMap[id], true);
+#endif
+ }
+
+ ///
+ /// Gets the ID for the class of the given .
+ ///
+ /// The type of the .
+ /// The packet, whose class ID will be returned.
+ /// The ID of the class.
+ internal static ushort GetID(T packet) where T : Packet
+ {
+#if DEBUG
+ if (packet is RawQueryPacket rawPacket)
+ return rawPacket.ID;
+#endif
+ return _packetTypeMap.Where(x => x.Value == packet.GetType()).First().Key;
+ }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/PacketHandler.cs b/src/Syroot.OnlineWorms.Server/Net/PacketHandler.cs
new file mode 100644
index 0000000..c85c75f
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/PacketHandler.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Reflection;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents a class capable of dispatching received instances to a corresponding method.
+ ///
+ internal class PacketHandler : IDisposable
+ {
+ // ---- FIELDS -------------------------------------------------------------------------------------------------
+
+ private static readonly Dictionary> _packetHandlers
+ = new Dictionary>();
+
+ private readonly TcpClient _tcpClient;
+ private readonly Dictionary _handlerMethods;
+ private readonly GameConnection _connection;
+ private bool _disposed;
+
+ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+
+ internal PacketHandler(TcpClient tcpClient)
+ {
+ Console.WriteLine($"{tcpClient.Client.RemoteEndPoint} connected");
+ _tcpClient = tcpClient;
+ _handlerMethods = GetHandlerMethods();
+ _connection = new GameConnection(_tcpClient);
+ }
+
+ // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
+
+ public void Dispose()
+ {
+ // Do not change this code. Put cleanup code in Dispose(bool disposing).
+ Dispose(true);
+ }
+
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ ///
+ /// Starts handling packets incoming from this client and calls corresponding methods.
+ ///
+ internal void Accept()
+ {
+ Packet packet;
+ while ((packet = _connection.ReceivePacket()) != null)
+ HandlePacket(packet);
+ }
+
+ // ---- METHODS (PROTECTED) ------------------------------------------------------------------------------------
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!_disposed)
+ {
+ if (disposing)
+ _tcpClient.Dispose();
+
+ _disposed = true;
+ }
+ }
+
+ // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
+
+ private Dictionary GetHandlerMethods()
+ {
+ Type classType = GetType();
+ if (!_packetHandlers.TryGetValue(classType, out Dictionary handlerMethods))
+ {
+ handlerMethods = new Dictionary();
+ // Find all packet handling methods which are methods accepting and returning a specific packet.
+ foreach (MethodInfo method in classType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance))
+ {
+ Type inPacket = method.GetParameters().FirstOrDefault()?.ParameterType;
+ if (typeof(Packet).IsAssignableFrom(inPacket) && typeof(Packet).IsAssignableFrom(method.ReturnType))
+ handlerMethods.Add(inPacket, method);
+ }
+ _packetHandlers.Add(classType, handlerMethods);
+ }
+ return handlerMethods;
+ }
+
+ private void HandlePacket(Packet inPacket)
+ {
+ Type packetType = inPacket.GetType();
+ Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} >> {packetType.Name}");
+
+ // Invoke the handler and send back any packet resulting from it.
+ if (_handlerMethods[packetType].Invoke(this, new[] { inPacket }) is Packet outPacket)
+ {
+ Console.WriteLine($"{_tcpClient.Client.RemoteEndPoint} << {outPacket.GetType().Name}");
+ _connection.SendPacket(outPacket);
+ }
+ }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectQueryPacket.cs b/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectQueryPacket.cs
new file mode 100644
index 0000000..71edf94
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectQueryPacket.cs
@@ -0,0 +1,18 @@
+using System;
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents the client request for a .
+ ///
+ [Packet(0x800E)]
+ internal class ConnectQueryPacket : Packet
+ {
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ internal override void Deserialize(BinaryStream stream) { }
+
+ internal override void Serialize(BinaryStream stream) => throw new NotImplementedException();
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectReplyPacket.cs b/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectReplyPacket.cs
new file mode 100644
index 0000000..9224c21
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/Packets/ConnectReplyPacket.cs
@@ -0,0 +1,28 @@
+using System;
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents the server response to a .
+ ///
+ [Packet(0x800F)]
+ internal class ConnectReplyPacket : Packet
+ {
+ // ---- PROPERTIES ---------------------------------------------------------------------------------------------
+
+ public string Unknown { get; set; }
+
+ public string Version { get; set; }
+
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ internal override void Deserialize(BinaryStream stream) => throw new NotImplementedException();
+
+ internal override void Serialize(BinaryStream stream)
+ {
+ stream.WriteString(Unknown);
+ stream.WriteString(Version);
+ }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packets/LoginReplyPacket.cs b/src/Syroot.OnlineWorms.Server/Net/Packets/LoginReplyPacket.cs
new file mode 100644
index 0000000..02f33a8
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/Packets/LoginReplyPacket.cs
@@ -0,0 +1,40 @@
+using System;
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents the server response to a .
+ ///
+ [Packet(0x8001)]
+ internal class LoginReplyPacket : Packet
+ {
+ // ---- PROPERTIES ---------------------------------------------------------------------------------------------
+
+ public LoginResult LoginResult { get; set; }
+
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ internal override void Deserialize(BinaryStream stream) => throw new NotImplementedException();
+
+ internal override void Serialize(BinaryStream stream)
+ {
+ stream.WriteBoolean(LoginResult == LoginResult.Success);
+ stream.WriteEnum(LoginResult);
+ }
+ }
+
+ internal enum LoginResult : byte
+ {
+ Success,
+ IDAlreadyInUse = 3,
+ UnqualifiedID = 4,
+ IncorrectID = 6,
+ IncorrectPassword = 7,
+ DuplicateIDs = 9,
+ IncorrectVersion = 10,
+ BannedID = 11,
+ PublicAccessBanned = 12,
+ TemporarilyBannedID = 13
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packets/Packet.cs b/src/Syroot.OnlineWorms.Server/Net/Packets/Packet.cs
new file mode 100644
index 0000000..f9cef3a
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/Packets/Packet.cs
@@ -0,0 +1,17 @@
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents a packet with an ID specifying its contents. To allow the server to instantiate child packet classes,
+ /// decorate it with the .
+ ///
+ internal abstract class Packet
+ {
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ internal abstract void Deserialize(BinaryStream stream);
+
+ internal abstract void Serialize(BinaryStream stream);
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Net/Packets/RawQueryPacket.cs b/src/Syroot.OnlineWorms.Server/Net/Packets/RawQueryPacket.cs
new file mode 100644
index 0000000..676e386
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Net/Packets/RawQueryPacket.cs
@@ -0,0 +1,38 @@
+#if DEBUG
+using Syroot.BinaryData;
+
+namespace Syroot.Worms.OnlineWorms.Server.Net
+{
+ ///
+ /// Represents a special fallback packet which simply stores any ID and the raw client packet data.
+ ///
+ internal class RawQueryPacket : Packet
+ {
+ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+
+ internal RawQueryPacket(ushort id)
+ {
+ ID = id;
+ }
+
+ // ---- PROPERTIES ---------------------------------------------------------------------------------------------
+
+ internal ushort ID { get; }
+
+ internal byte[] Data { get; set; }
+
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ internal override void Deserialize(BinaryStream stream)
+ {
+ Data = new byte[stream.Length];
+ stream.Read(Data, 0, Data.Length);
+ }
+
+ internal override void Serialize(BinaryStream stream)
+ {
+ stream.Write(Data, 0, Data.Length);
+ }
+ }
+}
+#endif
diff --git a/src/Syroot.OnlineWorms.Server/Program.cs b/src/Syroot.OnlineWorms.Server/Program.cs
new file mode 100644
index 0000000..f1e1c73
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Program.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Text;
+
+namespace Syroot.Worms.OnlineWorms.Server
+{
+ ///
+ /// Represents the main class of the application containing the entry point.
+ ///
+ internal class Program
+ {
+ // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
+
+ static Program()
+ {
+ Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
+ }
+
+ // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
+
+ private static void Main(string[] args)
+ {
+ try
+ {
+ new Server().Listen(17022);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Unhandled exception: {ex}");
+ }
+ }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Server.cs b/src/Syroot.OnlineWorms.Server/Server.cs
new file mode 100644
index 0000000..c97351d
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Server.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading.Tasks;
+
+namespace Syroot.Worms.OnlineWorms.Server
+{
+ ///
+ /// Represents a server listening for incoming client connections and dispatching them into
+ /// instances.
+ ///
+ public class Server
+ {
+ // ---- FIELDS -------------------------------------------------------------------------------------------------
+
+ private readonly List _clients = new List();
+
+ // ---- METHODS (INTERNAL) -------------------------------------------------------------------------------------
+
+ ///
+ /// Starts the server by accepting new client connections under the given and
+ /// dispatching them into asynchronous handling threads. This call is blocking.
+ ///
+ /// The port on which to listen for new client connections.
+ internal void Listen(int port)
+ {
+ TcpListener tcpListener = new TcpListener(IPAddress.Any, port);
+ tcpListener.Start();
+ Console.WriteLine($"Listening on port {port}...");
+
+ while (true)
+ {
+ Client client = new Client(tcpListener.AcceptTcpClient(), this);
+ _clients.Add(client);
+
+ Task.Run(client.Accept).ContinueWith(_ =>
+ {
+ _clients.Remove(client);
+ client.Dispose();
+ });
+ }
+ }
+ }
+}
diff --git a/src/Syroot.OnlineWorms.Server/Syroot.Worms.OnlineWorms.Server.csproj b/src/Syroot.OnlineWorms.Server/Syroot.Worms.OnlineWorms.Server.csproj
new file mode 100644
index 0000000..3129073
--- /dev/null
+++ b/src/Syroot.OnlineWorms.Server/Syroot.Worms.OnlineWorms.Server.csproj
@@ -0,0 +1,14 @@
+
+
+ Exe
+ netcoreapp2.1
+ latest
+
+
+ DEBUG;TRACE
+
+
+
+
+
+
diff --git a/src/Syroot.Worms.sln b/src/Syroot.Worms.sln
index 1725a38..a6888a2 100644
--- a/src/Syroot.Worms.sln
+++ b/src/Syroot.Worms.sln
@@ -11,28 +11,45 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.Scratchpad", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.OnlineWorms.Launcher", "Syroot.Worms.OnlineWorms.Launcher\Syroot.Worms.OnlineWorms.Launcher.csproj", "{510EE83E-9C52-40FD-AC7E-C4981EBF4182}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Syroot.Worms.OnlineWorms.Server", "Syroot.OnlineWorms.Server\Syroot.Worms.OnlineWorms.Server.csproj", "{2A06124C-EA75-4946-9959-4AD3DC754B90}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
+ ReleaseSigned|Any CPU = ReleaseSigned|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DD76B6AA-5A5A-4FCD-95AA-9552977525A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD76B6AA-5A5A-4FCD-95AA-9552977525A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD76B6AA-5A5A-4FCD-95AA-9552977525A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD76B6AA-5A5A-4FCD-95AA-9552977525A1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD76B6AA-5A5A-4FCD-95AA-9552977525A1}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
+ {DD76B6AA-5A5A-4FCD-95AA-9552977525A1}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
{20ACA971-A9D0-4424-9958-9CEE24F43C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{20ACA971-A9D0-4424-9958-9CEE24F43C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20ACA971-A9D0-4424-9958-9CEE24F43C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20ACA971-A9D0-4424-9958-9CEE24F43C9C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {20ACA971-A9D0-4424-9958-9CEE24F43C9C}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
+ {20ACA971-A9D0-4424-9958-9CEE24F43C9C}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
{0D7F9DC3-7268-494E-BA1E-6C01525EB9AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D7F9DC3-7268-494E-BA1E-6C01525EB9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D7F9DC3-7268-494E-BA1E-6C01525EB9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D7F9DC3-7268-494E-BA1E-6C01525EB9AB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0D7F9DC3-7268-494E-BA1E-6C01525EB9AB}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
+ {0D7F9DC3-7268-494E-BA1E-6C01525EB9AB}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
{510EE83E-9C52-40FD-AC7E-C4981EBF4182}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{510EE83E-9C52-40FD-AC7E-C4981EBF4182}.Debug|Any CPU.Build.0 = Debug|Any CPU
{510EE83E-9C52-40FD-AC7E-C4981EBF4182}.Release|Any CPU.ActiveCfg = Release|Any CPU
{510EE83E-9C52-40FD-AC7E-C4981EBF4182}.Release|Any CPU.Build.0 = Release|Any CPU
+ {510EE83E-9C52-40FD-AC7E-C4981EBF4182}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
+ {510EE83E-9C52-40FD-AC7E-C4981EBF4182}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
+ {2A06124C-EA75-4946-9959-4AD3DC754B90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2A06124C-EA75-4946-9959-4AD3DC754B90}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2A06124C-EA75-4946-9959-4AD3DC754B90}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2A06124C-EA75-4946-9959-4AD3DC754B90}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2A06124C-EA75-4946-9959-4AD3DC754B90}.ReleaseSigned|Any CPU.ActiveCfg = Release|Any CPU
+ {2A06124C-EA75-4946-9959-4AD3DC754B90}.ReleaseSigned|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE