Explorar o código

添加 Protocol 基架

SunnyCase %!s(int64=8) %!d(string=hai) anos
pai
achega
497db54ed4

+ 2 - 1
.gitignore

@@ -258,4 +258,5 @@ paket-files/
 
 # Python Tools for Visual Studio (PTVS)
 __pycache__/
-*.pyc
+*.pyc
+*.txt

+ 34 - 0
src/MineCase.Gateway/MineCase.Gateway.csproj

@@ -0,0 +1,34 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFramework>netcoreapp1.1</TargetFramework>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <None Remove="OrleansConfiguration.dev.xml" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Content Include="OrleansConfiguration.dev.xml">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  
+  <ItemGroup>
+    <None Remove="*.log;*.txt" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Orleans.Client" Version="2.0.0-preview2-20170724" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\MineCase.Protocol\MineCase.Protocol.csproj" />
+  </ItemGroup>
+
+</Project>

+ 65 - 0
src/MineCase.Gateway/Network/ClientSession.cs

@@ -0,0 +1,65 @@
+using MineCase.Protocol;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MineCase.Gateway.Network
+{
+    class ClientSession : IDisposable
+    {
+        private readonly TcpClient _tcpClient;
+        private bool _useCompression = false;
+
+        public ClientSession(TcpClient tcpClient)
+        {
+            _tcpClient = tcpClient;
+        }
+
+        public async Task Startup(CancellationToken cancellationToken)
+        {
+            using (var remoteStream = _tcpClient.GetStream())
+            {
+                while (!cancellationToken.IsCancellationRequested)
+                {
+                    await DispatchIncommingPacket(remoteStream);
+                }
+            }
+        }
+
+        private async Task DispatchIncommingPacket(Stream remoteStream)
+        {
+            if(_useCompression)
+            {
+                var packet = await CompressedPacket.DeserializeAsync(remoteStream);
+            }
+            else
+            {
+                var packet = await UncompressedPacket.DeserializeAsync(remoteStream);
+            }
+        }
+
+        #region IDisposable Support
+        private bool disposedValue = false; // 要检测冗余调用
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (!disposedValue)
+            {
+                if (disposing)
+                {
+                    _tcpClient.Dispose();
+                }
+                disposedValue = true;
+            }
+        }
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+        #endregion
+    }
+}

+ 45 - 0
src/MineCase.Gateway/Network/ConnectionRouter.cs

@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MineCase.Gateway.Network
+{
+    class ConnectionRouter
+    {
+        private readonly TcpListener _listener;
+
+        public ConnectionRouter()
+        {
+            _listener = new TcpListener(new IPEndPoint(IPAddress.Any, 25565));
+        }
+
+        public async Task Startup(CancellationToken cancellationToken)
+        {
+            _listener.Start();
+            while (!cancellationToken.IsCancellationRequested)
+            {
+                DispatchIncomingClient(await _listener.AcceptTcpClientAsync(), cancellationToken);
+            }
+            _listener.Stop();
+        }
+
+        private async void DispatchIncomingClient(TcpClient tcpClient, CancellationToken cancellationToken)
+        {
+            try
+            {
+                using (var session = new ClientSession(tcpClient))
+                {
+                    await session.Startup(cancellationToken);
+                }
+            }
+            catch (Exception ex)
+            {
+
+            }
+        }
+    }
+}

+ 3 - 0
src/MineCase.Gateway/OrleansConfiguration.dev.xml

@@ -0,0 +1,3 @@
+<ClientConfiguration xmlns="urn:orleans">
+  <Gateway Address="localhost" Port="30000"/>
+</ClientConfiguration>

+ 67 - 0
src/MineCase.Gateway/Program.cs

@@ -0,0 +1,67 @@
+using Microsoft.Extensions.Configuration;
+using Orleans;
+using System;
+using System.IO;
+using Microsoft.Extensions.DependencyInjection;
+using System.Threading;
+using Microsoft.Extensions.Logging;
+using MineCase.Gateway.Network;
+
+namespace MineCase.Gateway
+{
+    class Program
+    {
+        public static IConfiguration Configuration { get; private set; }
+
+        private static IClusterClient _clusterClient;
+        private static readonly ManualResetEvent _exitEvent = new ManualResetEvent(false);
+
+        static void Main(string[] args)
+        {
+            Console.CancelKeyPress += (s, e) => _exitEvent.Set();
+
+            Configuration = LoadConfiguration();
+            _clusterClient = new ClientBuilder()
+                .LoadConfiguration("OrleansConfiguration.dev.xml")
+                .ConfigureServices(ConfigureServices)
+                .Build();
+            Startup();
+            _exitEvent.WaitOne();
+        }
+
+        private static void ConfigureServices(IServiceCollection services)
+        {
+            services.AddSingleton(ConfigureLogging());
+            services.AddLogging();
+        }
+
+        private static ILoggerFactory ConfigureLogging()
+        {
+            var factory = new LoggerFactory();
+            factory.AddConsole();
+
+            return factory;
+        }
+
+        private static IConfiguration LoadConfiguration()
+        {
+            var builder = new ConfigurationBuilder()
+                .SetBasePath(Directory.GetCurrentDirectory())
+                .AddJsonFile("config.json", true, false);
+            return builder.Build();
+        }
+
+        private static async void Startup()
+        {
+            //var logger = _clusterClient.ServiceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<Program>();
+
+            //logger.LogInformation("Connecting to cluster...");
+            //await _clusterClient.Connect();
+            //logger.LogInformation("Connected to cluster.");
+
+            var connectionRouter = new ConnectionRouter();
+            Console.WriteLine("Press Ctrl+C to terminate...");
+            await connectionRouter.Startup(default(CancellationToken));
+        }
+    }
+}

+ 8 - 0
src/MineCase.Protocol/MineCase.Protocol.csproj

@@ -0,0 +1,8 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFramework>netstandard1.4</TargetFramework>
+    <RootNamespace>MineCase</RootNamespace>
+  </PropertyGroup>
+
+</Project>

+ 97 - 0
src/MineCase.Protocol/Protocol/Packet.cs

@@ -0,0 +1,97 @@
+using MineCase.Serialization;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MineCase.Protocol
+{
+    [Packet]
+    public struct UncompressedPacket
+    {
+        [SerializeAs(DataType.VarInt)]
+        public uint Length;
+
+        [SerializeAs(DataType.VarInt)]
+        public uint PacketId;
+
+        [SerializeAs(DataType.ByteArray)]
+        public byte[] Data;
+
+        public async Task SerializeAsync(Stream stream)
+        {
+            Length = (uint)Data.Length + PacketId.SizeOfVarInt();
+
+            using (var bw = new BinaryWriter(stream, Encoding.UTF8, true))
+            {
+                bw.WriteAsVarInt(Length);
+                bw.WriteAsVarInt(PacketId);
+                bw.Flush();
+            }
+            await stream.WriteAsync(Data, 0, Data.Length);
+        }
+
+        public static async Task<UncompressedPacket> DeserializeAsync(Stream stream)
+        {
+            var packet = new UncompressedPacket();
+            int packetIdLen;
+            using (var br = new BinaryReader(stream, Encoding.UTF8, true))
+            {
+                packet.Length = br.ReadAsVarInt(out _);
+                packet.PacketId = br.ReadAsVarInt(out packetIdLen);
+            }
+            packet.Data = new byte[packet.Length - packetIdLen];
+            await stream.ReadExactAsync(packet.Data, 0, packet.Data.Length);
+            return packet;
+        }
+    }
+
+    [Packet]
+    public struct CompressedPacket
+    {
+        [SerializeAs(DataType.VarInt)]
+        public uint PacketLength;
+
+        [SerializeAs(DataType.VarInt)]
+        public uint DataLength;
+
+        [SerializeAs(DataType.VarInt)]
+        public byte[] CompressedData;
+
+        public async Task SerializeAsync(Stream stream)
+        {
+            PacketLength = (uint)CompressedData.Length + DataLength.SizeOfVarInt();
+
+            using (var bw = new BinaryWriter(stream, Encoding.UTF8, true))
+            {
+                bw.WriteAsVarInt(PacketLength);
+                bw.WriteAsVarInt(DataLength);
+                bw.Flush();
+            }
+            await stream.WriteAsync(CompressedData, 0, CompressedData.Length);
+        }
+
+        public static async Task<CompressedPacket> DeserializeAsync(Stream stream)
+        {
+            var packet = new CompressedPacket();
+            int dataLengthLen;
+            using (var br = new BinaryReader(stream, Encoding.UTF8, true))
+            {
+                packet.PacketLength = br.ReadAsVarInt(out _);
+                packet.DataLength = br.ReadAsVarInt(out dataLengthLen);
+            }
+            packet.CompressedData = new byte[packet.PacketLength - dataLengthLen];
+            await stream.ReadExactAsync(packet.CompressedData, 0, packet.CompressedData.Length);
+            return packet;
+        }
+    }
+
+    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
+    public sealed class PacketAttribute : Attribute
+    {
+        public PacketAttribute()
+        {
+        }
+    }
+}

+ 50 - 0
src/MineCase.Protocol/Serialization/BinaryReaderExtensions.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MineCase.Serialization
+{
+    internal static class BinaryReaderExtensions
+    {
+        public static bool ReadAsBoolean(this BinaryReader br) =>
+            br.ReadBoolean();
+
+        /// <see cref="http://wiki.vg/Protocol#VarInt_and_VarLong"/>
+        public static uint ReadAsVarInt(this BinaryReader br, out int bytesRead)
+        {
+            int numRead = 0;
+            uint result = 0;
+            byte read;
+            do
+            {
+                read = br.ReadByte();
+                uint value = (uint)(read & 0b01111111);
+                result |= (value << (7 * numRead));
+
+                numRead++;
+                if (numRead > 5)
+                    throw new InvalidDataException("VarInt is too big");
+            } while ((read & 0b10000000) != 0);
+
+            bytesRead = numRead;
+            return result;
+        }
+    }
+
+    internal static class StreamExtensions
+    {
+        public static async Task ReadExactAsync(this Stream stream, byte[] buffer, int offset, int count)
+        {
+            while (count != 0)
+            {
+                var numRead = await stream.ReadAsync(buffer, offset, count);
+                if (numRead == 0)
+                    throw new InvalidDataException("Unexpected end of stream.");
+                offset += numRead;
+                count -= numRead;
+            }
+        }
+    }
+}

+ 40 - 0
src/MineCase.Protocol/Serialization/BinaryWriterExtensions.cs

@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace MineCase.Serialization
+{
+    internal static class BinaryWriterExtensions
+    {
+        public static void WriteAsBoolean(this BinaryWriter bw, bool value) =>
+            bw.Write(value);
+
+        public static void WriteAsByte(this BinaryWriter bw, byte value) =>
+            bw.Write(value);
+
+        /// <see cref="http://wiki.vg/Protocol#VarInt_and_VarLong"/>
+        public static void WriteAsVarInt(this BinaryWriter bw, uint value)
+        {
+            do
+            {
+                byte temp = (byte)(value & 0b01111111);
+                value >>= 7;
+                if (value != 0)
+                    temp |= 0b10000000;
+                bw.Write(temp);
+            } while (value != 0);
+        }
+
+        public static void WriteAsByteArray(this BinaryWriter bw, byte[] value) =>
+            bw.Write(value);
+    }
+
+    internal static class DataTypeSizeExtensions
+    {
+        public static uint SizeOfVarInt(this uint value)
+        {
+            return 0;
+        }
+    }
+}

+ 41 - 0
src/MineCase.Protocol/Serialization/SerializeAsAttribute.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MineCase.Serialization
+{
+    public enum DataType
+    {
+        Boolean,
+        Byte,
+        UnsignedByte,
+        Short,
+        UnsignedShort,
+        Int,
+        Long,
+        Float,
+        Double,
+        String,
+        Chat,
+        VarInt,
+        VarLong,
+        EntityMetadata,
+        Slot,
+        NBTTag,
+        Position,
+        Angle,
+        UUID,
+        ByteArray
+    }
+
+    [AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
+    public sealed class SerializeAsAttribute : Attribute
+    {
+        public DataType DataType { get; }
+
+        public SerializeAsAttribute(DataType dataType)
+        {
+            DataType = dataType;
+        }
+    }
+}

+ 23 - 1
src/MineCase.Server/MineCase.Server.csproj

@@ -1,8 +1,30 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>netcoreapp1.1</TargetFramework>
   </PropertyGroup>
 
+  <ItemGroup>
+    <PackageReference Include="Autofac" Version="4.6.1" />
+    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.2.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Logging" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Options" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="1.1.2" />
+    <PackageReference Include="Microsoft.Orleans.Server" Version="2.0.0-preview2-20170724" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Update="OrleansConfiguration.dev.xml">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Remove="*.log;*.txt" />
+  </ItemGroup>
+
 </Project>

+ 17 - 0
src/MineCase.Server/OrleansConfiguration.dev.xml

@@ -0,0 +1,17 @@
+<OrleansConfiguration xmlns="urn:orleans">
+  <Globals>
+    <SeedNode Address="localhost" Port="11111" />
+    <Liveness LivenessType ="MembershipTableGrain" />
+    <ReminderService ReminderServiceType="ReminderTableGrain"/>
+    <StorageProviders>
+      <Provider Type="Orleans.Storage.MemoryStorage" Name="PubSubStore" />
+    </StorageProviders>
+    <StreamProviders>
+      <Provider Type="Orleans.Providers.Streams.SimpleMessageStream.SimpleMessageStreamProvider" Name="JobsProvider"/>
+    </StreamProviders>
+  </Globals>
+  <Defaults>
+    <Networking Address="localhost" Port="11111" />
+    <ProxyingGateway Address="localhost" Port="30000" />
+  </Defaults>
+</OrleansConfiguration>

+ 161 - 0
src/MineCase.Server/OrleansHostWrapper.cs

@@ -0,0 +1,161 @@
+using Orleans.Runtime;
+using Orleans.Runtime.Configuration;
+using Orleans.Runtime.Host;
+using System;
+using System.Collections.Generic;
+using System.Net;
+using System.Reflection;
+using System.Text;
+
+namespace MineCase.Server
+{
+    class OrleansHostWrapper
+    {
+        private readonly SiloHost _siloHost;
+
+        public OrleansHostWrapper(ClusterConfiguration config, string[] args)
+        {
+            var siloArgs = SiloArgs.ParseArguments(args);
+            if (siloArgs == null)
+            {
+                return;
+            }
+
+            if (siloArgs.DeploymentId != null)
+            {
+                config.Globals.DeploymentId = siloArgs.DeploymentId;
+            }
+
+            _siloHost = new SiloHost(siloArgs.SiloName, config);
+            _siloHost.LoadOrleansConfig();
+        }
+
+        public int Run()
+        {
+            if (_siloHost == null)
+            {
+                SiloArgs.PrintUsage();
+                return 1;
+            }
+
+            try
+            {
+                _siloHost.InitializeOrleansSilo();
+
+                if (_siloHost.StartOrleansSilo())
+                {
+                    Console.WriteLine($"Successfully started Orleans silo '{_siloHost.Name}' as a {_siloHost.Type} node.");
+                    return 0;
+                }
+                else
+                {
+                    throw new OrleansException($"Failed to start Orleans silo '{_siloHost.Name}' as a {_siloHost.Type} node.");
+                }
+            }
+            catch (Exception exc)
+            {
+                _siloHost.ReportStartupError(exc);
+                Console.Error.WriteLine(exc);
+                return 1;
+            }
+        }
+
+        public int Stop()
+        {
+            if (_siloHost != null)
+            {
+                try
+                {
+                    _siloHost.StopOrleansSilo();
+                    _siloHost.Dispose();
+                    Console.WriteLine($"Orleans silo '{_siloHost.Name}' shutdown.");
+                }
+                catch (Exception exc)
+                {
+                    _siloHost.ReportStartupError(exc);
+                    Console.Error.WriteLine(exc);
+                    return 1;
+                }
+            }
+            return 0;
+        }
+
+        private class SiloArgs
+        {
+            public SiloArgs(string siloName, string deploymentId)
+            {
+                this.DeploymentId = deploymentId;
+                this.SiloName = siloName;
+            }
+
+            public static SiloArgs ParseArguments(string[] args)
+            {
+                string deploymentId = null;
+                string siloName = null;
+
+                for (int i = 0; i < args.Length; i++)
+                {
+                    string arg = args[i];
+                    if (arg.StartsWith("-") || arg.StartsWith("/"))
+                    {
+                        switch (arg.ToLowerInvariant())
+                        {
+                            case "/?":
+                            case "/help":
+                            case "-?":
+                            case "-help":
+                                // Query usage help. Return null so that usage is printed
+                                return null;
+                            default:
+                                Console.WriteLine($"Bad command line arguments supplied: {arg}");
+                                return null;
+                        }
+                    }
+                    else if (arg.Contains("="))
+                    {
+                        string[] parameters = arg.Split('=');
+                        if (String.IsNullOrEmpty(parameters[1]))
+                        {
+                            Console.WriteLine($"Bad command line arguments supplied: {arg}");
+                            return null;
+                        }
+                        switch (parameters[0].ToLowerInvariant())
+                        {
+                            case "deploymentid":
+                                deploymentId = parameters[1];
+                                break;
+                            case "name":
+                                siloName = parameters[1];
+                                break;
+                            default:
+                                Console.WriteLine($"Bad command line arguments supplied: {arg}");
+                                return null;
+                        }
+                    }
+                    else
+                    {
+                        Console.WriteLine($"Bad command line arguments supplied: {arg}");
+                        return null;
+                    }
+                }
+                // Default to machine name
+                siloName = siloName ?? Dns.GetHostName();
+                return new SiloArgs(siloName, deploymentId);
+            }
+
+            public static void PrintUsage()
+            {
+                string consoleAppName = Assembly.GetEntryAssembly().GetName().Name;
+                Console.WriteLine(
+                    $@"USAGE: {consoleAppName} [name=<siloName>] [deploymentId=<idString>] [/debug]
+                Where:
+                name=<siloName> - Name of this silo (optional)
+                deploymentId=<idString> - Optionally override the deployment group this host instance should run in 
+                (otherwise will use the one in the configuration");
+            }
+
+            public string SiloName { get; set; }
+            public string DeploymentId { get; set; }
+        }
+    }
+}

+ 48 - 3
src/MineCase.Server/Program.cs

@@ -1,12 +1,57 @@
-using System;
+using Orleans.Runtime.Configuration;
+using System;
+using System.Threading;
 
 namespace MineCase.Server
 {
     class Program
     {
-        static void Main(string[] args)
+        private static readonly ManualResetEvent _exitEvent = new ManualResetEvent(false);
+
+        private static OrleansHostWrapper hostWrapper;
+
+        static int Main(string[] args)
+        {
+            int exitCode = StartSilo(args);
+
+            Console.WriteLine("Press Ctrl+C to terminate...");
+            Console.CancelKeyPress += (s, e) => _exitEvent.Set();
+            _exitEvent.WaitOne();
+
+            exitCode += ShutdownSilo();
+
+            //either StartSilo or ShutdownSilo failed would result on a non-zero exit code. 
+            return exitCode;
+        }
+
+
+        private static int StartSilo(string[] args)
+        {
+            // define the cluster configuration
+            var config = new ClusterConfiguration();
+            config.LoadFromFile("OrleansConfiguration.dev.xml");
+            config.UseStartupType<Startup>();
+
+            hostWrapper = new OrleansHostWrapper(config, args);
+            return hostWrapper.Run();
+        }
+
+        private static int ShutdownSilo()
+        {
+            if (hostWrapper != null)
+            {
+                return hostWrapper.Stop();
+            }
+            return 0;
+        }
+
+        // Workaroud for assembly references
+        private void Dummy()
         {
-            Console.WriteLine("Hello World!");
+            var type = new[]
+            {
+                typeof(Orleans.Storage.MemoryStorage)
+            };
         }
     }
 }

+ 48 - 0
src/MineCase.Server/Startup.cs

@@ -0,0 +1,48 @@
+using Autofac;
+using Autofac.Extensions.DependencyInjection;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
+using System;
+using System.IO;
+
+namespace MineCase.Server
+{
+    public class Startup
+    {
+        public IConfiguration Configuration { get; }
+
+        public Startup()
+        {
+            Configuration = LoadConfiguration();
+        }
+
+        private IConfiguration LoadConfiguration()
+        {
+            var builder = new ConfigurationBuilder()
+                .SetBasePath(Directory.GetCurrentDirectory())
+                .AddJsonFile("config.json", true, false);
+            return builder.Build();
+        }
+
+        public IServiceProvider ConfigureServices(IServiceCollection services)
+        {
+            services.AddOptions();
+            services.AddSingleton(ConfigureLogging());
+            services.AddLogging();
+
+            var container = new ContainerBuilder();
+            container.Populate(services);
+            return new AutofacServiceProvider(container.Build());
+        }
+
+        private static ILoggerFactory ConfigureLogging()
+        {
+            var factory = new LoggerFactory();
+            factory.AddConsole();
+
+            return factory;
+        }
+    }
+}

+ 15 - 1
src/MineCase.sln

@@ -3,7 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00
 # Visual Studio 15
 VisualStudioVersion = 15.0.26430.14
 MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineCase.Server", "MineCase.Server\MineCase.Server.csproj", "{8E71CBEC-5804-4125-B651-C78426E57C8C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineCase.Server", "MineCase.Server\MineCase.Server.csproj", "{8E71CBEC-5804-4125-B651-C78426E57C8C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MineCase.Gateway", "MineCase.Gateway\MineCase.Gateway.csproj", "{08B3641D-29F1-4E2B-BA88-84B2FFB436FF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MineCase.Protocol", "MineCase.Protocol\MineCase.Protocol.csproj", "{0842DE6C-4270-440E-9666-255A0B2EC1D9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Private", "Private", "{59A25C6F-3315-4CE8-9016-B088C9AF3A86}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +21,14 @@ Global
 		{8E71CBEC-5804-4125-B651-C78426E57C8C}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{8E71CBEC-5804-4125-B651-C78426E57C8C}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{8E71CBEC-5804-4125-B651-C78426E57C8C}.Release|Any CPU.Build.0 = Release|Any CPU
+		{08B3641D-29F1-4E2B-BA88-84B2FFB436FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{08B3641D-29F1-4E2B-BA88-84B2FFB436FF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{08B3641D-29F1-4E2B-BA88-84B2FFB436FF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{08B3641D-29F1-4E2B-BA88-84B2FFB436FF}.Release|Any CPU.Build.0 = Release|Any CPU
+		{0842DE6C-4270-440E-9666-255A0B2EC1D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{0842DE6C-4270-440E-9666-255A0B2EC1D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{0842DE6C-4270-440E-9666-255A0B2EC1D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{0842DE6C-4270-440E-9666-255A0B2EC1D9}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 18 - 0
src/NuGet.config

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+  <!-- NOTE: Leave this file here and keep it in sync with list in dir.props. -->
+  <!-- The command-line doesn't need it, but the IDE does.                    -->
+  <packageSources>
+    <clear/>
+    <add key="orleans-prerlease" value="https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json" />
+    <add key="nuget.org" value="https://www.nuget.org/api/v2/" />
+  </packageSources>
+  <config>
+    <add key="repositoryPath" value="..\packages" />
+  </config>
+  <packageRestore>
+    <!-- Automated package restore in VS does not work at this time with
+         this project and it causes build failures in VS. Disable it. -->
+    <add key="automatic" value="false" />
+  </packageRestore>
+</configuration>